When it comes to building websites or web applications, almost everyone has it own recipe, and I am no exception. I thought I’d share the one I’ve been using and which works like a charm. Keep in mind that this is just an introduction, and that it can not be used as is. You’ll have to wait for part two for a more detailed and robust implementation :)
What is a layered architecture ?
A multilayered (software) architecture is using different layers for allocating the responsibilities of an application.
Well, this concept is not new, and it applies also to most real world organizations. Most of them work more or less the same way: they divide the tasks that are required to come up with a finished product (or service).
As an example, think about the way a restaurant works. The main actors are :
- the customer
- the waiter
- the Chef
They all have different responsibilities that can be briefly described as below:
- decides what he’d like to eat
- asks for the bill
- takes the order
- forward it to the Chef
- serves the meal
- brings the bill
- clean the table
- cooks (or gets the food out of the freezer)
It makes sense to have the waiter taking the customer’s order and asking the Chef to cook the desired meal. You wouldn’t let the customer go into the kitchen and take whatever he feels like having at anytime, would you ? Great, so why are you querying the database straight from your markup and mixing the logic bits eh ?
Why a layered architecture ?
Now that you know what a layered architecture is, the reasons why it is a good idea to build your site / application following those principles must be pretty obvious.
- clear separation of responsabilities — each layer being only responsible for itself
- exposed workflow — as opposed to the spaghetti code we’ve all see way too many times
- ability to replace one or several layers implementation with minimum effort and side effects.
While this is good in theory, setting everything up for the first time requires some efforts. You’ll have to set up all the layers upfront. Depending on the language & platform you’re developing for, you’ll have to include, import, add references to the classes we’ll define later.
This can be daunting in complex applications, but is fairly trivial for the majority of projects.
You may be wondering why there is another name for what looks like an MVC application, and you’d be totally right. The concept may be similar, the implementation can differ quite a lot.
MVC application, will refer to a website or application running on top of trendy frameworks such as Ruby On Rails or some of its clones. There are multiple ways to run a site on top of ROR and the goal here is not to point out what can be considered as broken, but to provide an alternative. No fanboyism here.
Anatomy of a layered architecture
What we’ve covered so far isn’t what I’d call a practical introduction. We’ve barely scratched the concepts. But before we delve into code, you need to know what the 3 main layers are:
- the Data Access layer
- the Business Logic layer
- the Graphical User Interface layer
Note : As our application grows and gets more complex, additional layers will be added. In the mean time, we’ll focus only on these 3 core layers.
Now would be a good time to define what will be exchanged between these layers in order to accomplish an action before taking a look at what’s under the hood.
For different entities to communicate, we need to agree on the information that will be exchanged.
Communication requires that all parties have an area of communicative commonality.
This is where the concept of Business Objects comes into play. They are the glue between the layers, going back and forth, holding the data and making interactions possible.
Business Objects — BO — are objects around which the application revolves. They represent the data that will be managed by the application. If you’re building a contact management application, everything it will do, will involve dealing with users. Create, Retrieve, Update, Delete — that CRUD stuff you’ve heard about — everything is about users. I leave it up to you to guess which class is the Business Object in a recipe management application.
For now, remember that a Business Object is a simple class with no methods, only properties. It might look like this:
class User: def __init__(self,firstname,lastname,email): self.firstname = firstname self.lastname = lastname self.email = email
Note: *This is not meant to be an introduction to Object Oriented Programming — the code here will be stripped down to the bare functionalities. It’s up to you to apply the best practices you’ve learned along the way *
Chances are that if you’re an OOP fanboy, the statement a Business Object is a simple class with no methods will make you cringe. I guess that it comes down to personal preferences in how objects are implemented. I do believe that in order to really split apart the responsibilities, the Business Object should be a collection of properties that will be managed by another object and nothing more.
Data Access Layer — DAL
The Data Access Layer — DAL — is as its name implies the layer at which the data is processed. It’s typical CRUD operations. Create, Retrieve, Update and Delete. What’s important to keep in mind, is that even though it’s the most common case, the DAL is not limited to a RDBMS. Any persistent layer is perfectly suited for the DAL. Remote calls through APIs, flat text files, XML files and every other persistent system should reside in the Data Access Layer. They’re not exclusive and can be mixed and matched. The goal of the DAL is to provide a uniform input/output mechanism for the layer above it in the stack, regardless of where the data comes from.
The DAL is composed of one or many Data Access Object — DAO. So for instance, a user management application will have a User DAO, which should look like this:
class UserDAO: def retrieve(self,user): #retrieve data from the persistent layer (MySQL, flat file, API call...) #and returns it or return None if user was not found userFromPersistentLayer = User(name,firstname,email) #do some data cleaning if needed return userFromPersistentLayer def create(self,user): #insert data into the persistent layer def update(self,user): #update data into the persistent layer def delete(self,user): #delete data into the persistent layer
Note: It’s up to you to name your methods according to the CRUD names, or to come up with alternatives.
def get(self,user) &
def insert(self,user) are perfectly valid names. Pick whichever you like best
Even though all methods should only accept a user as a unique parameter, they may return different types.
class UserDAO: def create(self,user): success = False try: #insert into persistent layer success = True except: #log that something went wrong return success
The DAL is the lowest layer of our application. On top of it, is — in this simple example — the Business Logic Layer.
Business Logic Layer — BLL
The Business Logic Layer — BLL — is obviously where all the Business Logic is implemented. Wikipedia sums it up as:
Business logic is a non-technical term generally used to describe the functional algorithms that handle information exchange between a database and a user interface.
For small applications, the BL is pretty basic if / else clauses that determine which functions should be called. It might look something like this:
if user.firstname.lower()[:1] == "j": #do something with user else: #do something else
The objects that constitute the BLL are objects called managers. You can tell by their name that they manage Business Objects. Since we’re dealing with user operations in our application, we’ll have to create a
UserManager class which will contain methods for managing the user. Most of the time, we’ll have the basic CRUD operations. The
Usermanager class should look like this:
class UserManager: def create(self,user): pass def retrieve(self,user): pass def update(self,user): pass def delete(self,user): pass #insert additional methods here
In order to talk to the DAL, the
UserManager class has to instantiate a new
UserDAO class. Even though it is not recommended to create new instances of a class in a constructor, in order to keep the example short & simple, that’s what we’ll do here.
class UserManager: def __init__(self): self.dao = UserDAO() def retrieve(self,user) #perform some business logic here if needed userRetrieved = self.dao.retrieve(user) #perform additional business logic here if needed return userRetrieved
This is how the BLL and DAL are linked together. The UserManager calls the appropriate UserDAO method after having performed its Business Logic.
Note: When the Business Logic is extremely simple or inexistent, the BLL may seem a bit superfluous, since it’s calling the DAO methods without performing any additional operation. While this is true, this is a necessary step to be able to add business logic at a later time, without having to rewrite a significant part of the code. Trust me, that happens all the time :)
All this is well and good, but we’re actually missing the only part that matters to the user of our application : The Graphical User Interface
Graphical User Interface — GUI
The Graphical User Interface — GUI — is the only visible part of the application. It may have several representations:
- flash / flex
- Silverlight / WPF
- Cocoa / Cocoa touch
In short, the goal of the GUI is to collect the input data and pass it along to the Business Logic Layer and wait for the enriched data or success/failure message to come back, in order to provide a visual feedback that the action has been processed (flashing message, redirection to another page …).
How does that happen ? First, the GUI has to create a new BO with the input data:
userFromGUI = User("Jon","Doe","firstname.lastname@example.org")
Then it passes it along to the appropriate method of the manager, in our case, the UserManager from the BLL userManager = UserManager()
#in this case we expect a User to be returned userFromDB = userManager.retrieve(userFromGUI)
Then we process the data we’ve received:
if userFromDB is not None: print "Welcome back %s" % (userFromDB.firstname) else: pint "Looks like we've no information about you."
In its simplest aspect, this is all the GUI has to do. Set the data, pass it along, and behave according to the data that has been returned.
But how is this different from the MVC architecture ?
Up to this point, it’s unclear how the layered architecture is different from the MVC architecture. The most important point is that none of the code we’ve implemented relies on a URL routing mechanism. We don’t even know if we’re operating in a web environment ! Of course, that means that we need to add another brick to our stack to deal with URLs, but that also means that this part of the code can be shared among several web, desktop or mobile applications. That’s the beauty of loosely coupled applications :)
Stay tuned for part two, we’ll integrate our example in the Google App Engine environment.