Sei sulla pagina 1di 8

Application Architecture: An N-Tier Approach - Part 1

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. 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

The Data Tier


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.

The Presentation Logic Tier


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.

The Proxy Tier and the Distributed Logic


There's also that little, obscure Proxy Tier. "Proxy" by definition is "a person [object] authorized to act for another. 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.)

The Client Interface


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.

The Business Tier


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. 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. 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.

Data Access Tier


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.

Application Architecture: An N-Tier Approach - Part 2

Introduction

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.

Section 1: Brief Needs Analysis


Here's a bit of information about what we are going to develop.
A. The Business Portion of the Application:

1. It is going to be an address book.


2. All users must login in order to gain access to their address book.
3. An address book has to have one owner, but can be owned and administrated by many.
4. An owner is a contact of the address book.
5. An address book can have one to many contacts.
6. Each address book has a name and a description.
7. Each user can have and belong to many address books.
8. A contact can only appear once in any address book.

B. The Implementation of the Application:

1. It is going to be available over normal HTTP (a Web site).


2. It is expected to be available over eXtensible Markup Language (XML) Web Services (via HTTP).
3. We do initial development with Microsoft Access 2000.
4. We want to be able to easily upgrade our database from Access to SQL Server 2K after development is done.

The rest of this article will work down from our Data Tier to the Presentation tier, in that order.

Section 2: Defining and Creating the Data Tier

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).
Figure 2.1 Contacts Table

zero
Key name type size defaultvalue required length indexed
PK emailaddress text 150 yes no yes
yes,
password text 10 no yes duplicates ok
first text 50 no yes no
last text 50 no yes no
areacode Number Integer 0 no no
Long
phone Number Integer 0 no no
Long
fax Number Integer 0 no no
address text 200 no yes no
address1 text 200 no yes no
city text 50 no yes no
zip text 10 no yes no
state text 75 no yes no
country text 50 no yes no

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

zero
Key name type size defaultvalue required length indexed
Long yes, no
PK bookid autonumber Integer yes no duplicates
yes,
name text 50 yes no duplicates ok
description 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.

Figure 2.3 AddressBookContacts Table

zero
Key name type size defaultvalue required length indexed
FK Long yes, duplicates
(PK) bookid number Integer 0 yes ok
FK yes, 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

zero
Key name type size defaultvalue required length indexed
PK id AutoNumber Long yes, no
Integer duplicates
name text 100 yes no no
description 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:

Figure 2.4 Sample Relationship Data

id name description
1Owner An owner of this address book
2Contact A contact belonging to this address book
3Administrator An administrator of the address book. These users can edit, add,
and remove contacts of the book, but cannot delete the entire
book itself.
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.

Section 3: Setting Up the Development Environment

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).

Change the name of the Class File Name. This can be found in the Solution Explorer, Click the "Class1.cs" file, and view the Properties
pane. You will see the "File Name" attribute. Rename it "AddressBookDA.cs".

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. 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.

Section 4: Creating the Data Access Tier


Looking back at Part 1 of this article, 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.

Section 5: Creating the Business Tier


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.

Figure 5.1 List of Objects' Properties and Methods

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 of none yes
the object
Overloaded Constructor Loads up the appropriate bookid yes
address book
Overloaded Constructor Creates a new address book name, description yes
Contact
loadAddressBook Internal method to load up the bookid no
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 error none yes
message
Validate Validates itself none yes
Save Persists the current Address none yes
book to the database
AddContact Adds a contact to the current Contact, Relationship yes
address book
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 of none yes
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 error none yes
message
Validate Validates itself none yes
Save Persists the current contact to none yes
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. 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.

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.

Section 6: The ASP.NET Presentation Interface


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).

Figure 6.1 List of ASP.NET Web Forms


File Name Purpose Links To
Login.aspx Allow user to login addressbooklist.aspx
addressbooklist.aspx View list of a user’s address books createbook.aspx
listusers.aspx
createbook.aspx Create a new address book addressbooklist.aspx
listusers.aspx List users within an address book createbook.aspx
createuser.aspx
Create a new user within an
createuser.aspx address book listusers.aspx

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.

Section 7: The XML Web Services Presentation Interface

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.

Potrebbero piacerti anche