Wednesday, September 16, 2009

On the Layers Pattern

Layers is a common architectural system fit for large systems that structures applications so that they can be decomposed into groups of subtasks such that each group of subtasks is a particular level of abstraction. It is common sense in software architecture that implementing an application following a layered model has more advantages than the monolithic approach. A direct advantage is logical segmentation, facilitating team development, incremental coding and testing.

It is essential that within a layer all components work at the same level of abstraction. The main structural property of the pattern is that the services in layer J are only used by layer J+1 and there are no further dependencies between layers. In other words, each individual layer shields all lower layers from direct access from the higher ones.

Finding the right decomposition is not trivial. Defining the layers can be done in bottom-up fashion, following a ‘yo-yo’ approach or else by refining the structure based on a sequence of steps, which involves defining the abstraction criterion for grouping tasks into layers (usually the distance from the platform – the first layer), determining the number of layers, layer naming and task assignment to each layer and finally specification of services between layers. Strategies involving refining seem natural choices as it is generally impossible to define an abstraction criterion right before defining the layers and their services. Defining the components and services first and later forcing the layer architecture based on usage relationships is also problematic since the pattern does not capture an inherent ordering principle. This means that new components, usually added to the architecture as part of system maintenance, may easily break the strict layering principle.

Once the decomposition is defined, specifying the interface to each layer and the communication between them, structuring the layers, decoupling adjacent layers and last but the not least, designing an error handling strategy are actions that are equally important for designing a good layered architecture. Decoupling layers is usually a nice exercise of design. A top-down, one-way coupling can be achieved by fixing the interface and the semantics of the previous layer. While this allows for top-down communication, for bottom-up communication one may use callbacks. In OOD, one can decouple the lower layer from the upper layer, and even have the upper layers change implementation of lower ones at runtime by means of base classes. This principle is at the basis of Layering Through Inheritance.

I once read about an interesting application of this pattern where the layers are actually used to isolate unrelated concepts that are part of the same application. Having multiple unrelated concepts is common in large systems. The usual tendency to tie these concepts closely together may lead to applications that are hard to implement, change and even understand. Writing a layer that isolates all the concepts from one another solves the issues above with the tradeoff of having a possibly very complex layer, which represents a single point of failure.

No comments:

Post a Comment