Action Objects

Now we turn to where the rubber meets the road—the point where the client applications interface with the business objects, which is through action objects.

Action objects represent the sequence of actions that an application performs. Action objects are responsible for manipulating business objects and are the containers for procedural business logic. Remember that the distributed clients should only send and receive cRecordset or cParams objects. This is a thin-client approach. The client should be displaying data, letting the user interact with the data, and then it should be returning this data to the action object.

Action objects live in the logical business tier (with factory and worker objects). Therefore, access to these action objects from the clients should be as stateless as possible. This means that if action objects live on a separate physical tier (as in a true three-tier system), performance is maximized by minimizing cross-machine references.

The other important task of action objects is to define transaction scope; that is, when an action object is created, all subsequent operations on that action object will be in a transaction. Physically, action objects live in a Visual Basic out-of-process component or in DLLs hosted by MTS. Worker and factory objects are contained in an in-process component that also could be hosted in MTS.

The structure of action objects comes from the requirements of the application. The requirements of the application logic should be determined from an object-based analysis, preferably a UML usage scenario. This is what I refer to as the Golden Triangle.

Here's an example: Imagine the archetypal Human Resource system in any organization. One of the most basic business requirements for an HR system is that it must be able to retrieve and update employee information and the employee's associated activities. These activities are a combination of the employee's current roles and projects and are known as the usage scenario.

The user interface needed to meet this requirement could be a Visual Basic form, but it could also be an Active Server Page-based Web page.

Action Object Interface

We can imagine that a Visual Basic form that implements this usage scenario would present two major pieces of information: EmployeeDetails and Activities. We can now determine the interface of the required action object.

  • GetEmployeeDetails

  • UpdateEmployeeDetails

  • GetCurrentActivities

  • UpdateCurrentActivities

Notice that these attributes of the action object are all stateless. That is, when you retrieve a recordset from GetEmployeeDetails, you then shut down the action object immediately, thereby minimizing the cross-layer communication cost. Users can then modify the resulting recordset and, when they are ready to send it back for updating, you create the action object again and call the UpdateEmployeeDetails method. The action object does not need to be held open while the recordset is being modified. Let's look at these calls in more detail:

Public Function GetEmployeeDetails(Optional i_sID As String) As cRecordset     Dim ocfEmployees      As New cfEmployees     Dim oParams           As New cParams          If Not IsMissing(i_sID) Then oParams.Add "ID", i_sID     ocfEmployees.Create oPicDAL     ocfEmployees.Populate oParams          Set GetEmployeeDetails = ocfEmployees.Recordset      End Function 

Likewise, the UpdateEmployeeDetail call looks like this:

Public Sub UpdateEmployeeDetail(i_ocRecordset As cRecordset)     Dim ocfEmployees As New cfEmployees     Dim ocRecordset  As New cRecordset          Set ocRecordset = New cRecordset     ocRecordset.Serialize = i_ocRecordset.Serialize ' Create local                           ' copy of object so that business objects do                           ' not have to refer across network.     ocfEmployees.Create oPicDAL     Set ocfEmployees.Recordset = ocRecordset     ocfEmployees.Persist     i_ocRecordset.Serialize = ocRecordset.Serialize ' Copy updated                           ' recordset back to client. End Sub 

The GetCurrentActivities call has a bit more work to do. It must create a new recordset because the usage scenario requires that both Roles and Projects come back as one set of data. So the GetCurrentActivities call would create a recordset with three fields—ActivityID, ActivityValue, and ActivityType (Roles or Projects) and then populate this recordset from the Roles business object and the Projects business object. This recordset would then be returned to the client.

The UpdateCurrentActivities call would have to do the reverse—unpack the recordset and then apply updates to the Roles and Projects table.

Transactions in Action

So if action objects are responsible for transactions, how do they maintain transactions? When an action object is initiated, it instantiates a cDAL object and begins a transaction. This cDAL object is passed to all business objects that the action object creates so that every business object in this action object has the same transaction.

Just before the action object is destroyed, it checks a module-level variable (bPiTransactionOK) in the class Terminate event to see whether the transaction should be committed. This module-level variable can be set by any of the procedures within the action object. Normally, if a transaction has to be rolled back, an exception is raised to the client and bPiTransactionOK is set to False so that the user can be informed that something very bad has happened. Checking this module-level variable in the Terminate event ensures that the action object is responsible for protecting the transaction, not the client.

Private bPiTransactionOK    As Boolean Private oPicDAL             As cDAL Private Sub Class_Initialize()     Set oPicDAL = New cDAL     oPicDAL.BeginTransaction     bPiTransactionOK = True End Sub Private Sub Class_Terminate()     If bPiTransactionOK Then         oPicDAL.CommitTransaction     Else         oPicDAL.RollbackTransaction     End If End Sub 

Wrap Up

So now we've covered the role and design of action objects. In summary, a good analogy is that of a relational SQL database: factory-worker objects are represented by the tables and records, and action objects are represented by the stored procedures. The lifetime of the action object controls the lifetime of the implicit transaction contained within.

Action objects should be accessed in a stateless fashion—get what you want and then get the hell out of there! Stateless fashion is enabled by the supporting of serialization by cRecordset and cParams, which ensures that your applications can achieve good distributed performance.



Ltd Mandelbrot Set International Advanced Microsoft Visual Basics 6. 0
Advanced Microsoft Visual Basic (Mps)
ISBN: 1572318937
EAN: 2147483647
Year: 1997
Pages: 168

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net