If you are not sure what the term '3-Tier architecture' actually means you should first read the following articles:-
This particular article is referring to 3-Tier software architecture.
Components in the presentation layer are not connected to the database, so all
DELETE commands are removed from their respective triggers.
When a presentation layer component wishes to obtain data it activates a
GETDATA (or equivalent) operation on a session service, which exists in the business layer. The session service retrieves data from the physical database, then uses the
XMLSAVE command to construct an XML stream which it then passes back to the presentation layer component. Stepped hitlists do not exist in this architecture, so the XML stream may contain multiple occurrences, and even multiple entities.
The presentation layer component simply performs an
XMLLOAD on this data stream in order to transfer all that data into its own structure. These occurrences will have
$OCCSTATUS="est" instead of
Occurrences can be modified, created or deleted in the presentation layer component as normal. However, this component does not perform a
STORE, instead it uses
XMLSAVE to create a new XML stream, which it then passes to a session service by activating a
PUTDATA (or equivalent) operation.
The session service will then perform an
XMLLOAD followed by a
RETRIEVE/RECONNECT which will identify if occurrences need to be added, modified or deleted. This is then followed by a
STORE/COMMIT. Game over.
By breaking down an application into 3 distinct and separate tiers (or layers) - the presentation tier, the business logic tier and the data access tier - you gain advantages in several areas. The Compuware Three Tier Development Guide (product code 10117038101-00) identifies the following:-
As a designer/developer I can summarise the benefits as follows:
I first attempted the 3-Tier software architecture when Compuware provided the functionality for Object Services in version 7.2.04 (see UNIFACE and the N-Tier Architecture). This was superseded in version 7.2.06 when the functionality for XML streams appeared (see 3 Tiers, 2 Models, and XML Streams). This proved to be a far superior approach, and I have successfully taken my sample 2-Tier application (which can be downloaded from my Building Blocks page) and converted it into 3 tiers. If you run the two versions (2-tier and 3-tier) side by side you will find it difficult to spot the differences.
However, in order to produce a 3-tier version which had the same look and feel as the 2-tier version I had to invent some nifty work-arounds for problems I encountered with the Compuware code.
Although Compuware state in their Three Tier Development Guide that the presentation layer can be made up of both client/server forms and web pages what they neglect to mention is that forms must now function in the same way as web pages - i.e. any validation performed by the business layer component (session service) cannot be initiated via any field or occurrence trigger, only at the SUBMIT/STORE stage. This means that you have to wait until the entire form contents is sent to the session service for updating on the database before any individual field can be validated. I consider this to be unacceptable, and so does every other person I have spoken to on the matter.
These are the problems I have found, and the work-arounds that I have produced:
XMLLOADsets all modification and validation flags
When you retrieve data into a traditional 2-tier form and allow the user to make changes no field or key validation trigger is normally fired until the user actually makes a change. In a 3-tier form where data is obtained from a session service and loaded using the
XMLLOAD statement you will find that all key, field and occurrence modification/validation flags are automatically set. This means that validation triggers will be fired even though data has not yet been modified. This causes existing primary keys to be rejected as duplicates.
I logged a call in September 2001 asking Compuware to include an /init switch on the
XMLLOAD statement to prevent these validation flags from being set, but they do not consider it to be of any importance.
I notice from the documentation for version 8 that Compuware now recommend using the
RELEASE command to unset the modification flags after loading data from an XML stream. Unfortunately this also clears out the values for
$occcrc which are vitally important when attempting to reconnect occurrences to the database. Nice try Compuware, but not good enough!
PUTDATAoperation on the session service is activated. This replaces the
STOREcommand in the <STORE> trigger.
However, there are some triggers containing code that should not be fired because of the
STORE, but which should be fired if the data is changed. I achieve this with the following code:
$$store_after_xmlload = 1 ; set flag store/e <entity> ; clear all modification/validation flags $$store_after_xmlload = 0 ; unset flag
Any trigger that would normally activate the session service to perform remote validation contains a call to a global proc, and each of these global procs has been modified to test the contents of the
$$store_after_xmlload variable before it does anything else. If it is set then the global proc terminates immediately with a zero status.
Remote validation is where the presentation layer component passes data to a session service in the business layer so that it can be validated. Remember that the session service may be running on a remote server and not the client device. The session service will either pass back a zero status, or it will pass back an error message which can be displayed to the user. This is supposed to be achieved by using
$OCCPROPERTIES for occurrence errors and
$FIELDPROPERTIES for field errors. These will cause the error messages to be included in the
valerr attribute of the field or occurrence in the XML stream. When the data is loaded back into the presentation layer component the
XMLLOAD command will detect the existence of an error message in the
valerr attribute and automatically fire the relevant <ON ERROR> trigger so the message can be displayed.
So what is wrong with this you ask? Quite simply the
$FIELDPROPERTIES function cannot be used in a self-contained service. One of the advantages of using the 3-tier software architecture is that it can be deployed on a 3-tier hardware structure with all services and reports running on a remote server. If Compuware state that services which are to be deployed remotely should be self-contained, then why do they prevent remote validation from working in a self-contained service? Where is the logic in that?
I logged this as a fault in May 2001, but it has yet to be resolved.
So what is my work-around? Instead of using
$FIELDPROPERTIES to pass error messages from the session service to the form component I use my Message Object instead. This has the advantage that it can deal with any number of error messages at a time. And it works in self-contained services.
valerrattribute after remote validation
DISCARDstatement in a <validate key>, <validate field> or <validate occurrence> trigger because this is a no-no according to the Guidelines for use of proc statements in triggers. This can result in Invalid Page Faults which Compuware refuse to investigate.
XMLLOADbecause there may be other occurrences that I do not want to lose, or perhaps some fields or inner entities that are not included within the DTD that would otherwise be lost.
I logged a call in September 2001 asking Compuware to include an /replace switch on the
XMLLOAD statement to prevent these duplicate occurrences from being created, but they do not consider it to be worthy of their attention.
So what is my work-around? Instead of loading the XML stream so I can obtain any error messages from the
valerr attribute I ignore the XML stream completely and get any error messages from my Message Object.
It is quite possible that during remote validation some values may be modified in the session service which must then be returned to the presentation layer component. If I return these changes to the presentation layer component in the form of an XML stream then I will have problems with duplicate occurrences, as identified in the previous section.
So what is my work-around? I simply modified my remote validation operation so that it returns any changed data as an associative list as well as an XML stream. It is then a simple matter to use
getlistitems/occ to overwrite the current occurrence instead of using
XMLLOAD and creating a duplicate occurrence.
The approach adopted by Compuware in UNIFACE EIGHT is to use a single application model (which they refer to as the Business Object Model) but to create subtypes of each entity for different types of component. These 'component subtypes' are named according to the following conventions:
They do this so that they can provide different trigger code in each component subtype that is relevant to that type of component. I do not like this method for two reasons:-
Why should this be important? I first came across the idea of using a separate application model for the presentation layer in 1999. By creating a 'logical' model which was separate from the 'physical' model you were supposed to be able to hide any complexities in the physical database from the presentation layer components, which were supposed to be kept as simple as possible. The ability to have a totally separate application model provides the following features:-
In my software I refer to the 'physical' model as the Business Application Model (BAM) and the 'logical' model as the Presentation Application Model (PAM).
'How can you reconcile the differences between the two models?' I hear you ask. When using the
XMLSAVE commands you have the ability to specify object mapping. This is defined at the DTD level, and provides the ability for objects in the DTD to be mapped to objects inside the component with different names. I use this ability in my session services to access objects with their physical names in the component's structure, but to convert them to their logical names in the XML stream. I do not use this mapping facility in the presentation layer because the object names in the XML stream are exactly the same as the object names in the Presentation Application Model.
Here are some examples which show how this facility can be used:-
Suppose in the BAM I have an entity called PERSON which contains a foreign key (PERS_TYPE_ID) which links to a foreign entity called PERS_TYPE. If I wish to display the description that is related to a particular value of PERS_TYPE_ID I would normally have to include the foreign entity in my component. However, in my PAM I can add the PERS_TYPE_DESC field to my PERSON entity. The presentation layer does not need to know that PERS_TYPE_DESC has to be obtained from a different entity - this is totally transparent in the presentation layer and only of concern to the session service.
Let me draw you a picture:
This is a relationship between two entities (for example, X and Y), where more than one occurrence of Y can exist for each occurrence of X, and more than one occurrence of X can exist for each occurrence of Y. This is resolved by having a link entity between them to form two one-to-many relationships, as shown in the following diagram:
This can all be achieved by creating an entity in the PAM with the following definition:
|X_ID||foreign key||primary key|
|Y_DESC||description for entity Y|
The session service contains all the code necessary to construct this PAM entity from the BAM entity, and to create/delete occurrences of the LINK entity based on the new setting of each checkbox once the data is passed down from the presentation layer for updating. The presentation layer component does not know how many entities are involved, all it knows is that there is a checkbox that can be toggled on or off.
In this example the BAM contains two entities to hold address details - an address header, with each address line held as a separate occurrence in a separate subordinate entity.
In the PAM these two are combined into a single entity, as follows:-
|ADDR_LINE_1||address line 1|
|ADDR_LINE_2||address line 2|
|ADDR_LINE_n||address line n|
It is the responsibility of the session service to merge the two BAM entities into the single PAM entity before the data is passed back to the presentation layer. If the data is updated it must then extract the data from the single PAM entity and write it to the two BAM entities.
There may be occasions when you require to display a piece of data that does not actually exist in the database in that form, but which has to be derived at runtime. This 'derivation' can now be removed from all presentation layer components and performed solely within the business layer.
A typical example is where you have UNIT_PRICE and QUANTITY from which you can derive EXTENDED_PRICE. You can include EXTENDED_PRICE in the PAM entity and have the session service perform the calculation.
Another example is where the contents of several string fields may need to be joined together and presented in a single string field. Thus you might have TITLE, FIRST_NAME, LAST_NAME and INITIALS on the database, but wish to present them in a single field called PERSON_NAME.
2nd May, 2002
|27th June 2002||Added a comment regarding the use of the