Today, we'll briefly introduce the basic concepts of Domain-Driven Design and Onion Architecture and highlight some advantages of bringing these two approaches together.
What is Domain-Driven Design?
First, we have to answer the following question: what is domain?
Domain is a sphere of knowledge. It refers to the business knowledge that our software is trying to model. So what is Domain-Driven Design?
DDD is an approach where the main focus is on real business values more than on technologies, frameworks, etc. Concepts and technological details are important, but they are secondary. In order to have a clear understanding of the business and its domain, it's essential to take a collaborative approach involving both technical experts and domain experts, who should both use the same language for effective communication. Another important point is reducing complexity by using object-oriented design and design patterns to avoid reinventing the wheel.
Domain Experts are the people who know the business rules. They may be accounting managers, marketing specialists, cooks, waiters, etc. They may or may not have any knowledge in technology.
Technical Experts are the people responsible for translating the domain experts' knowledge into software. For this communication to be effective, it's important to use a common language between technical experts and domain experts. We call this common language:
Ubiquitous Language, which should be used in all forms of communication, from meetings and documentation all the way to source code, becoming the domain model implemented in the code.
When using ubiquitous language, each term (though similar) should have only one meaning. Unlike human language, where words may mean different things depending on context, software does not handle ambiguity well.
To handle this, DDD requires that each language belongs to one application context. This context is called Bounded Context. It defines a scope where a ubiquitous language can be used freely. Outside it, terms may have other meanings.
Since in DDD the language is the model, and vice versa, each Bounded Context defines the application scope of a specific model. That is, a model is only valid within its own Bounded Context, as shown below:
When we use Domain-Driven Design, we can use different architectures. There is, for example, the traditional three-tier architecture. These layers can be split up into smaller layers. The basic idea is to have the presentation layer at the top, the business/domain layer in the middle, and the data access layer at the bottom.
Onion Architecture is based on the inversion of control principle. It's composed of multiple concentric layers interfacing with each other towards the core. This architecture doesn't depend on the data layer, as in traditional multi-layer architectures, but rather on domain models.
We can find some Domain-Driven Design concepts present in the Onion Architecture domain layer, but it's important to point out that DDD and Onion Architecture are not necessarily the same thing. We can use Onion without using DDD.
The above drawing is simplified. There are other representations of Onion Architecture that feature additional layers, which can be subdivided into smaller layers. But Onion Architecture usually consists of four layers: infrastructure, application services, domain, and the core.
Domain-Driven Design also has a service concept that is slightly different from the concept of an application service. It may cause confusion and force us to always qualify with "I'm referring to the application service," or "I mean the domain service." So it's simpler to name the application service layer API, which also better describes what that layer represents.
The arrow pointing from the infrastructure layer to the core layer represents the fact that each layer can see the underlying layers, but the innermost layers have no visibility or knowledge of the outer layers.
When we use Onion Architecture, we start with the central layer, the core. We keep all domain objects that have business value in the core. We should never include technological concepts like database, REST, or SQL. The core layer, being the central layer, doesn't even know that your domain, API, and infrastructure exist.
The Domain layer is where all the business rules belong. By controlling the domain through the API, and inserting all business logic within the domain, we have a portable application.
We use the API to communicate with the domain. To do so, we must expose only immutable objects, preventing misuse of the API to gain domain access. If we return mutable objects through the API, people using the code could gain access to domain components we might not intend to expose. Although the API has access to the Domain and Core, it doesn't know anything about the Infrastructure.
This is the outermost layer. It includes adapters for databases, UI, external services like RESTful, etc. It has access to all areas of the API, Domain, and Core, though generally most operations that access the Domain must do so through the API. The exception to this would be something like domain interfaces that contain infrastructure implementations.
Joining the Pieces
Domain-Driven Design gives us a more realistic approach to defining what really has business value. With Onion Architecture, we achieve a good level of decoupling and can abstract the technology specifications that are secondary to the business.
Infrastructure abstraction makes it easier to adapt and adopt new technologies that best meet application requirements.
Diego Souza Rodrigues
Diego Rodrigues is a Software Engineer at Avenue Code. He has a soft spot for all kinds of design, creative solutions, and philosophy.