Any requests for data should be made to the Business Entity, never directly from outside to the Data Access object. This simply preserves the isolation of the different layers of the application. The fetchOrder procedure in OrderEntity.p is an example of this:
PROCEDURE fetchOrder:
DEFINE INPUT PARAMETER piOrderNum AS INTEGER NO-UNDO.
DEFINE OUTPUT PARAMETER DATASET FOR dsOrder BY-VALUE.
/* This turns around and runs an equivalent procedure in the
Data-Source procedure, passing in the static dataSet. */
DYNAMIC-FUNCTION("attachDataSet" IN hSourceProc, hDataSet).
RUN fetchOrder IN hSourceProc (INPUT piOrderNum,
INPUT-OUTPUT DATASET dsOrder BY-REFERENCE).
DYNAMIC-FUNCTION("detachDataSet" IN hSourceProc, INPUT hDataSet).
END PROCEDURE. /* fetchOrder */
This turns around and runs an equivalent procedure in the Data Access procedure. Significantly, the ProDataSet parameter is INPUT-OUTPUT BY-REFERENCE in the second-level call to the Data Access object. This uses the Business Entity's ProDataSet and avoids the expense of copying the ProDataSet back and forth. Having avoided this, the overhead of the second procedure call is not very significant.
The following figure shows how the ProDataSet is being used in this case.
Figure 19. ProDataSet flow
These are the steps illustrated in the above figure:
1. A requesting procedure on the client runs fetchOrder in the Order Business Entity on the server.
2. That fetchOrder procedure runs fetchOrder in the Data Access object in its session.
3. Because the ProDataSet is passed in BY-REFERENCE, it is actually the Business Entity's instance that is used (marked in bold). The dotted lines indicate that this instance is passed without being copied.
4. The fetchOrder procedure in the Data Access object attaches Data-Sources, which are tables in the database, and also sets any callback procedures for FILL events.
5. It then does a FILL, which actually fills the ProDataSet instance back in the Business Entity.
6. It returns the ProDataSet to the requesting procedure. It is copied there rather than being passed BY-REFERENCE because the presumption is that the requester is or may be in a different session.
If the Data Access object is a super procedure of the Business Entity, then in cases where the Business Entity version of a procedure like fetchOrder does not do any additional work of its own, it could be dispensed with, and a call to fetchOrder from another procedure would be handled automatically by the Data Access object. In this case, there are two things to consider:
First, the Data Access object version of fetchOrder would need to make its ProDataSet parameter OUTPUT instead of INPUT-OUTPUT, because it would be passed back directly to the caller. The caller would not be passing in a ProDataSet of its own. In this case, the Data Access object's ProDataSet instance is the one that is used to satisfy the request. It then becomes the Data Access object's responsibility to make sure the Data-Sources are attached, as illustrated in the second version of fetchOrder in the .
Second, if there is any reason for the Business Entity procedure to run fetchOrder in the Data Access procedure, or to provide extended behavior for a call from outside to fetchOrder, then this arrangement becomes inefficient, because the ProDataSet would be copied from the Data Access procedure to the Business Entity procedure. For example, consider this alternative to fetchOrder in the Entity:
PROCEDURE fetchOrder:
/* Alternative OrderEntity.p version for comparison only ! */
DEFINE INPUT PARAMETER piOrderNum AS INTEGER NO-UNDO.
DEFINE OUTPUT PARAMETER DATASET FOR dsOrder.
/* fetchOrder does some prep work here, for example validating the
INPUT data or the requester's privileges to make the request */
/* Then it just does a RUN SUPER to invoke the standard behavior. */
RUN SUPER (INPUT piOrderNum, OUTPUT DATASET dsOrder).
/* The RUN SUPER is done instead of running it directly as in the
original version:
RUN fetchOrder IN hSourceProc (INPUT piOrderNum,
INPUT-OUTPUT DATASET dsOrder BY-REFERENCE).
*/
END PROCEDURE. /* fetchOrder */
If the parameter definition for the ProDataSet in fetchOrder in the procedure OrderSource.p is changed to be OUTPUT instead of INPUT-OUTPUT, and hSourceProc is a super procedure of OrderEntity.p, and fetchOrder in OrderSource.p does the attachDataSet, then this arrangement works fine.
However, the ProDataSet is copied from OrderSource to OrderEntity before being copied back to the caller. This is not a good thing. Because of this, and because of the potential for confusion between when the Data Access ProDataSet instance is being used and when the Business Entity instance is being used, making the data access procedure a super procedure may not be a good practice.
The following figure illustrates what happens if the Data Access object is a super procedure of the Business Entity, and it has the only implementation of procedure fetchOrder.
Figure 20. Data Access object as super procedure
These are the steps illustrated in the above figure:
1. The requesting procedure runs fetchOrder in the Business Entity as before.
2. There is no fetchOrder procedure in the Business Entity. However, since the Data Access object is a super procedure, the AVM runs fetchOrder there.
3. This means that it is the Data Access object's ProDataSet instance that is used for the request.
4. The fetchOrder procedure attaches Data-Sources and callback procedures to this ProDataSet.
5. It then fills the ProDataSet.
6. The fetchOrder procedure then returns this ProDataSet to the original caller as an OUTPUT parameter. The ProDataSet instance in the Business Entity is not used.
This all works correctly in this case, but could be a source of confusion and errors because the ProDataSet instance being used is not consistent.
By contrast, the following figure shows the case where, once again, the Data Access object is a super procedure of the Business Entity, and fetchOrder in the Business Entity does a RUN SUPER to run the standard attach and fill behavior.
Figure 21. Data Access object as super procedure with RUN SUPER
These are the steps illustrated in the above figure:
1. The requesting procedure runs fetchOrder in the Business Entity as before. There is an implementation of fetchOrder there, so it is executed.
2. Procedure fetchOrder does a RUN SUPER, which runs fetchOrder in the Data Access object. Because the parameter definitions must be consistent in this case, the ProDataSet is simply an OUTPUT parameter.
3. Because of this, the Data Access object uses its own ProDataSet instance.
4. It attaches Data-Sources to its own ProDataSet instance.
5. It fills its own ProDataSet instance.
6. It returns its ProDataSet as an OUTPUT parameter to the Business Entity, copying it to the Business Entity's ProDataSet instance.
7. The Business Entity then returns it to the original caller as OUTPUT, again copying the ProDataSet.
Because of the extra copy operation, this is not a good configuration. This is something you need to keep in mind when you design your procedures and decide how they are related.
This discussion may seem complex, but the intention is to make you aware of some of the issues and how you should consider them when you are designing your application. As with every other aspect of design, once you have thought through an appropriate solution to a part of your design, if you keep to your solution consistently, then you will not have to worry about it anymore, and developers writing business logic do not need to be concerned about the details of the Data Access architecture supporting them.