ost developers understand the value of the layered approach to architecture. The main principle behind layered architectures is that of "separation of responsibility". Each layer is responsible for a finite amount of work. Any work that cannot (read should not) be done by a particular layer gets delegated to a layer more appropriate for handling the task.
Unfortunately, people using layered architectures can often run into a scenario where they introduce an unnecessary amount of coupling between layers of their application. A high degree of coupling is one factor that can lead to fragile application architectures that are difficult to change or extend.
In this article, I'll take a project that was built using techniques that result in fragile, hard-to-test code and introduce some principles, techniques, and refactoring strategies that will help you realize flexibility and testability in your applications.
The first part of this article deals with introducing a design principle that will enable you to take advantage of layered architectures in a much cleaner fashion, and then demonstrates how introducing dependency injection into the mix can help you realize your goals for pluggable application architectures.
The Case at Hand
|Figure 1. A Simple Screen: This simple screen looks like it should be easy to build, right?|
Assume for a moment that Figure 1
is a screen that I am developing for viewing information on Employees that work with my company. Being that it's such a "simple" screen, I dive in and quickly hammer out the code shown in Listing 1
. (In case anyone asks, I would never do this!)
I'll pause for a second to allow the utter travesty of the code in Listing 1
to truly sink in! The code breaks several application architecture rules:
Separating Responsibilities with a Layered Architecture
- Hard-coded connection strings.
- User interface has intimate knowledge of database tables.
- User interface is responsible for mapping database data into domain objects.
- Coding to implementations not abstractions.
- Forget about that last bullet point for a little while. I can start to remedy the first three items by introducing a layered application architecture.
|Software developers use the term cohesion to describe the relation and focus of a particular component.|
One of the main issues with the code in Listing 1
is that it takes the single responsibility principle and throws it completely out of the window. The single responsibility principle simply states that "every object should have a single responsibility and therefore only a single reason to change." If you are already familiar with the term cohesion, you can quickly identify that a component that follows this principle can often be described as a "highly cohesive" component.
If I took a moment to ask the question, "What should the main responsibility
of the View Employees web page be?" The simple answer is that it should only be dealing with the rendering of a set of employees to the user. However, if I take a look at the code-behind, the reality of the situation is quite different. Instead of methods focused around the rendering of employees to the user, the component is currently responsible for:
- Creating a connection to the database
- Creating a SQL statement to pull the appropriate information from the database
- Disposing of expensive resources (connection, reader, etc.)
- Mapping the database information to a domain representation of the data
- Rendering the information to the user
As you can see from this short list, this component has far too many responsibilities. To make matters worse, only one of the responsibilities has anything to do with "rendering" a list of employees to the user. If this does not smell of low cohesion, I don't know what does.
|Figure 2. Layer Diagram: Here's a proposed high-level diagram for the separate layers.|
For quite some time, most developers have been aware of the value of introducing the concept of n-tier/layered architectures into their applications to address this very issue. In short, the introduction of a layered architecture can ensure that each layer (as pragmatically as possible) can adhere to the single responsibility principle. Figure 2
shows a proposed high-level diagram for the separate layers that will make up the application.
I am going to tackle refactoring this application from the top down. I can first make use of the "passive view" variant of the "model view presenter" design pattern to deal with the abuse of responsibility in the code-behind for the web page. I will not dive into the details of this pattern in this article; you can take a look at an article I wrote last year
that discusses the pattern in more detail)
The first refactoring task is to pull out code not directly related to the responsibility of "rendering employees" (read: pull pretty much all code from the code-behind!). The following is now the code-behind for the web page:
public partial class
ViewEmployeesWithAdditionOfPresenter : Page,
private ViewEmployeesPresenterLowCohesion presenter;
protected override void OnInit(EventArgs e)
presenter = new
public IList Employees
this.employeesRepeater.DataSource = value;
Compared to Listing 1
, the difference here is night and day. If you are not familiar with the passive view pattern, here's a quick description:
- View implementation (web page) implements an interface that is consumed by the presenter.
- The View interface exposes events that are implemented by the View (web page) and subscribed to by the presenter.
- The View raises events in response to events that occur on itself (for example, "Button is clicked," "Load is happening"); these events are in turn handled by the presenter.
- The presenter processes the event accordingly and may push information back to the view using the View interface.
- Using the View interface allows the presenter to remain loosely coupled to any particular UI technology (for example, ASP.NET).
shows the first iteration of the presenter class.
From the looks of Listing 2
all that I have managed to accomplish is to push the messiness that was originally located in the code-behind for the web page and move it into another class. However, with the introduction of the interface for the view, I have introduced a concept that is critical in creating flexible layered architectures: the dependency inversion principle.
|Editor's Note: This article was first published in the May/June 2007 issue of CoDe Magazine, and is reprinted here by permission.