Layered systems

Problem

Many large software projects are very complex. How do you increase project organization, support module enhancement and re-use, and reduce direct inter-module interactions?

Context

Consider a large software project that is composed of many modules. Often, as a project grows, the number of inter-module interactions becomes unmanageable, in the sense that a large number of additional modules must be modified if the interface or functionality of a particular module is revised. It can also be difficult to debug a problem using unit tests because of the shared state between modules. Portability of modules will also be very limited.

If the project can be broken down into layers such that each layer only interacts in a limited way (no shared state) with its adjacent layers (and no layer interacts with any non-adjacent layer) using a prescribed protocol (usually function calls), then the architecture of the system can become much simpler and the implementation of each module can become much cleaner. Additionally, each layer can be tested independently, as there is no shared state between the layers. Implementations of layers will also be much more portable as long as the interface definitions between layers are stable.

Forces

  • Increasing abstraction increases code readability and project organization
  • Encapsulating functionality through abstraction can support module enhancement and re-use, as well as illuminate applications of the Virtual

Interface pattern

  • Encapsulating functionality through abstraction can lead to decreased performance
  • Finding clean layers in a software project can be difficult
  • Number of layers should stay low to avoid loss of performance
  • Layers should be opaque for the full benefits of layer isolation and transparent for performance improvements
  • Interface definitions should be system independent for portability but need to be system specific (especially at lower levels) to support hardware specific protocols.
  • Interfaces should avoid alternatives for same or similar functions to isolate system specific features in lower layers. They might provide alternatives to allow system specific performance optimizations in higher layers.

Solution

The lowest layer is usually non-portable and hardware dependent to support the specific features of a system. The upper interface of this layer is already designed as portable as possible to isolate any necessary system specific changes in the lowest layer. Interface definitions will in general not prescribe the details of actual feature implementations to provide flexibility.

Higher layers and their interfaces are kept system independent and the number of layers is kept to a minimum to allow well performing implementations. The abstractions and interface definitions used at the highest layer are driven by the targeted application space as they need to facilitate effective and easy usage.

The number of interface features at the lower levels is often kept to a minimum. This potentially minimizes cost of adaptation to new system layers and also allows a portable straight-forward implementation of upper layers. In performance sensitive situations a potentially large number of only marginally different features may be defined to buy generic Viagra online https://www.topcanadianpharmacy.org/cheap-viagra-questions-will-help-feel-good-going-inexpensive-route/.

State is confined within layers and execution within a layer has to be side-effect free to other layers.

Invariant

Pre-condition

Abstract user interface layer of functionality for a set of desired actions can be defined, which satisfies all user/application needs.
Hierarchical set of abstractions between basic system level actions and application level actions exists.

Invariant

User interface layer functionality and behavior are independent of system choice and lower layer implementation choices.
States of layers are independent of each other.
Executions of layers are independent of each other

Post-condition

No side-effects of layer execution remain.

Examples

Consider a robotic system that senses its environment and controls its actuators. Such a system would be composed of decision-making, actuator control, actuator driver, sensor processing, sensor driver, and low-level communication modules.

This example can be divided into four layers, from innermost to outermost:

  • decision-making modules
  • actuator control and sensor processing modules (control and sensing layer)
  • actuator driver and sensor driver modules (device driver layer)
  • low-level communication modules

This example also illustrates the common interaction of the Layered Architecture pattern with the Virtual Interface pattern. Many of the modules discussed above contain potentially-identical interfaces:

  • many motors have an input that corresponds to desired output torque (actuator control)
  • several sensor processing modules produce lists of moving obstacles using different input sensors
  • all camera driver modules provide images (sensor drivers)
  • many low-level communication protocols are based on an initialize/read/write/close interface

Application of the Virtual Interface pattern simplifies the use of these modules by next-inner-layer modules.

Known Uses

The best-known use of this pattern is in layered communication protocols (for example, HTTP/TCP/IP/Ethernet). It is also commonly used in operating systems, for example in the Linux kernel. Domain specific software libraries are also often structured in layers of increased abstraction and complexity. However, this pattern is also used to manage complexity in large software projects too numerous to list.

Related Patterns

The following common software architecture patterns are related to Layered Architecture:

  • Abstract Datatype
  • Virtual Interface
  • Pipe-and-Filter
  • Libraries for computational patterns

Authors

Todd Templeton
Erich Strohmaier