Microservices Powered By Domain-Driven Design
Difficult to model the boundaries of your system’s microservices? If answers to any or many of such questions are yes, then Domain-Driven Design aka DDD is likely to be useful to your Team!
Join the DZone community and get the full member experience.
Join For FreeHave you been finding it difficult to model the boundaries of your system’s microservices? Have you been slowed down by the Technical complexity of your codebase? Has your team been stepping on each other’s toes?
If answers to any or many of such questions are yes, then Domain-Driven Design is likely useful to your Team!
What Is Domain-Driven Design aka DDD
Domain-Driven Design is a language and domain-centric approach to software design for complex problem domains.
It consists of collective wisdom from the Software Industry, a collection of patterns, principles, and practices that will enable teams to focus on what is core to the business's success while crafting software that manages complexity in both the technical and business spaces.
Eric Evans coined the term in his seminal book “Domain-Driven Design: Tackling Complexity in the Heart of Software” written in 2003 and was well ahead of its time!
It started becoming very relevant with microservices architecture era. It was relevant in 2003 for designing modular monolith and today as well! Modular monolith is a topic for my other blog!
Tactical and Strategic Patterns of DDD
We will use the example of a retail e-commerce domain to explain the following concepts. Everything in this Domain revolves around the “Product” being sold.
If you want to see some code related to this, please do check out:
http://github.com/sandeepjagtap/ddd-workshop
Let’s talk about some strategic patterns like bounded context, ubiquitous language, and context map.Bounded Context:
Building just one domain model for entire e-commerce will be tough to comprehend and implement in the code. Bounded context helps split the e-commerce domain into smaller subdomains: E.g. Inventory, Shopping Cart, Product Catalog, Fulfilment & Shipment, and Payment. We can use technics like event-storming to identify such subdomains and bounded contexts. So we now have Inventory bounded context, Product Catalog bounded Context, and so on…
It is important to note that the Product in each bounded context has very different behavior. In Inventory, Context Product is concerned about weight, expiry date, and supplier, whereas in Shopping Cart bounded context, the expiry, the Supplier of the Product, is not in the picture. So it is better to model different Product classes in each bounded context instead of having a common Product class across the bounded context.
So bounded context is a linguistic boundary! Any time you see that the Product is acting differently, it is a clue that there are different bounded contexts in the play.
One bounded context typically has few (or one) micro-services
Ubiquitous Language:
The ubiquitous language applies within a bounded context. Each bounded context has its own ubiquitous language. It is a language that is spoken both by the Business Teams and the Development teams. It helps them to communicate better.
If your Business team is talking in terms of Database tables, then as the Development Team, you have influenced them incorrectly.
Business and development teams should communicate with each other using DDD tactical patterns like Domain Event, Domain Service, Entity, Value Object. More on that later (in this blog).
Context Map:
Context Map defines the relationship between different bounded contexts.
How different bounded contexts communicate with each other and how they influence each other.
Now, let’s talk about some tactical patterns like Domain Event, Entity, Value object, Domain service, and Aggregate.
Domain Event:
Domain events indicate something important that has happened in the system from the perspective of the Business Team. Let’s take the example of Cart Entity in the Shopping cart bounded context. Events like ProductAddedToCart, ProductRemovedFromCart, CartCheckedOut are important to business teams.
Domain Events can not be updated or deleted once they happen. They can be used as a communication mechanism between different bounded contexts. So CartCheckoutEvent, when generated by Cart, can be used by Payment bounded context in e-commerce to initiate a payment from User.
Domain Event helps with building loosely coupled, scalable systems.
They are also the basis for designing Event Sourced systems.
Entity:
In Shopping Cart bounded context, Cart is an Entity. The cart has different behaviors such as add a product, remove the product, checkout, etc. The cart also has Identity and life cycle associated with it. You can identify one cart from another cart!
Many objects are not fundamentally defined by their attributes but rather by the thread of continuity and Identity. The fundamental concept of Entity is continuity threading through the life cycle and even passing through multiple forms.
Object defined primarily by its identity is called an Entity.
E.g., A person, city, car, lottery ticket, or a bank transaction.
Example of seat booking in stadium, Seat, and Attendee as Entities. If it is general admission, then Seat need not be an Entity but a Value object.
Value Object:
Price of Product in Cart is a Value Object.
Many objects have no conceptual identity. These objects describe the characteristic of the thing. E.g., two markers of the same color and the same shape. If one is lost, a drawing can continue by replacing it with the other marker.
Value objects describe the things. They are elements of design that we care about only for what they are and not who or which they are.
Code Value objects as Immutable.
Aggregate:
Aggregate is a logical concept. At the root of aggregate, there is an Entity!
In the Shopping cart bounded context, Cart is Aggregate, and at the root of it, there is the Cart Entity.
Aggregate is a cluster of associated objects that we treat as a unit for data changes. The aggregate root is at the top and is the only entity through which Aggregate can be accessed and has the global identifier.
Other Objects inside aggregate can have local identifiers and are NOT accessible outside aggregate directly. Aggregate root controls access to them from the outside world.
One microservice typically has one Aggregate
Invariants/consistency rules/business rules applied within an Aggregate will be enforced with each transaction's completion, like adding product to the cart, removing the product from the cart, etc. Example — Cart total price should match the sum of all the product prices in the cart.
Within an aggregate Strong Consistency (Semantics of ACID properties of transaction) apply.
Domain Service:
Sometimes it is not just a thing or a noun. You can model verbs or business processes as Domain Service, too.
ProductPricer, which uses different pricing algorithms by taking other inputs in e-commerce, can be modeled as Domain Service.
If you want to see some code related to this, please do check out:
https://github.com/sandeepjagtap/ddd-workshop
References:
Book: Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans
https://www.dddcommunity.org/book/evans_2003/
Image Credits - Photo of Front Cover of Domain-Driven Design Book by Eric Evans from amazon website.
Opinions expressed by DZone contributors are their own.
Comments