Discovering the Domain Architecture
- 9/10/2014
- The real added value of domain-driven design
- The ubiquitous language
- Bounded contexts
- The layered architecture
- Summary
- Finishing with a smile
The layered architecture
In the rest of this chapter, we’ll provide an overview of the layered architecture—the multilayer architecture introduced in Evans’ book about DDD. The layered architecture is probably the most common type of architecture that results from DDD analysis.
Origins of the layered architecture
In the 1990s, most computing scenarios consisted of one insanely powerful server (at least for the time it was) and a few far slower personal computers. Software architecture was essentially client/server: the client focused on data presentation and commands, and the server mostly implemented persistence. Any business logic beyond basic CRUD (Create, Read, Update, Delete) was stuffed in stored procedures to further leverage the capacity of the insanely powerful server machine.
Over the years, we all managed to take larger and larger chunks of the business logic out of stored procedures and place them within new components. This originated the classic (up to) three-segment model, which is shown in Figure 5-7. Note that the figure also shows a direct connection between the presentation and data layers in memory of SQL data binding.
FIGURE 5-7 The classic three-segment architecture.
Note that we’re using the term segment here as a general way to interchangeably refer to both tiers and layers.
From what we have seen, learned, and done ourselves, we’d say that the largest share of systems inspired by the three-segment architecture is actually implemented as a layered system deployed on two physical tiers. For a website, for example, one tier is the ASP.NET application running within the Internet Information Services (IIS) process space and another was the Microsoft SQL Server service providing data. (See Figure 5-8.)
FIGURE 5-8 Common deployment of a multilayer ASP.NET application.
For the most part, the data model of any three-segment architecture is the relational data model of the data store. The growing complexity of applications has led developers to a more conceptual view of that data. As patterns like the Domain Model and approaches like Domain-Driven Design (DDD) were developed and exercised, the internal structure of a layered architecture evolved quite a bit. (See Figure 5-9.)
FIGURE 5-9 A more modern version of a layered architecture.
Roughly speaking, the presentation layer is the same in both architectures and the infrastructure layer includes the data layer of Figure 5-7 but is not limited to that. The infrastructure layer, in general, includes anything related to any concrete technologies: data access via O/RM tools, implementation of IoC containers, and the implementation of many other cross-cutting concerns such as security, logging, caching, and more.
The business layer exploded into the application and domain layer. Upon a more thoughtful look, the layered architecture of Figure 5-9 results from a better application of the separation of concerns (SoC) principle. In systems inspired by the schema of Figure 5-7, the actual business logic is sprinkled everywhere, mostly in the business logic but also in the presentation and data layers.
The layered architecture of Figure 5-9 attempts to clear up such gray areas.
Presentation layer
The presentation layer is responsible for providing some user interface (UI) to accomplish any tasks. Presentation is a collection of screens; each screen is populated by a set of data and any action that starts from the screen forwards another well-defined set of data.
Generally speaking, we’ll refer to any data that populates the presentation layer as the view model. We’ll refer to any data that goes out of the screen triggering a back-end action as the input model. Although a logical difference exists between the two models, most of the time the view model and input model coincide. (See Figure 5-10.)
FIGURE 5-10 Describing the data that goes into and out of presentation screens.
Application layer
As we see it, the application layer is an excellent way to separate interfacing layers such as presentation and domain. In doing so, the application layer contributes immensely to the clarity of the entire design. In the past, a typical gray area of many architectures was the placement of the part of the business code that needed to be aware of the presentation.
The application layer is the additional layer that reports to the presentation and orchestrates any further business action. The application layer is where you orchestrate the implementation of use-cases.
Entry point in the system’s back end
Each interactive element of the user interface (for example, buttons) triggers an action in the back end of the system. In some simple scenarios, the action that follows some user’s clicking takes just one step to conclude. More realistically, instead, the user’s clicking triggers something like a workflow.
According to Figure 5-9, the application layer is the entry point in the back end of the system and the point of contact between the presentation and back end. The application layer consists of methods bound in an almost one-to-one fashion to the use-cases of the presentation layer. Methods can be grouped in any way that makes sense to you.
For example, in an ASP.NET MVC application, we expect the application layer classes to go hand in hand with controllers. The HomeController class, therefore, will have injected some HomeControllerService worker class. Here’s a quick sample:
public class HomeController { private readonly IHomeControllerService _service; public HomeController(IHomeControllerService service) { _service = service; } public ActionResult Index() { var model = _service.FillHomePage( /* input model */ ); return View(model); } ... }
The mechanism of injection can happen at your leisure. It can happen via Unity or any other Inversion of Control (IoC) container, or it can be done through poor man’s dependency injection, as shown here:
public class HomeController { private readonly IHomeControllerService _service; public HomeController() : this(new HomeControllerService()) { } public HomeController(IHomeControllerService service) { _service = service; } }
In a nutshell, the application layer is responsible for the implementation of the application’s use-cases. All it does is orchestrate tasks and delegate work to other layers down the stack.
We think there can be two flavors of an application layer: inside or outside the business logic. Neither is preferable to the other; it’s all about how you envision the system.
Orchestrating the business logic
In general, the application layer is bound one-to-one to the presentation with the notable exception of unattended systems. The structure of the layer is driven by the actionable controls in the various user interface screens. With reference to Figure 5-5, this flavor of application layer lives in the club-site context and orchestrates workflows involving components in the Core Domain and external services. Key aspects of this application layer are these:
- It might or might not be consumable by different front ends because, for example, a mobile front end might have slightly different use-cases than the web front end or ends.
- It can be stateful at least as far the progress of a UI task is concerned.
- It gets its input from the presentation and sends a view model back as shown in Figure 5-10.
The application layer holds references to the domain layer and infrastructure layer. Finally, the application layer has no knowledge of business rules and doesn’t hold any business-related state information.
As the name suggests, the application layer is just application specific. If our aforementioned booking system must be consumed by a website and a mobile application, we’re going to have two distinct application layers—one on the site and one either within the mobile application or exposed as an HTTP service. Are these layers actually different?
Well, they might be different; and if they’re different, different layers should be implemented. To continue with the booking example, the application layer of the website might redirect to the credit card site to pay and then proceed with the persistence of the booking data. The application layer of the mobile app might use in-app payment features or just use stored payment information and pass them along in some way to the domain layer.
Domain layer
The domain layer hosts the entire business logic that is not specific to one or more use-cases. In other words, the domain layer contains all business logic that remains once you have compiled the application layer.
The domain layer consists of a model (known as the domain model) and possibly a family of services. The nature of the model can vary. Most of the time, it is an entity-relationship model, but it can be made of functions too. Let’s stick to what appears to be the most common scenario. So let’s say that at the end of the day an entity model is an object model.
However, in an entity model, constituent classes usually follow certain conventions. We’ll return in detail to domain modeling and DDD conventions for entities in upcoming chapters. For now, it suffices to say that entities in the model are expected to expose both data and behavior. A model with entities devoid of any significant behavior—that is, merely data structures—form an anemic domain model.
The ultimate goal of a domain model is to implement the ubiquitous language and express the actions that business processes require. In this regard, exposing some behavior tends to be more relevant than holding some data.
Along with an entity model, the domain model layer features domain services.
Domain services are pieces of domain logic that, for some reason, don’t fit into any of the existing entities. A domain service is a class, and it groups logically related behaviors that typically operate on multiple domain entities. A domain service often also requires access to the infrastructure layer for read/write operations. In the aforementioned club-site context, a domain service can be the code to book a court:
void BookCourt(Court court, ClubMember member)
Court and ClubMember are domain entities, and the method BookCourt knows how to retrieve and apply due policies.
Infrastructure layer
The infrastructure layer is anything related to using concrete technologies, whether it is data persistence (O/RM frameworks like Entity Framework), specific security API, logging, tracing, IoC containers, caching, and more.
The most prominent component of the infrastructure layer is the persistence layer—which is nothing more than the old-faithful data access layer, only possibly extended to cover a few data sources other than plain relational data stores. The persistence layer knows how to read and/or save data.
The data can reside on a relational server as well as in a NoSQL data store or in both. The data can be accessible through web services (for example, CRM or proprietary services) or live in the file system, cloud, or in-memory databases such as Memcached, ScaleOut, or NCache.