FREE hit counter and Internet traffic statistics from freestats.com

Monday, November 15, 2004

Organizing Domain Logic: Transaction Script

This is first of several articles on representing domain logic in .NET applications and services, again taken from a course I developed on .NET patterns and architecture. I should mention that because one of the benefits of using patterns is to share a common nomenclature I'm using the pattern names as defined by Martin Fowler in his excellent book.


Since an application can usually be thought of as a series of transactions, one pattern for representing the transaction is called a Transaction Script. As the name implies each transaction, for example processing an order, is encapsulated in its own script housed in a method of a Business Component (a Business Component is a class or set of classes packaged in a Class Library assembly in .NET used to encapsulate business or domain logic). The benefit of this approach is that it is conceptually straightforward to design by looking at the actions that the application needs to perform.
The way the transaction script is packaged can vary in two basic ways:

Multiple Transactions per Component

This is the most common technique and involves factoring the transactions into higher-level groupings and then creating a Business Component for each grouping and a public or shared method for each transaction. For example, in a retail application the process of ordering a product involves several steps and can be encapsulated in an PlaceOrder method in the OrderProcessing component like so:

Public Class OrderProcessing    

Public Shared Function PlaceOrder(ByVal order As OrderInfo) As Long
' Start a transaction
' Check Inventory
' Retrieve customer information, check credit status
' Calculate price and tax
' Calculate shipping and total order
' Save Order to the database and commit transaction
' Send an email confirming the order
' Return the new order ID
End Function
End Class

In this case the order information is passed to the PlaceOrder method in an OrderInfo structure defined as follows:
Public Structure OrderInfo    

Public ProductID As Long
Public Quantity As Integer
Public CustomerId As Long
Public ShipVia As ShipType
End Structure

Public Enum ShipType
FedEx
UPS
Postal
End Enum

In a similar fashion the customer processing can be encapsulated in a CustomerProcessing component (class) like so:
Public Class CustomerProcessing    

Public Shared Function SaveCustomer(ByVal customer As CustomerInfo) As Long
' Validate Address
' Start a transaction
' Look for duplicate customers based on email address
' Save customer to database and commit transaction
' Return the new customer ID
End Function
End Class

Public Structure CustomerInfo
Public Name As String
Public Address As String
Public City As String
Public State As String
Public PostalCode As String
Public Email As String
End Structure

The user process or user or service interface components would be responsible for calling the SaveCustomer method, persisting the customer ID, creating the OrderInfo structure and then passing it to the PlaceOrder method. Note that here the product information would already be known since the UI would have called a method in the OrderProcessing class (or another Business Component) to retrieve products.

Of course, both the OrderProcessing and CustomerProcessing classes could inherit from a base class if there were any code that both could use (for example a base class constructor). In that case the methods would likely not be shared methods. Since these components have dependencies on each other they might well be packaged in the same assembly so they can be versioned and deployed as a unit.

Each Transaction is its own Component

Using this technique, each transaction script is implemented in its own class which uses implementation or interface inheritance in order to provide polymorphism. For example, the PlaceOrder and SaveCustomer scripts could be implemented as classes that inherit from the IProcessing interface like so:



The code to implement the PlaceOrder component would then look as follows:
Public Interface IProcessing    

Function Execute() As Long
End Interface

Public Class PlaceOrder : Implements IProcessing
Private _order As OrderInfo
Public Sub New(ByVal order As OrderInfo)
_order = order
End Sub
Public Function Execute() As Long Implements IProcessing.Execute
' Start a transaction
' Check Inventory
' Retrieve customer information, check credit status
' Calculate price and tax
' Calculate shipping and total order
' Save Order to the database and commit transaction
' Email a confirmation
' Return the new order ID
End Function
End Class
This design is based on the Command Pattern documented by the GoF and allows the user or service interface and process components to treat the scripts polymorphically by calling the Execute method of the IProcessing interface. The UI components could rely on one of the Factory Patterns to create the appropriate object.

The logic to perform the various steps shown in these code snippets can be implemented either with inline managed code or through calls to stored procedures. Using stored procedures has the benefit of introducing a further layer of abstraction and taking advantage of database server performance optimizations. In either case the Transaction Script typically uses only the barest of data access layers or none at all.

1 comment:

Anonymous said...

What do you know Pirates of the Burning Sea Gold. And do you want to know? You can get potbs gold here. And welcome to our website, here you can play games, and you will get potbs Doubloon to play game. I know potbs money, and it is very interesting.Do you want a try, come and view our website, and you will learn how to buy potbs Doubloon. Come and join with us. We are waiting for your coming.