Default Architecture: Layers

time to read 5 min | 801 words

imageThe question came up in the ALT.Net group, and it seems like a good idea to post my thought about it.

You can see my overall default design for most just about any system. No, this isn't the end all be all design dogma, it is just something that has served me well in a wide number of applications.

This architecture has only one thing at its code, it is focused on clearly defining responsibilities between different parts of the application.

On the face of it, this looks like the traditional Model View Controller approach, but while there is some relation (specifically, the idea of controllers is shared), there aren't a lot of similarities between how I applied this in many cases.

Let us get to the specific, and see what we have.

External Interface

The external interface is the entry point to the system. It can be the UI, a service call, a message handler, a scheduled task or anything that is executing as a result of external input. The external interface is where you establish your current context. In general, it is strongly recommended to put as much as possible into the external interface context rather than the global context. This allows you more control overall.

Note that we explicitly recognize that we may have more than a single external interface into our application. Each of those external interfaces can be simple (Service Call) or complex (non trivial User Interface), but what is important is that it is mainly dealing with the issues of the external interface, not with the actual application. That is the responsibility of the controllers.

Controllers

The controllers (which is a generic term, in the past I have used commands, handlers and tasks to name the part of the application that perform this duty) are responsible of taking a particular use case and making it happen. This is the place where you start the unit of work and the transaction, this is the scope of what you are doing.

However, the controller is only dealing with the Core API to invoke domain behaviors. It is responsible for aggregating those behavior into the particular use case, but that about it. It is in the core that you actually perform the business behavior.

Core

The core is where your real business logic lives. This is where you define your domain model, define the operations and services that deal with it, etc. I'll not touch on how to structure the core here, there is enough material elsewhere for this. I'll say that the core exposes the complete set of operations that can be perform by the application.

In fact, one major rule to observe in this scenario is that the controllers layer should not add any higher level operations on top of what the core already has. Its responsibility is merely to coordinate.

Infrastructure

The infrastructure layer contains... well, the infrastructure. The OR/M, the container, the works. Note that unlike traditional layering, we don't have just the core touch the infrastructure, rather, we have the infrastructure used by all parts of the application. And no, the infrastructure is not merely a set of of utility methods and classes.

What do we gain by using this approach?

First, we have a clearer separation of concerns, which is always good. But more to the point, we avoid the case where the external interface is taking over the entire design of all the lower layers. I distinctly remember seeing 3 tiers applications where it was abundantly clear that the design process was primarily focused on the UI functionality. To the point where a functionality change in the application was much harder to deal with, because everything was so focused on the UI.

This approach allow us to offer a rich set of operation on the domain, allow us to contain the logic for use cases in a well defined locations and reuse them as needed, in matching use cases across different external interfaces. It also tend to make new development far easier in the long run.

A note about controllers, however. While they can and should be shared if you are using the same use case in several external interfaces, note that this should not bleed into the controller itself. If it does, it might be a good idea to create two controllers, and declare this as two separate use cases, because the requirements of the external interfaces are different enough even though the underlying business scenario is the same.