Software applications are typically composed of a User Interface (UI), code to process input from a user or another system and a database that acts as the persistence mechanism. Python applications could incorporate the UI, the business logic and the data access into a single file. For very simple applications this might make sense. However, as the complexity of the domain rises there is a need to divide code intro separate files within many directories and create separate code packages. The aim of this high level code organization is separation of concerns.
The term architecture was adopted from the science of creating buildings and other physical structures [Perry1992]. Buschmann et al presented eight architectural patterns to express structural organization schemas in software systems [Buschmann1996]. One of them, the Layers Architecture enjoyed great popularity in enterprise, desktop and web applications. The Layers Architecture focuses in decomposing the application functionality into vertically stacked layers in which each layer specializes in a particular group of responsibilities [Buschmann1996]. In its strict form layers are coupled only to the layers directly below and in its relaxed form any higher level layer is permitted to couple to any lower level layer [Vernon2013].
In the context of Domain-Driven Design, Eric Evans [Evans2003] proposed the use of four layers as shown in Figure 1:
- The User Interface Layer (or Presentation Layer) is at the top and is responsible for processing interactions with a user (or another computer system) and depends on all layers below.
- The Application Layer orchestrates the collaboration between domain objects based on information passed from the User Interface. It is a thin layer without business logic.
- The Domain Layer captures and isolates the business logic i.e. the Domain Model. It is the most important part of the software and should be user interface agnostic and without any presentation logic. Domain object persistence is delegated to the infrastructure in an indirect way.
- The Infrastructure Layer is at the bottom and all upper layers depend on it. It takes care of persisting domain objects, provides a messaging mechanism, renders graphical widgets to the user interface and generally dealing with anything that is considered a technical detail.
A serious drawback of the Layers Architecture is the vertical stacking of dependencies and especially the coupling of all higher layers to the infrastructure [Vernon2013]. In order to isolate and decouple elements of a system, Uncle Bob [Martin2003] recommends the Dependency Inversion Principle:
- "High level modules should not depend upon low level modules. Both should depend upon abstractions".
- "Abstractions should not depend upon details. Details should depend upon abstractions".
The preceding quote contains a number of important insights. Vernon applied the dependency inversion principle to demonstrate its effect in the Layers Architecture (Figure 2) [Vernon2013]. Of particular interest is that the order of layers is reversed and surprisingly the Domain Layer is placed at the bottom. In this layout, the Infrastructure Layer is a client of all other layers. The User Interface is a direct client of the Application Layer which plays the role of the mediator between the User Interface and the Domain Layer. It is important to note that the Application Layer depends on abstractions defined in the Domain Layer which are implemented in the Infrastructure Layer [Vernon2013]. The Dependency Inversion Principle and the use of abstractions are going to be demonstrated in a future post.
The influence of Dependency Inversion Principle in Layers Architecture has prompted new ways of looking at an application's architecture. The Hexagonal Architecture was originally suggested by Cockburn [Cockburn2005] with the intention to control applications by users and/or other computer systems while at the same time allowing the development and testing of an application independent of any technical infrastructure like fax machines, printers, databases, etc. It was subsequently discussed by Freeman and Pryce [Freeman2010] in the context of developing an online auction system using a test-driven approach. Recently, Vernon promoted the use of Hexagonal Architecture in Domain-Driven Design [Vernon2013].
As shown in Figure 3, the business logic is placed at the centre of the architecture that is the Domain Model which is expressed in terms of the Ubiquitous Language [Evans2003]. The inner hexagon represents the Use Case boundary in which the Application Programming Interface (API) is defined, containing no business logic [Cockburn2005] [Vernon2013]. The Application Services access the Domain Model directly and are responsible for cross-cutting concerns like authorization and transaction management [Vernon2013]. In order to communicate with the outside an Application defines ports and adapters are created to interface the technical infrastructure with the application [Cockburn2005] [Freeman2010].
The term “Ports and Adapters” was also coined by Cockburn (2005) as a synonym to Hexagonal. However, port and adapters are loosely defined which permits many interpretations. For instance, a web based user interface could be seen as an adapter that translates user input to the internal API. In this case the HTTP protocol could be considered as the “port” [Vernon2013]. A further example of a port is an interface (abstraction) defined in the Domain Model that is required to access external services provided by a third-party application. In this case the adapter is the concrete implementation of the interface that incorporates the technical details of e.g.which permits many interpretations. For instance, a web based user interface could be seen as an adapter that translates user input to the internal API. In this case the HTTP protocol could be considered as the “port” [Vernon2013]. A further example of a port is an interface (abstraction) defined in the Domain Model that is required to access external services provided by a third-party application. In this case the adapter is the concrete implementation of the interface that incorporates the technical details of e.g. creating SOAP requests and translating the SOAP responses to entities compatible with the Domain Model [Cockburn2005] [Vernon2013]. The last example is referred to by Evans [Evans2003] as the Anti-Corruption Layer which will be discussed in a future post.
Finally, it is worth noting that the application of Domain-Driven Design does not mandate any particular architectural style [Evans2003] [Vernon2013]. No matter which architecture is chosen it is a prerequisite that the Domain Model is isolated and placed at the heart of software, thus focusing in designing domain objects by neglecting user interface interactions and persistence infrastructure details [Evans2003].
The above is an abstract of my MSc Thesis titled "Applying Domain-Driven Design in Python - designing a self-hosted Read-it-Later service" (January 2014).
|[Buschmann1996]||(1, 2) Buschmann, F., Meunier, R., Rohnert, H., Sommerlad, P. & Stal, M. (1996) Pattern-oriented software architecture, Volume 1: A System of Patterns. New York: Wiley.|
|[Cockburn2005]||(1, 2, 3, 4, 5) Cockburn, A. (2005) Hexagonal Architecture: Ports and Adapters (“Object Structural”). [Online] Available at: http://alistair.cockburn.us/Hexagonal+architecture [Accessed 4th September 2013].|
|[Evans2003]||(1, 2, 3, 4, 5, 6) Evans, E. (2003) Domain-Driven Design: Tacking Complexity In the Heart of Software. Boston: Addison-Wesley.|
|[Freeman2010]||(1, 2) Freeman, S. & Pryce, N. (2010). Growing Object-Oriented Software, Guided by Tests. Boston: Addison Wesley.|
|[Martin2003]||Martin, R. (2003) Agile software development: Principles, Patterns, and Practices. Boston: Prentice Hall.|
|[Perry1992]||Perry, D., Wolf, A. (1992) 'Foundations for the study of software architecture'. ACM SIGSOFT Software Engineering Notes. 17(4) pp. 40-52.|
|[Vernon2013]||(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) Vernon, V. (2013) Implementing Domain-Driven Design. Boston: Addison-Wesley.|