07 October 2009
Arhitecture of a thick web application
Nowadays thick web applications are not the hip thing to do. Everybody is doing service oriented applications or rich internet applications or even cloud oriented applications. Distributed systems are the in of the moment. Thick applications are seen as antiquated, not performing solutions, unable to cope with the requirements expected from modern systems both in the performance/scalability and the maintainability/coolness sides of things.
Everyone forgets that a few years ago every application was a thick application, and that everybody started their careers learning and writing those types of applications. Of course there are still viable cases for such applications to be developed.
First, they are simpler to understand, develop, deploy and maintain. Secondly they can be developed much cheaper and faster than their distributed counterparts due to the lesser numbers of different parts which need to interact and be set up in order to deploy and maintain the thick system in respect to the distributed one.
I’ve actively developed user experience enhanced (web 2.0) applications both in PHP and ASP.NET (with a side note in Django and Google App Engine for a short time) and I’ve found that the following general application architectures is very suitable for developing thick web applications.
The architecture is composed of the following elements (from the front to the back):
Client side modules (screen). This is the top most tier of the application. Here is what is actually sent and executed to the client. Due to the fact how the majority of web application frameworks are organized here we talk about client side code which drives the behavior of a specific module(screen). In lesser respect we talk also about images, video, flash, HTML, and CSS code.
Server side modules (screen). This the first server side tier. This tier has two important tasks: first it must generate client side elements sent to the client in response to their request, and the other it must make use of the services provided by the service tier and the API provided by the Domain tier objects returned by the called services in order to compose them in a viable user task. A user task is a set of operations (a workflow) u user must do on the module (screen) in order to achieve a quantifiable business objective.
This tier is where is decided what service (or services and their interactions) must be used in response to a users operation and what message must be sent back to the user in response. This tier must “know” everything that it needs to know about the business problems it tries to solve to successfully communicate with the user.
Server side components. Server side components are composed of reusable elements (classes, functions, widgets or other similar elements) used one or more times in one or more modules. Each component must be used for data carrying or data transformation, it must rely for its required input on the server side module using it and must not decide what to do with its output , which is left to do for the module making use of the component.
Services. This the first tier on the middle tier side. How it is separated from the other middle tiers is not important. What is important how it is used and how it is structured. Regardless of the programming implementation (functional or object oriented) the Service tier must provide a set of stateless, not dependent services each providing an atomic operation. Services must be grouped together by the business task their can perform when used together, but must not make assumptions about the order they are called or the way messages are sent back and forward between the user and the application.
Services must concern themselves with their required input and expected output. If an error condition occurs (invalid parameter etc) the service must raise and exception (or similar message carrying ) construct which will provide technical data to the front end about the error condition occurred. It must not, however, return a full fledged user message.
Domain This tier is tasked with the modeling of the business domain the application is working on. The domain tier is composed from entities, value objects, events and repositories each interacting with each other in the same manner they interact in the business system used by the application. In order to find out more about the Domain tier please read the excellent book written by Eric Evans Domain Driven Design .
This tier is used by both the server side modules and the services. The services will work with input and output parameters taken from the domain tier, and the server side modules will use them to carry out their associated business tasks.
Infrastructure. This tier is used only by the service tier since it provides low level support for interacting with the underlying system, database, other application services and etc. If you need to open and read a file, or send an e-mail message use this tier. What to do with the read data from the file, or what to put in the e-mail message is the job of the server side and services tiers.
Database. The database, or the backend, is the final tier of our typical thick web application architecture. The database is used for permanent data storage, data storage manipulation, querying and fast retrieval. Only one thing is important regarding the implementation of the database: under no condition put a line of application logic in it, no matter how fast or simple it seems to be. It almost always is a bad decision, which will lead to lower system operation visibility and a nightmare of maintenance problems. Just don’t do it. Really.
How to tie all of this together. It’s both simple and terribly complex. What I’ve found out works best is to first design the domain of the application, then translate it into a data model and data transformation procedures. Secondly to define all the business task which must be carried by a user, group them in related modules and break each task into atomic operations which will be actually written as services using the domain tier of input/output.
When writing services I use the following technique: first I ask my self what questions as the user interface I have in order to successfully complete the business task, and then what operations I must perform in order to do it. As the last thing I define the input and output messages for each task/operation.
The infrastructure tier must be made low level as possible. Really low level stuff. The question I ask my self is what I need from the system (world) around me as the services and what I want to be present but also to forget it ever exists. The answer to both question goes into the infrastructure tier.