DDD is a software design method that allows the alignment of the solution on the business. Specifically, this is a toolbox because none of the design patterns associated with DDD actually belongs to DDD. This is not a new way of working that should overburden other work methods over and over again… The approach simply presents a set of guidelines that allow developers to get right to the basics, to understand the needs, and to get closer to the experts of the domain (the interaction is valuable in both ways). It is really just common sense. However, in a constant and perpetual context of technical emulation and innovation, this common sense is sometimes very disruptive.
Domain Driven Development…
Literally, DDD stands for “Domain-Driven-Design”. So, the first question you should ask, and maybe the most structuring one is : what is a domain ? A domain is a sphere of business knowledge, indivisible, embedding a more or less important set of processes, rules, which do not depend on another domain. In fact, a domain is very often embodied by one or several business teams.
Following DDD, databases do not really exist (neither do they exist in the Bounded Context, which is the incarnation of one part of the domain), nor are technical services (be careful not to confuse technical services with transverse services). This is a first particularly disruptive point, so we, developers, have become accustomed to model databases in the very early stages of the developments, in order to accelerate our developments. Or, so do we think… Good hygiene in DDD consists in banning certain technical terms of the meetings with business people : databases, services, APIs… This is technical jargon and this jargon finds no legitimacy in the discussions with the operational staff. Once this discipline is adopted, operational people and developers can finally speak the same language, and understand each other. This is the first design pattern of the DDD, and it is not technical ! This prerequisite to DDD is the adoption of the Ubiquitous Language.
The domain only exists in the space of the problem. This is what we want to model, this is what we want to solve. The domain does not exist in the solution space. In the space of the solution, we only find Bounded Contexts (BCs). The BC is the highest level of all the DDD abstraction. After UL, everything starts with it. It is rare to have one BC per domain. In fact, a domain may be broken down into one or more BCs. The main challenge of DDD is to limit all functional coupling that may bind these BCs.
DDD and SOA
Unlike SOA or micro-services approaches, the idea of remoting does not exist in DDD. Remoting means the remote execution of processes, or parts of processes, that imperatively need to be acknowledged. This guideline sometimes hurts itself with reality, and the fact that it is now very simple (unfortunately) to create APIs to carry out processes, or manage data. Unfortunately, because there is a perverse effect. These new frameworks indirectly encourage the distribution of coherent sets of functionalities, which are naturally not intended to be distributed.
In order to minimize the number of application layers implemented in a solution, DDD promotes onion architectures. Usually, solutions are encoded with a data access layer, a business layer, and a presentation layer and… layers with intermediate abstractions ! In this architecture, the presentation layer references the business layer, as well as its abstraction, which itself refers to the data access layer, and its abstraction. In an onion architecture, the business layer does not reference any other layers. The business is sanctuarized to remain independent from the questions of persistence and presentation. This makes possible to address the issue of persistence last, and this greatly facilitates the writing of functional tests. Since no database is needed in the early stages, it is possible to achieve functional testing very early in the developments. Thus, domain objects are only concerned with the business, and some “tolerant” patterns even advocate the UI aspect by reflection, or introspection of the business code (for example, by generating code, such as RestFullObjects, Noodles…)
DDD arrives with its design patterns. These patterns are sometimes architectural patterns, sometimes development patterns. And sometimes, DDD is adopted at a more strategic level. The most common ones (those that are the most often mentioned in literature) are :
- Ubiquitous Language (UL) : this is not a technical pattern. However, this pattern is more intrusive than it seems, because it is mandatory to find this language in the code. In DDD, this is contractual. Technically, this is more of a clean-code pattern, popularized by Robert C. Martin, who says that the different artifacts of the solution must be named in a way that reveals their intentions.
- Bounded Context (BC) : this is a part of the solution. This part has to be as sealed as possible. A domain is applicatively supported by one or more BCs. In these BCs, the notion of remoting does not exist. Each context is independent of others, it embeds its data and its behaviors.
- Context Map (CM) : this is a very simple scheme that models the different BCs of the solution on a large scale, and the type of information that they have to exchange. This scheme belongs to the business, not to IT.
- Domain Model : model to understand… each BC contains one or more models. In the nominal case, there is only one model. This model groups the entities, services, etc… of the context. This is all that can be represented by zooming inside the blocks of the previous CM. The model is meant to be precise, but it is often inaccurate. In absolute terms, this is not a problem. Indeed, creating a model is a delicate operation, which can be very time consuming, if one does not know how to be a little pragmatic. A BC can contain 2 models (more may become twisted engineering…). This is the case for CQRS architectures for example, which have 1 model for reading, and 1 model for writing (transactional). This style of architecture is legitimate for applications with divergent scalability and performance constraints, as is often the case, in reading and writing. Let’s say, for example, that an application issues a notification. The CQRS application (subscribing to this notification) integrates this information in a transactional way, after validation, etc… via the writing model. At the end of the update, the read model is refreshed, very simply, like an SQL view… but unlike the SQL view, there is only a simplified aggregation in the read model (but exhaustive) of the writing model : without validation, without expensive treatments, without heavy demands… just raw data or information ! This aggregation is for third-party client applications. In a DDD approach, the CQRS is the architecture that comes closest to remoting, or, in a broad sense, closest to SOA.
- Aggregate : these are the limits of the different transactions that must take place in the context. Let me explain : in a model, certain groups of entities must evolve in a transactional and consistent manner. This group of entities defines a scope : the aggregate. Within this aggregate, all state changes must pass through the so-called root of the aggregate (AR). This object masks the underlying entities and services, and prohibits wild and unlegitimate changes (from a business point of view). It is the boss of the aggregate, all requests are addressed to it. The root is responsible for validation and consistency of the aggregate. Each of its members is inaccessible, if not by the root. The aggregate must maintain a maximum degree of consistency. Only the root is referencable, referenced, and visible beyond the limits of the BC. It is impossible to distribute an aggregate. Everything must evolve on the same server. Within its borders, all operations are synchronous, to enforce invariants, these business rules which must apply whatever the state of the system. Apart, everything can be asynchronous (in reality, it is to challenge with the domain experts…).
- Value Object (VO) : analyzing the lifecycle of the entities of the model allows to define the aggregates. But DDD also attaches great importance to read-only artefacts, as a good means of validation. In this approach, the VO acts as a mini-aggregate. The VO transforms dummy and mute data (strings, or integers, for example…) into business objects, with a precise meaning. The object is immutable. To modify this object, it is necessary to instantiate it again and force to pass through the constructor. In this case, the root of this mini-aggregate is its constructor (even if it is a characterized abuse of language, please forgive me…). It is the constructor that validates the object. If its state is considered inconsistent, the object is not instantiated. The entities are equal if their identities are equal. The VOs are equal if their attributes are equal. The VO is therefore used to give certain variables a business sense and to encapsulate their validation rules.
- Domain Event (DE) : the event (posted on a service bus for example) is not used to convey data. It is stateless, it carries business information. This information is expressed in the past participle and its meaning must be totally intelligible by the business people (and it even is their responsibility to define it !). It has to express concrete facts. Transporting raw data or reports via notifications is possible, but not in the case of DDD. For example, for BI purposes…
Differences with SOA
Previously, I talked about SOA. SOA recommends reusability of the functional capabilities (through the code base). DDD recommends the specialization of the functional capabilities (and the code base), but it does not promote reuse. So forget about making a piece of code more generic than it needs to be, in order to be able to expose a feature in the form of an API. You will create functional coupling and make the code more opaque than ever. If an entity has 2 different meanings in 2 different domains, or even 2 differents names, or if it is used differently, then that entity must evolve into different BCs in 2 different forms. This rule applies to aggregates too. Additionally, if 2 models share elements that are difficult to duplicate, or if a BC is straddling several domains, it will be necessary to investigate the side of the “Shared Kernel”, another pattern of DDD…
Shortly now, what does emerge from these last lines ? Firstly : a reinforced collaboration with the business. The business arbitrates a number of decisions that are usually within the scope of IT : synchronism, asynchronism, nature and content of notifications, etc… this is only possible when IT and operational people (or domain experts) speak the same language. Secondly : the respect of one of the key OOP patterns, and more precisely, encapsulation. Application artifacts aligned with business needs are never exposed or unveiled without an excellent reason. Divide and rule ? No ! DDD never divides what does not need to be, because it creates functional coupling. Everything is encapsulated for a better control.