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 READ
, WRITE
and 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 $DBOCC=TRUE
.
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:
XMLLOAD
sets all modification and validation flagsWhen 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 $occstatus
and $occcrc
which are vitally important when attempting to reconnect occurrences to the database. Nice try Compuware, but not good enough!
So what is my work-around? One way to unset all modification and validation flags is to perform the STORE
command. This is not normally used in a 3-tier form because:-
PUTDATA
operation on the session service is activated. This replaces the STORE
command 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 $OCCPROPERTIES
or $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.
valerr
attribute after remote validationEven if the previous problem did not exist I would still have a problem obtaining the contents of the valerr
attribute because I would have to perform an XMLLOAD
, and this works by appending to the existing occurrence, not by overwriting it, and thus creating a duplicate. Unfortunately the removal of this duplicate occurrence causes no end of problems:
DISCARD
statement 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.CLEAR
before the XMLLOAD
because 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 XMLLOAD
and 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:
|
|
PERSON entity | |
---|---|
PERSON_ID | primary key |
PERS_TYPE_ID | foreign key |
PERS_TYPE_DESC | description |
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:
|
|
|
|
|
A typical form to maintain the contents of the LINK entity may be described as follows:
This can all be achieved by creating an entity in the PAM with the following definition:
X_ID | foreign key | primary key |
Y_ID | foreign key | |
ACCESS_FLAG | checkbox | |
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:-
ADDRESS entity | |
---|---|
PERSON_ID | primary key |
ADDRESS_NO | |
PHONE_NO | telephone number |
START_DATE | start date |
END_DATE | end date |
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.
Tony Marston
2nd May, 2002
mailto:tony@tonymarston.net
mailto:TonyMarston@hotmail.com
http://www.tonymarston.net
27th June 2002 | Added a comment regarding the use of the RELEASE command. |