Transaction script pattern
The short definition for the Transaction Script Pattern is: “Organizes business logic by procedures where each procedure handles a single request from the presentation.”
When the Transaction Script Pattern is used, we see a business application as consisted of a number of transactions. The transaction could be viewing or changing some data, and also some application logic might be in the interaction between the application and the data store. With the Transaction Script Pattern, we build each transaction as a single method, then use the method to interact with database or database wrapping layers.
One benefit of using the Transaction Script Pattern is its simplicity nature; a procedure can be dedicated with one task without worrying others. Transaction script can be placed at any location within an application, the best solution is to separate the transaction script from others and put it into its own layer. Several transaction scripts can be put into one class or we can use one class for each transaction script with using the Command pattern.
When To Use It
The Transaction Script Pattern should be used when the application itself does not have much logic, or it does not have much overhead in performance and understanding. As the business logic getting complex, other patterns such as Domain model pattern is a better choice.
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:
In this case the order information is passed to the PlaceOrder method in an OrderInfo structure defined as follows:
In a similar fashion the customer processing can be encapsulated in a CustomerProcessing component (class) like so:
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:
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.
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 ClassThis 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.
No comments:
Post a Comment