Professional Documents
Culture Documents
Introduction
Developers must realize there is more to programming than simple code. This two-part series
addresses the important issue of application architecture
using an N-tier approach. The first part is a brief introduction to the theoretical aspects, including the
understanding of certain basic concepts. The second part shows how to create a flexible and reusable
application for distribution to any number of client interfaces. Technologies used consist of .NET Beta
2 (including C#, .NET Web Services, symmetric encryption), Visual Basic 6, the Microsoft SOAP
Toolkit V2 SP2, and basic interoperability [ability to communicate with each other] between Web
Services in .NET and the Microsoft SOAP Toolkit. None of these discussions (unless otherwise
indicated) specify anything to do with the physical location of each layer. They often are on separate
physical machines, but can be isolated to a single machine.
For starters, this article uses the terms "tier" and "layer" synonymously. In the term "N-tier," "N" implies
any number, like 2-tier, or 4-tier, basically any number of distinct tiers used in your architecture.
"Tier" can be defined as "one of two or more rows, levels, or ranks arranged one above another" (see
http://www.m-w.com/cgi-bin/dictionary?Tier). So from this, we get an adapted definition of the
understanding of what N-tier means and how it relates to our application architecture: "Any number of
levels arranged above another, each serving distinct and separate tasks." To gain a better
understanding of what is meant, let's take a look at a typical N-tier model (see Figure 1.1).
Figure 1.1 A Typical N-Tier Model
Since this has been deemed the Age of Information, and since all information needs to be stored, the
Data Tier described above is usually an essential part. Developing a system without a data tier is
possible, but I think for most applications the data tier should exist. So what is this layer? Basically, it
is your Database Management System (DBMS) -- SQL Server, Access, Oracle, MySql, plain text (or
binary) files, whatever you like. This tier can be as complex and comprehensive as high-end products
such as SQL Server and Oracle, which do include the things like query optimization, indexing, etc., all
the way down to the simplistic plain text files (and the engine to read and search these files). Some
more well-known formats of structured, plain text files include CSV, XML, etc.. Notice how this layer is
only intended to deal with the storage and retrieval of information. It doesn't care about how you plan
on manipulating or delivering this data. This also should include your stored procedures. Do not place
business logic in here, no matter how tempting.
Let's jump to the Presentation Logic Layer in Figure 1.1. You probably are familiar with this layer; it
consists of our standard ASP documents, Windows forms, etc. This is the layer that provides an
interface for the end user into your application. That is, it works with the results/output of the Business
Tier to handle the transformation into something usable and readable by the end user. It has come to
my attention that most applications have been developed for the Web with this layer talking directly to
the Data Access Layer and not even implementing the Business Tier. Sometimes the Business Layer
is not kept separated from the other two layers. Some applications are not consistent with the
separation of these layers, and it's important that they are kept separate. A lot of developers will
simply throw some SQL in their ASP (using ADO), connect to their database, get the recordset, and
loop in their ASP to output the result. This is usually a very bad idea. I will discuss why later.
There's also that little, obscure Proxy Tier. "Proxy" by definition is "a person [object] authorized to act
for another" (see http://www.m-w.com/cgi-bin/dictionary?Proxy). This "object," in our context, is
referring to any sort of code that is performing the actions for something else (the client). The key part
of this definition is "act for another." The Proxy Layer is "acting" on behalf of the Distributed Logic layer
(or end-user's requests) to provide access to the next tier, the Business Tier. Why would anyone ever
need this? This facilitates our need for distributed computing. Basically it comes down to you choosing
some standard method of communication between these two entities. That is, "how can the client talk
to the remote server?"
This is where we find the need for the Simple Object Access Protocol (SOAP). SOAP is a very simple
method for doing this. Without too many details, SOAP could be considered a standard (protocol) for
accessing remote objects. It provides a way in which to have two machines "talking" or
"communicating" with each other. (Common Object Request Broker Architecture [CORBA], Remote
Method Invocation [RMI], Distributed Component Object Model [DCOM], SOAP, etc., all basically
serve the same function.)
In this section of Figure 1.1 we notice that the end-user presentation (Windows forms, etc.) is
connected directly to the Business Tier. A good example of this would be your applications over the
Local Area Network (LAN). This is your typical, nondistributed, client-server application. Also notice
that it extends over and on top of the Distributed Logic layer. This is intended to demonstrate how you
could use SOAP (or some other type of distributed-computing messaging protocol) on the client to
communicate with the server and have those requests be transformed into something readable and
usable for the end user.
This is basically where the brains of your application reside; it contains things like the business rules,
data manipulation, etc. For example, if you're creating a search engine and you want to rate/weight
each matching item based on some custom criteria (say a quality rating and number of times a
keyword was found in the result), place this logic at this layer. This layer does NOT know anything
about HTML, nor does it output it. It does NOT care about ADO or SQL, and it shouldn't have any
code to access the database or the like. Those tasks are assigned to each corresponding layer above
or below it.
We must gain a very basic understanding of Object-Oriented Programming (OOP) at this time. Take
time to read over http://searchwin2000.techtarget.com/sDefinition/0,,sid1_gci212681,00.html and
make sure you understand the important benefits of OOP. To clarify, let's look at another example,
such as a shopping cart application. Think in terms of basic objects. We create an object to represent
each product for sale. This Product object has the standard property getters and setters: getSize,
getColor, setSize, setColor, etc. It is a super simple implementation of any generic product. Internally,
it ONLY knows how to return information (getters) and understands how it can validate the data you
pump into it (ONLY for its limited use). It is self-contained (encapsulation). The key here is to
encapsulate all the logic related to the generic product within this object. If you ask it to "getPrice," it
will return the price of the single item it represents. Also if you instruct it to "validate" or "save," it has
the brains to be able to handle this, return any errors, etc.
We can plug this Product object into another object, a "Cart" object. This cart can contain and handle
many Product objects. It also has getters and setters, but obviously on a more global scale. You can
do something like "for each product in myCart", and enumerate (loop through) each product within.
(For more information on enumeration, refer to http://www.m-w.com/cgi-bin/dictionary?enumeration.)
Now, when you call "getPrice" for the Cart object, it knows that it must enumerate each product that it
has, add up the price for each, and return that single total. When we fire the "saveCart" method, it will
loop for each "product" and call its "saveProduct" method, which will then hit the Data Access Tier
objects and methods to persist itself over to the Data Tier.
We could also take our simple Product object, and plug it into our "Sale" object. This Sale object
contains all of the items that are available for a particular sale. And the Sale object can be used for
things like representing all the items on sale at a given outlet or the like. I'm sure you are beginning to
understand the advantage of using an OOP environment.
This layer is where you will write some generic methods to interface with your data. For example, we
will write a method for creating and opening a Connection object (internal), and another for creating
and using a Command object, along with a stored procedure (with or without a return value), etc. It will
also have some specific methods, such as "saveProduct," so that when the Product object calls it with
the appropriate data, it can persist it to the Data Tier. This Data Layer, obviously, contains no data
business rules or data manipulation/transformation logic. It is merely a reusable interface to the
database.
Conclusions
In all of the systems that I have been able to dig my dirty little hands into, I have rarely ever seen both
the Business Tier and Data Access Tiers used. I mostly combine the two tiers. Allow the Business
Layer to talk directly to the Data Layer, and do not bother with the Data Access Layer. To justify this,
we are all developing on Internet time, and the last time I looked, it's still going at about 3 to 4 times
faster than normal time, which means we are expected to also work and produce at the same rate.
The bottom line is reducing the time to market. In my opinion, writing this Data Access Tier, which is
simply abstracting the Data Tier, is overkill, and ADO can be considered as this Data Access Layer. It
provides us with the interface directly. We still keep all SQL in the Data Tier (stored procedures), but
no business rules should be kept here.
Of course, the more tiers you add, the more performance is affected. The client hits "Save Cart" on
their Web-enabled phone, it hits the Business Tier to call the "Cart" "saveCart," which calls the
products "save," which goes either directly to the database or goes through the Data Access Layer
and finally persists into the database. This path does affect performance. It is up to the application
architect (you) to know and understand this, and all other factors affecting the system, and be able to
make a good decision on how to develop it at this level. This decision is usually pretty easily made,
depending on the amount of work and documentation that was produced from the analysis phase.
We all now know how to do this logically. Let's explain the why. A good example is to look at the
Presentation Logic Tier. Notice that many of its sections --the Web, the Proxy Tier, and the Client
Interface -- all sit directly on top of the Business Tier. We gain the advantage of not needing to redo
any code from that Business Tier all the way to the database. Write it once, and plug into it from
anywhere.
Now say you're using SQL Server and you don't want to pay Microsoft's prices anymore, and you
decide to pay Oracle's instead. So, with this approach you could easily port the Data Layer over to the
new DBMS and touch up some of the code in the Data Access Layer to use the new system. This
should be a very minimal touch-up. The whole point is to allow you to plug each layer in and out (very
modular) without too many hassles and without limiting the technology used at each tier.
Another example would be that we initially develop our entire system using VB (COM) and ASP, and
now we want to push it over to our friendly VB .NET or C#. It is just a matter of porting the code over
at each layer (phased approach), and voila, it's done. (Microsoft has given us the ability for interop
between classic COM and .NET.) We can upgrade each layer separately (with minor hurdles) on an
as-needed basis.
PART -2 Introduction
This article is the second in a two-part series regarding developing the client server model using an N-tier
approach. Throughout this article, I will be making many references to Part 1 (see
http://www.15seconds.com/issue/011023.htm). You might consider rereading it before proceeding.
The purpose of this article is to take the theoretical explanation given in Part 1, and show you how to
apply it to your future applications. The example I have chosen is a fairly simple one, an address book. I
will walk you through the development and integration of an N-tier model, showing you how it can be easy
to implement a new Database Management System (DBMS) (unplug your current database and plug in a
new one), and finally how to take your existing middle tier and deploy it on to a new platform (Web
Services). My platform of choice is the .NET Framework Beta 2, using Visual Studio Release Candidate 1
(RC1), using the new .NET language, C#. It is important for you to not focus on the actual code of this
article. That is irrelevant. Instead, put more focus on the way in which I developed and separated each
tier of the application. You must also realize that I'm not going to formally walk you through any distinct
stages of an application systems development life cycle, just the bare bones needed for the completion of
this application. I will also not cover extensive error-handling routines, and security is always a major
issue with any application so I have put in place only the minimum needed for this article. Please consult
one of the many resources regarding these features and implement them at your leisure.
The rest of this article will work down from our Data Tier to the Presentation tier, in that order.
Think of the Data Tier as your DBMS (a.k.a. Access or SQL Server). We must create our database so
that it will solve our requirements listed above and at the same time not limit our future development.
Usually the database administrator does this work, but since this is a fairly small application, we will
assume that role and throw the database together.
We start by opening up Access and creating a blank database. I called it "addressbook.mdb." Let's begin
with the Contact List that will represent your standard user. I decided to use the Email Address as our
Primary Key1 (PK) (explanation footnoted at end of section). (I have included the Access database as a
part of the download for this article).
The second table we will throw together is the Address Book table. This is going to represent each
individual address book in the system.
Figure 2.2 AddressBook Table
defaultvalu zero
Key name type size e required length indexed
autonumbe Long yes, no
PK bookid r Integer yes no duplicates
yes,
duplicates
name text 50 yes no ok
descriptio
n text 250 no yes no
If we take a look at both of these tables, you will see that the there is no relationship between them. What
we must do is define what is called an "intersection table." This is simply a table that will contain both of
the PKs from each table, which is used to relate each table to each other. We use this "intersection" to
assign a contact to an address book, for example, and other things.
zero
Key name type size
defaultvalue required length indexed
yes,
FK Long duplicates
(PK) bookid number Integer 0 yes ok
yes,
FK duplicates
(PK) emailaddress text 150 yes no ok
relationship Number byte 0 yes no
Let me run you through each of the fields for this table. The "bookid" field is the PK from the AddressBook
table, represented in this table as a Foreign Key1 (FK). The "emailaddress" field is the PK from the
Contacts table and is also represented in this table as a FK. Together, these two make up the PK for this
table. This allows us to have any number of contacts belonging to any address book. It also allows us to
not allow a contact to appear more than once in a given address book. The last field,"relationship", is
used to indicate the relationship between these records. Let's create a new table to describe these
relationships. Figure 2.3.1 Relationship Table
defaultvalu zero
Key name type size e required length indexed
AutoNumbe Long yes, no
PK id r Integer duplicates
name text 100 yes no no
descriptio
n text 255 no yes no
You may be asking yourself what exactly is a relationship and why do we need it? A relationship, in this
case, is how we relate the specific contact to the address book. An example would be "Owner", or
"Administrator", or "Contact". Here is some sample data for this table:
To the question "Why do we need this?", I say take a look back at our requirements, specifically A3. It
states "An Address Book has to have one owner, but can be owned and administrated by many." This will
allow us to enable the system to have any address book with a designated owner, but also an
administrator who would have some of the same abilities as the owner. This introduces a model where we
can grant access to different users to perform different actions to our address books and contacts.All we
need is to have at least one owner defined for each address book, and the rest can be optionally used in
order to start associating contacts to each address book. This will also allow us to define more roles in the
system and allow for contacts to have different sets of access to any given address book. I have found
this to be a very flexible model.
1 Keys -- primary and foreign -- They are used in establishing relationships between records. The
primary key in a table is the data field in each record, which is guaranteed to be unique and which can
therefore be used to establish relationships between each record in that table and other records in other
tables. The foreign key in a table is a primary key for records in another table. Foreign key values don't
have to be unique within a table, thus allowing a one- (primary key) -to-many (foreign key) relationship to
exist (see http://magni.grainger.uiuc.edu/lis450ds/introsql.htm).
Let's start the Visual Studio.NET development environment and create a new ASP.NET application. I
chose to start out with an ASP.NET application because our primary interface for this application will be
through the Web and ASP.NET. In the Visual Studio.NET start page, we first hit the "New Project" (1)
button. Personally, I prefer using C# rather than Visual Basic so I chose "Visual C# Projects (2), and
using a "ASP.NET Web Application" (3), I created the application at the location
http://localhost/AddressBook (4).
The next application we will add to our solution will be used to contain the Data Access tier. In the
Solution Explorer, right click the "AddressBook" Solution, choose "Add", "New Project". This new project
will be a reusable C# (1), Class Library (2), with the name "DataAccess" (3).
The next application we will add is our Business tier. Follow the same procedure as we did to add the
Data Access tier, but give it the name "Business" (3). Don't forget to change the name of its default class
to "AddressBookBiz.cs". And finally, since we wish to expose this entire application over Web Services
(HTTP), we will also add in a C# ASP.NET Web Service (3) application. Make sure you change its
location to http://localhost/AddressBookService. This time rename the "Service1.asmx" file to
"AddressBook.asmx".
The next step will be to set up the dependencies for each application. See Figure 1.1 at
http://www.15seconds.com/issue/011023.htm and the dependencies will become pretty clear. The
Business tier will depend on the Data Access tier, and above that the Presentation tier (ASP.NET
application, and Web Services application) will depend on the Business tier.
Right click on the "Business" application, and choose "Add Reference", "Projects" tab, and then select the
"DataAccess" application. Hit the "Select" button to add it to the "Select Components" section. Repeat this
process for both the "Address Book" application and the "AddressBookService" application, but for each
of these, choose the "Business" project. The next few steps will guide you through the setup for each
application.
Looking back at Part 1 of this article (see http://www.15seconds.com/issue/011023.htm), we see that the
Data Access tier is used to simply interface with our database. It contains no business logic or
constraints. It merely provides a way in which to interact with the database (Insert, Select, Delete, and
Update). In other words, it allows us to create a set of useful and specific routines to be able to perform
those four actions on our tables.
Most of this code is standard. To reiterate, the most important thing to remember for this tier is that it is a
set of specific methods that are used to perform all of the database interactions. It must NOT contain any
business logic or presentation items. If you wish, take a look in the AddressBookDA.cs file (part of
download accompanying this article) to see what methods I exposed and how I perform the database
operations with it now. Also pay close attention to how this has been created for an Access database.
Since each DBMS we use in our careers has different capabilities, our Data Access tier will differ slightly
for each. One of the nicest features of an N-tier architecture is the ability to easily switch this layer out
when a new DBMS is put in place. I will develop this application based on an Access database. When the
time comes to upgrade to SQL Server, or another quality DBMS, we will only need to modify our Data
Access tier and the rest of the application will function unchanged. This means no more rooting though
thousands of lines of code just to upgrade our in-line SQL to stored procedures.
If you recall, the Business tier is where we place all of our custom business logic. For this address book
application it will contain logic such as all of the items we find in Section 1, List A of our requirements.
There will be no connections to any database and no presentation-specific code.
In our Business tier we must expose a set of methods that will be useful and that will solve the business
rules. It is also important to remember that these objects can be stateful for the lifetime of that specific
object instance, but not stateful for the entire application's existence.
We will create two separate classes within this AddressBookBiz namespace, one to represent an actual
address book and another to represent our contacts.
Let's drill down for each object and determine the methods and properties that we will expose.
AddressBook
Object
Properties
Name Get Set Public
bookid yes no yes
name yes yes yes
description yes yes yes
relationshiplist* yes no yes
Methods
Name Description Parameters Public
Default Constructor Creates an empty instance none yes
of the object
Overloaded Constructor Loads up the appropriate bookid yes
address book
Overloaded Constructor Creates a new address name, description yes
book Contact
loadAddressBook Internal method to load up bookid no
the book. Called by other
methods
CanView Used to determine if the emailaddress yes
supplied user can view the
book
GetContactsDataSet Gets a DataSet of the none yes
contacts for the Address
object
AddNewContact Add a new contact to the Contact yes
current Address object
getLastError Simply returns the last none yes
error message
Validate Validates itself none yes
Save Persists the current none yes
Address book to the
database
AddContact Adds a contact to the Contact, yes
current address book Relationship
Contact Object
Properties
Name Get Set Public
emailaddress yes no yes
password yes yes yes
first yes yes yes
last yes yes yes
areacode yes yes yes
phone yes yes yes
fax yes yes yes
address yes yes yes
address1 yes yes yes
city yes yes yes
zip yes yes yes
state yes yes yes
country yes yes yes
Methods
Name Description Parameters Public
Default Constructor Creates an empty instance none yes
of the object
Overloaded Constructor Loads up the appropriate emailaddress, yes
user (login) password
Overloaded Constructor Loads up the appropriate emailaddress yes
user
saveContact save the specific contact none no
isUserValid Determines if the supplied none yes
user is valid or not
getAddressBooks Returns all of the user's emailaddress yes
address books (not
preloaded)
getLastError Simply returns the last none yes
error message
Validate Validates itself none yes
Save Persists the current none yes
contact to the database (if
we can)
* I’ve decided to place this method, which merely returns a list of the available
relationships, in the Address Book
object in order to keep this article short and simple. Normally you would probably
make the decision to create
a new, separate object that would perform the needed relationship
actions.
** Since each Address Book object will contain a collection of Contact Objects, our
Contact Pointer is merely
a reference to the current contact in our list.
At this time, review my version of "AddressBookBiz.cs" (part of download). Pay attention to the Save
method, and how it is first calling its own Validate method in order to ensure that it conforms to the
established rules. (This will validate the object at the object level). Calling the Save method in the address
book will validate the current address book, and then it will also call the Save method on each of its
contacts, which will also call its own Validate method. In the end, if everything validates fine, it will all be
persisted to the database.
I want to quickly review what I did regarding security. In order to represent the current user (who is logged
in), I needed a way to easily represent that user based on their login information. I used a basic
symmetrical encryption algorithm (borrowed from http://www.4guysfromrolla.com/webtech/090501-
1.shtml). This Namespace is based on the Cryptography base classes within .NET. To integrate it within
our "business" application, I only needed to add it to our application (see previous examples on how to do
this). Now, when the user is logged in, or authenticated against our database, I take their email address
and their password, and join them into a single string, and then apply the encryption algorithm. This
generates a Universal Unique (user) IDentifier (UUID). And when I need to determine who this UUID
belongs to, I simply unencrypt the UUID, and then split the string back into the email address and the
password and re-authenticate against my database. I created this in this fashion in order to simplify the
security issues for this article. More help on cryptography can be found at
http://www.demcom.com/english/steganos/htmlhelp/IDH_TECH_CRYPTOGRAPHY.htm or on many other
Internet sites.
I have completed most of the Business tier for you. I have left out some validation within each of the
Property Get/Set methods. You should conform each method to what you expect for this application. For
example, the Area Code property could be limited to only valid area codes (001 to 999), or the Country
property could be limited to only a set of ISO-standard country codes. This has offered you a place to put
all of your server-sided data validation. Another thing you may consider is looking into the validation tools
within the .NET architecture. There are plenty of examples of this available on the Internet, just as long as
you always keep in mind that validation MUST be performed on the server, and in your middle tier. You
can optionally add it on the client with JavaScript or whatever is specific to your deployment platform.
Adding validation to a single interface on the Presentation tier will only enable that specific interface to the
system to take advantage of that validation. Your other interfaces will not be able to take advantage of
this.
Now that we have a working infrastructure for our Address Book application, it is usually a small task to
wrap this functionality within any Presentation interface we wish. It is only a matter of hooking into our
Business tier. In order to keep things simple I have created a minimal amount of forms for you to review,
each with a slim interface with no fancy pictures or formatting (my excuse -and apologies-- for the ugly
design).
Take time now to review each file and the code behind each. First look at the login.aspx page that allows
the user to log in and get their UUID, which gives them access to the rest of the application. Also, you
may wish to start adding some functionality to the application. One important issue would be a way to
initially create a user when no address books are defined. Personally I would create an application
administrator (you), and create a global address book. And then each new user who is created (first time
in) will be added to this global address book. And then each of these users can create/edit their own
address books, etc... Take the logic from the createuser.aspx page and create your own, generic page to
allow for this functionality. You could also consider adding in a feature to share contacts between address
books. All you would really need is another table to represent the intersection between the shared
contacts and the appropriate users.
You can test this application by hitting the Start button in the Integrated Development Environment (IDE)
or by going to "Debug", "Start" menu items.
By simply exposing the methods in our Business tier within the "AddressBookService" application, we can
easily create a new interface into our application based on XML Web Services (or any other way, like
Windows Forms, etc...). I have started this interface for you, but left it incomplete on purpose, to allow you
to get your hands dirty by trying to port the rest of the Business tier to this new interface. All you really
need to know is that each user will start by logging into the application and getting their UUID. Then each
subsequent request will also pass this UUID in with the each method call. An alternative to this would be
to use SOAP headers so that you do not need to expose the methods with the additional UUID string on
each method.
Conclusion
During your examination of this article and the supplied code, you must always keep in mind what it was
intended for -- a simple demonstration on how one would develop an N-tier application. You should take
into consideration the concepts, more than the simple language or my choice of development
platform/framework and environment. You can easily take and apply these concepts back to your
ASP/COM or JSP/Bean environments; it really doesn't matter, just as long as you understand the
usefulness of keeping things separate using this tiered approach.
Some more things that I do recommend that you look into include:
http://www.aw.com/cseng/titles/0-201-89542-0/techniques/refactoring.htm
http://www.aw.com/cseng/titles/0-201-89542-0/vidrefact/vidrefact.html
http://www.industriallogic.com/xp/refactoring/
http://hem.passagen.se/gumby/cs/patterns.html
3. Systems Development Life Cycle
http://www.dbm.state.md.us/mdplan/apdx-a.htm
http://searchvb.techtarget.com/sDefinition/0,,sid8_gci755068,00.html
http://www.student.richmond.edu/~mbarkley/portfolio/projects/SDLC%20Lecture(1).ppt
4. Other
http://www.armaties.com/extreme.htm
http://c2.com/cgi/wiki?ExtremeProgramming
I did my best to produce code, which is in good working order, but as with most applications, there will
always be bugs. If you do find any problems, please send me an email and I'll try to make any fixes that
people send in. Also, if you do take the time to create a Data Access tier for any other DBMS than
Access, or maybe you finish the ASP.NET or Web Services interfaces, send me the code, and I'll add it to
the library (giving you full credits of course!). If you do plan on completing any of this and you need help,
drop me a line, and Ill be glad to lend a hand.
I do have to admit that this is my first exposure to using VS.NET for any type of application. I have spent a
lot of time with the .NET Framework and C#, but, specifically with this tool, this application was my first!
Usually I take what Microsoft says with a grain of salt. But when they say that the .NET Framework and
VS.NET increase productivity, they are very right. I believe I will finally make the switch out of simple
Notepad (or IDM Computer Solution's UltraEdit) and into this development environment.
This article is intended for all levels of developers, and I hope you find it useful.