Back

Multi-layered Architectural Design

Jim Booth

Application Architectural Design

Introduction

The architecture of an application is the high level “block-diagram” of how the application is structured.  In common practice this design is often a result of “How we always did it” mentality.  The architecture of an application will influence all other decisions of design in that project.

There is no “correct” architectural design for applications.  Each design has its merits and its drawbacks.  There is, however, correct and incorrect implementations of any particular design.  What makes an implementation correct or incorrect is how closely it follows the “rules” of the design.

I have often heard the question, “What is the correct multi-layered design?”  There is no single answer to that question.  There are many good designs.  There are also many applications that did not follow the architecture design with which they started; these are examples of poor implementations.

In the following sections we will examine a couple of different architectures for application development.

Single Layer

Applications can be divided into “layers” of responsibility.  In the simplest design there is a single layer.  This one layer handles all activities of the application.  The figure below shows this design.



In this single layer design the single layer of the application handles all the activities of the application.  Visual FoxPro can easily be used to create these designs.

Advantages

All designs have both advantages and disadvantages.  The major benefit of the single layer design is the sheer simplicity of the design.  All functionality is contained in a single layer.

Visual FoxPro, because of its ease of “instance programming”, can lull a developer into the single layer design.  Writing code in the form designer often results in a single layer structure.

Disadvantages

Single layer architectures do not adapt to changes very easily.  Often a minor change in technology or requirements will result in a major rewrite of large portions of the application.

Multi-Layer

The next level of application architecture is to move from the single layer to multiple layers.  The major goal of moving to a multiple layered design is to achieve a componentization of the application layers.  The various layers of these multiple layered designs have very specific responsibilities.  These designs call for a separation of these responsibilities clearly into the layers in such a fashion that the different layers do not over lap each other in terms of their behavior and responsibilities.

Componentization

The word componentization means to enclose functionality into discrete components that can be interchanged to expand or enhance the application.  For example, a given application needs to store data.  Initially the application is distributed to a single department of a company.  Later the requirements change such that the data in this application must be shared with other applications in different departments, even departments that are located in different cities.

Ideally we should be able to simply open the data for sharing and be done with it.  However, in this situation there is a requirement for data security.  Our initial data storage system did not include a high level of data security because the data was local to the using department.  If we had achieved componentization of the data storage, then, in theory, we should be able to simply change the data storage system in use to one that provides the required security levels and make minor adaptations to the existing application.

Two Layers

The first multiple layered design we will examine is the two layered design, often called client-server design.  In the two-layered design we separate the presentation of the information from the storage of the data.  The figure below demonstrates this design.



In the figure you can see the two layers, presentation and data storage.  The presentation layer is responsible for displaying the data to the user and accepting input from the user.  The data storage layer is responsible for storing and retrieving the data to and from the storage media.

Notice in this design that the requests go in only one direction, from presentation to data storage, although the data moves in both directions.  The single direction of requests in a integral part of this design.

Three or More Layers

Now lets look at a more complex design, the three layered model.



In this three-layered design we have introduced a layer titled Business Logic.  This layer is responsible for enforcing the business rules on the data.  The raw data is stored in the data storage layer.  The presentation layer is responsible for displaying the data to the user and for accepting input from the user.  The new business logic layer is responsible for formatting the data into information based on the business rules and for enforcing constraints on the user’s input based on the same business rules.

Other Layers

This layered design can be carried further into 4, 5, and 6 to n layers.  Below is an example of a 4-layered design.



It is this 4-layered design that we will be implementing later in this seminar.

Advantages

The advantages of the multiple-layered design are found in the maintenance and evolution stage of the application’s lifetime.  As technologies and functionality requirements change over time it is easier to adapt the multi-layered application than it is a single layered design.

The level of componentization achieved will directly influence the amount of effort needed to adapt the application the new requirements.

Disadvantages

Since the design is more complex than the single or two layer design, the implementation will also be more complex.  When compared to pure VFP single layered design data access performance may be affected.  Because of the nature of the data access mechanisms the presentation layer’s user interface design may be affected.

Although these are listed as disadvantages, they can be more accurately described as effects of the multiple layered architecture.  This is because they may or may not have a negative impact, in fact, their impact may be positive.

Our Multi-Layered Design

The following diagram shows the multi-layered design that we will build in this seminar.  This design uses 4 layers divided into Presentation, Business Logic, Data Access, and Data Storage.



The concentric circles are used to demonstrate that the layers only communicate with the adjacent layer and never skip a layer.  That is the presentation layer only communicates with the business logic layer and the business logic layer only ever communicates with the data access layer.

Special problems to a distributed application

Our exercise in this seminar is to create an application architecture design that can be used to build applications that meet certain requirements.  The requirements are as follows:

1 The applications are data centric in nature.

2 The applications will be primarily used for the in house activities of our clients.

3 The data in these applications must be available to other applications.

4 The business rules developed during our project must be enforced regardless as to what application is accessing our data.

5 Current applications being developed are our VFP applications only.

6 Future applications that need to be anticipated are web based applications as well as other desktops applications that may or may not be developed using VFP.

7 The data storage must be capable of scaling as the amount of data, applications, and users accessing it increases.

8 It must be possible to have data stored in multiple databases simultaneously as some of our data requirements may be already established in existing applications.

In reviewing these requirements we can see that our architecture must be very flexible indeed.  While we can depend on the features of VFP for our current work, we cannot afford to force the architecture to require VFP for its implementation.  In other words, whatever architecture we design must be capable of being written in many different development languages.

In addition to the above issue we have to deal with the special needs of a web based data application in our architecture.  The web has some special issues related to it that simple LAN based applications don’t need to address.  One of the most important issues has to do with the simple fact that in a web situation the client machine is not constantly in contact with the server machine.  This means there is a “disconnected” environment in effect.

Also in the web situation our business rules code cannot hold any “state”.  This means that from one call to another the business objects cannot assume anything about the context in which they operating, they cannot assume records pointer positions, data values, or anything else.  Why, because they may be receiving calls from different clients sequentially.  Study the following diagram for a moment.

Internet Configuration



In a LAN configuration the clients are always connected to the server, and there are known number of clients.  In the Internet configuration the clients are not always connected, a client will connect, make a request, get a response and then disconnect from the server.  The types of connections can vary widely on the Internet.  Then number of clients is never really known by the server and can vary widely in a very short period of time.

Describe a disconnected data access structure

The problem described above leads us to the realization that we need to somehow have a disconnected access to our data if we are to have an architecture that will scale to web based applications.  Now we could design a connected data architecture for our internal applications and use a different architecture for any web-based components that may arise in the future, but that would cause us to be using two differing application architectures and would totally prevent use from leveraging any work we did on the internal applications for our web requirements.

Instead we could design a disconnected data access mechanism that can be used in both the web-based environments and in our internal VFP applications.  What we need is a data access mechanism that is disconnected and stateless.  The diagram below shows the design we need to achieve.



In this design the business logic layer and the data access layer are not always connected.  The presentation layer will request data from the business logic layer.   Then the business logic layer will determine if it has the data necessary to comply with the request, if it does not it will make a connection to the data access layer and request the required data.  The data access layer will then submit a request to the data storage layer for the necessary data and return it to the business logic layer.  Once the data access layer has returned the data it then forgets everything it knew about that data and is prepared for a new request.  The business logic layer will then supply the requested data to the presentation layer.

In the reverse, that is updating data, the sequence is similar.  The presentation submits some data top the business logic layer for updating.  The business logic layer applies whatever business rules there may be and, given that the rules are passed, it establishes a connection to the data access layer and submits a request to update the data.  The data access layer then submits the request to the data storage layer and returns a status value indicating whether the update was successful or not.  The business logic layer will then return that same status to the presentation layer.

So why do we need all of this complexity?  If you examine the architecture carefully you can see that the only layer that needs any knowledge of how the data is displayed or manipulated by the user is the presentation layer.  This means that our business logic classes can support multiple differing user presentation without needing to be changed internally.

The business logic layer has no knowledge of exactly where the data resides or how the data is accessed.  These technologies are isolated to the data access layer.  This means that we can easily move the data storage from VFP tables to SQL Server without making any changes to the internals of the business logic layer.  We can also change our data access methodology from SQL Passthrough to ADO+, or any other data access technology, by making changes in the data access layer alone.

Programming to Interface not implementation

What, exactly, makes all of this possible?  It is a simple but pervasive concept called “programming to interface not implementation”.

The only communication between the business logic object and the data access object is through their respective interfaces.  This gives us an architecture where the only dependencies are between the interfaces and not the specific code used within an object.

An object’s, or class’s, interface is comprised of the public methods, their arguments and return values and the public properties of the class or object.  The implementation is the specific code inside of the methods.

Design classes to support a disconnected data access design

So in our diagrams above we can see that we will need two sets of classes to access and write data in our architecture.  We will design these classes in separate class libraries so that they can be used independently in the future.

The first class library we will create will hold our data access classes.  The design of our data access base class is shown below.



Let’s look at some documentation of what these classes do for us.

Data Access

The data access class is a composite and abstract class.  It is composite in that it contains an XMLHandler class and it is abstract in that it is not used directly to create objects.

The data access class has two subclasses named VFPDataAccess and ADODataAccess.  The intention of these two subclasses is to allow for the implementation of two different methods of accessing the data storage, one using Visual FoxPro data and VFP access methods and the other using other data sources and ADO methodologies to access that data.

Each of these subclasses has a subclass for each “entity” that needs to allow data access.  In the diagram we show the Customer and the Order entities.

The XMLHandler is used to produce and interpret XML streams that move between the data access and the business logic layers.

XMLHandler

The XMLHandler class is also an abstract class used to introduce the interface for all XML handlers in the design.  Our diagram shows two concrete subclasses, VFP6XLMHandler which is coded to work under Visual FoxPro 6.0 and VFP7XMLHandler which is coded to work in Visual FoxPro 7.0.

We need to two subclasses because VFP 6 and VFP 7 have different capabilities when dealing with XML.  VFP 7.0 has many built in functions that can be used whereas VFP 6.0 does not have these functions available.

You will see, later, that we reuse these XMLHandler classes in our business logic layer classes.  Let’s look deeper into the XMLHandler class design for VFP 6.0.

Internal Design of XML Handler class

We will start our in depth discussion of these classes with the XMLHandler class.  First let’s mention why we have an XML handler at all.  Since we are designing a disconnected data access system we need a way to move data between the data access layer and the business logic layer.

There are many ways we could move the data.  We could use VFP cursors or we could use a text stream, however XML provides a standard structure for moving data around that allows us to make our system versatile and robust.  By using XML we are not “married” to Visual FoxPro for both layers since other tools can assimilate XML as well.  We are also not making ourselves proprietary by using some structure that is private to our design; instead we are using an industry standard.


Below are listed the methods in our XMLHandler class along with a description of what each method does.

Method Name Visibility Description
CreateCursor Protected Creates a cursor structure to hold the data in the XML stream.  Actually creates two matching cursors, one that will be edited and one that will remain exactly as originally populated by this object.
CreateEmptyXML Protected Creates and XML stream that contains only a schema with no data.
CreateSchema Protected Creates the schema description for the XML stream
CreateXML Public Creates the XML stream for sending out.
GetNumSize Protected Determines the size of a numeric field within an XML stream when parsing the stream.
ParseXML Public Takes an XML stream and creates the two cursors described above.
PopulateCursor Protected Puts data into the cursors created by the CreateCursor method.
VerifyXMLStream Protected Verifies that an XML stream is one that this object can read.
Below is a listing of the properties of the XMLHandler class along with a description of their purpose.

Property Name Visibility Description
cLastError Public Holds a description of the most recent error that occurred in this object.
cXMLStream Protected Holds the XML stream being processed.
FieldList(1,5) Protected Holds a list of the fields that are included in the data being processed.


The job of the XMLHandler object is to take a cursor and produce an XML stream containing the data within the cursor and to take an XMNL stream and produce a cursor with the data contained in the XML stream.

Both the data access class and the business logic class must do these two processes in order to exchange data between themselves.  They both will use this XMLHandler class to accomplish these tasks.  Since they both use the same XML handler class any changes we make to the XML or the processing of the XML will be incorporated in both sides of the data access operations.

Internal Design of Data Access class

This object provides services to the business logic layer and consumes the services of the data storage layer.  It is the bridge between the business objects and the database system used to store the data.

Below are listed the methods of this class along with a description of their functionality.

Method Name Visibility Description
DoDelete Protected Deletes an existing record from the data source.
DoInsert Protected Inserts a new record into the data source.
DoUpdate Protected Updates an existing record in the data source.
FetchData Public Gets data and returns that data based on a request from the business logic layer.
WriteData Public Writes data to the data source based on a request from the business logic layer.


Now the properties are described.

Property Name Visibility Description
cAliasName Public The alias name to be used for the cursor that temporarily holds the data.
cDatabase Public The name of the database that is the data source
cFields Public A delimited list of field names within the data source.
cLastError Public Description of the last error that occurred in this object.
cTable Public The name of the table that is the data source.
cWhere Public The where clause that will be used in the SELECT command that gets the data or does the updates.
cXMLHandler Public The class name for the XML Handler that this object should use.
cXMLHandlerClassLib Public The name of the class library that the XML Handler class resides in.
FetchType Public The types of fetch data operation for this object.  Designates whether this object does a SELECT, uses a remote view, uses ADO, uses SQL Passthrough, or fires stored procedures in the data storage system.
nConnection Public The connection handler for this object if it is using a connection.
pkFieldName Public The name of the primary key field for the table that this object gets and writes data from and to.


Internal Design of Business Logic class

The following tables show you the methods and properties of the business logic object design.



Method Name Visibility Description
AddRecord Public Adds a new record to the cursor.
CheckFieldName Protected Checks for a valid field name.
DeleteRecord Public Marks a record for deletion.
FetchData Public Gets data from the data access object.
GetField Public Returns the current value for a field.
GetFirst Public Moves to the first record in the cursor.
GetLast Public Moves to the last record in the cursor.
GetNext Public Moves to the next record in the cursor.
GetPrevious Public Moves to the previous record in the cursor.
IsDeleted Public Returns a logical value indicating whether the current record in the cursor is marked for deletion.
NextAvailable Public Returns logical value indicating whether there are any other records further in the cursor.  Sort of a logical end of file.
PreviousAvailable Public Same as NextAvailable except it is checking for beginning of file.
PutField Public Updates a field in the cursor with a new value.
RecallRecord Public Unmarks a record fro deletion.
RemoveUnchanged Protected Removes all records in the cursor that have no changes to them since they were obtained from the data access layer.
RevertRecord Public Reverts any changes that have been made.
ValidateData Public Method to be used by developers to do any data validation that may need to be done.
WriteData Public Writes the data within the cursor back to the data access object.


The properties are listed below.



Property Name Visibility Description
cAliasName Public Alias name for the cursor.
cXMLHandlerClass Public The name of the XML handler class to be used.
cXMLHandlerClassLib Public The class library for the XML handler.
DataAccessClass Protected The data access class to be used.
DataAccessClassLib Protected The class library for the data access object.
FieldList Protected A list of valid field names.


As you have seen in the previous diagrams these two objects (the business logic object and the data access object) work together to provide access to data with both read and write operations.  These objects could reside on the server moving data to and from the presentation layer using XML, or they could reside on different machines.  That is, the data access object residing on the server and the business object on the client.