This blog explains techniques and building blocks of Domain-Driven Design which can be used to design a Reactive System.
Domain-Driven Design is an architectural approach that focuses on creating software that solves large and complex problems. The “who can solve this?” and that “what process will they follow?” aspects are addressed later.
Domain-Driven Design gets to the core problem earlier in the design and helps you structure your solution (i.e. identifying Entities, Value Objects, Repositories, Domain/Application/Infrastructure services, Bounded Contexts, Specifications, etc)
- Design an evolving model by placing the primary focus on the domain and domain logic. The subject area to which the user applies a program is the domain of the software.
- One of the key goals of DDD is to create a software implementation based on an evolving model that is easily understood by the domain experts. So DDD provides an effective communication channel between domain experts and software developers.
- To break a large complex system into smaller pieces or subdomains. Building a large complex system in one coherent model can be difficult.
Guidelines of DDD are more compatible with the Reactive Architecture. For example, breaking a large system into sub-domains or smaller pieces helps us to determine boundaries. Reactive Microservices have a similar goal that they need to be separated along clear boundaries. Each microservice has to have a clearly defined API and a specific set of responsibilities. It would be difficult to design and build a microservice if we don’t know it’s responsibilities. DDD provides a set of guidelines and techniques to break larger domains into smaller domains and define clear boundaries. Reactive framework Lagom is built based on DDD. However, DDD and Reactive Architecture can exist independently.
What is Domain?
A Domain is a sphere of knowledge, influence, or activity. In the context of software, it refers to be the business or idea that we are modeling. People who understand the domain are domain experts.
If you are building a retailing software, so your domain will be Retail and the Sales associate, Cashier, Customer Service Representative, Store Manager, Inventory Manager, Buyer, etc will be domain experts. And these people may or may not have expertized in software.
The purpose of DDD is to build a model that the domain experts understand. Here model represents the understanding of the domain which can be implemented as a software system. A domain model can also be implemented as a diagram or a document. The software for a domain model should be implemented in such a way that it reflects the model.
In DDD, communication between software developers and domain experts requires a common language and that is called Ubiquitous Language. Words in the Ubiquitous Language originates in the domain and comes from domain experts and those words used in the domain model and eventually in software. The introduction of software terms into the domain should be avoided. Whenever there is such a need to introduce a word, software developers and domain experts should have a conversation to find if there is any such word already exists in the domain.
For example, if we start using technical terms like databases, event bus, entity, etc our domain experts might get lost. They know words like order, payment, item, invoice, inventory, customer, etc. These are words that somebody who works in a retail store can understand.
Ubiquitous Language enables the communication between software developers and domain experts to have a conversation about the domain model and software system without using software terms.
Decomposing the Domain
Let’s say we have an online book store. Initially, our online book store domain looks very simple but we start modeling, it becomes very complex as there are so many objects involved like orders, customers, payments, books inventory and catalog, debit, credit, delivery, etc. If we try to model this as one domain it would get pretty complex.
So we will use this larger domain and separate this into subdomains. These subdomains are created by grouping related ideas, actions, and rules. Some concepts may exist in multiple subdomains and these sub-concepts may not be identified initially and they may evolve differently.
Our online book store domain can be decomposed into orders, customers, catalog, inventory, payment, delivery, etc subdomains.
Each sub domain has its Ubiquitous Language and model. Language and a model of a sub domain is called Bounded Context. Subdomains or Bounded Contexts are potential candidate for Reactive Microservices.
Meaning of a word may change across multiple Bounded Context. For example, meaning of taking an online order for a book and meaning of an order from inventory perspective are completely different.
Similarly relevant details of an order may change across Bounded Contexts, for example, price and author of the book is relevant when customer orders but that is irrelevant in delivery. Breaking down the system into smaller contexts require continuous integration and coherency.
Domain Building Blocks
In DDD, there are various artifacts to create, express and retrieve domain model within a Bounded Context:
Value objects are immutable and they are defined by its attributes. Two value objects are equal if their attributes are same. Value objects can contain state and business logic. Messages in Reactive Systems are value objects.
An Entity is defined by its unique identity. An entity may change its attributes but not its identity. Entities are mutable object so change in the identity of an entity results in different entity. Entities can contain business logic.
An Aggregate is a collection of domain objects bound to a root entity. The root entity is called the Aggregate Root. Objects in an Aggregate is treated as a single unit and access to objects in the Aggregate must be through Aggregate Root. Aggregate Roots are good candidate for distributions in Reactive Systems and transactions should not span multiple Aggregate Roots.
When an object conceptually does not belong to any Value Object or Entity, in that case, such business logic can be implemented as a Service. Services should be stateless. Services are often used as abstraction for anti-corruption layer (ACL).
Object creation should be done by specialized Factory objects. Factories abstract away object creation logic. Factories handles C (“create”) part of CRUD operation.
Repositories handles RUD (“Read”, “Update”, and “Delete”) part of CRUD operation. Repositories operate as abstraction layer over database, REST Apis, files, etc.