Domain-Driven Design and Microservices

Nitesh Malviya
The Startup
Published in
10 min readSep 12, 2020

Domain-Driven Design (DDD) concept was introduced by first Eric Evans in 2003. The concept of microservices did not exist at that time. So basically DDD was introduced to solve the problem of a large monolithic code base. In the monolithic world, once the codebase starts growing with the growth of the business, it becomes difficult to maintain the code organized and structured as it was originally designed. Monolithic applications designed using MVC architecture have good separation between the business layer and the presentation layer. But in the absence of the strict architectural guidelines, the business layer does not provide specific rules to maintain responsibility boundaries between different modules and classes. That’s why as the code base grows it increases the risk of logic breakdown, responsibility leakage between the different components of the application.

DDD attempts to solve the above challenges by keeping your application close to the REAL-WORLD system or more precisely to the concerned business. In DDD, application logic revolves around the business problems with defined boundary contexts. DDD focus on domain modeling. Let’s discuss the key characteristic of DDD

  1. Collaborative: DDD is collaborative. Business entities, stakeholders, and developers work together to solve a specific business problem. It is more like an AGILE methodology where collaboration is the key.
  2. Data Modeling: Data Modeling defines that the structure of your code should map to the structure of the domain. Here, the domain is not referred to as a business, this can be referred to as the part of the business. The domain can be accounting, storefront, warehouse, etc. The idea behind keeping the code model mapped to the domain is to keep the high-level structure of the application understandable to business users. So when a business expands and one of the domains of the business needs some enhancements in the application, you can change models and logics associated with that domain and keep the other part of the application intact.
  3. Incremental: DDD is incremental so you don’t need the entire architecture of the business to be designed upfront. You just need to solve the current problem and then evolve the code as the domain or business grows. Again DDD aligns with AGILE methodology with this characteristic of an incremental approach. An incremental approach is a process or releasing new features quickly and incrementally.

What are Microservices?

In simple words, microservices is about solving a specific problem. In Monolith design, we have a huge code base that is data-driven and tightly coupled. So whenever we need to change the data model the entire monolith design comes into the stake. Even the incremental approach is too cumbersome with a monolith as the entire application has to be tested with all circumstances and scenarios along with different dependency checks to make sure the update is not causing any failures. Microservice architecture solves this problem of monolithic design and provides the freedom to grow the application in any direction.

How DDD is related to Microservices?

Microservices is possibly the ideal way to implement the DDD because in Microservices we also talk about context and incremental approach which are the main characteristic of DDD too. Both DDD and microservices are modeled around the business concept. Some basic characteristics of microservices are following

1. Small: Microservice should be small enough to solve an individual problem. It can be as small as a class in OOPS terminology or a function in Functional design terminology.

2. Independently deployable: A Microservice is independently deployable. So basically you do not need to update the entire application like a monolith for adding a new feature. You just change the related microservice and check its context boundaries and you are ready to deploy.

3. Decoupling: Individual microservice should be decoupled, isolated, and hide its implementation details from the world. Microservices should be accessible via services only to provide decoupling.

Key Building Blocks of DDD

1. Domain: It’s a logical area which defines the different business entities. For example, a shipping business may have an inventory domain, accounting domain, tracking domain, etc.

2. Bounded Context: It’s a logical boundary in the code which solves the specific problem of the domain. It can be a single class or a group of files which are coded to resolve a specific problem. The different bounded context is completely isolated from each other and does not know about each other existence. The easiest way to define context is to define the “Responsibilities” of the domain. For example, an e-commerce company can have two domains named “Warehouse” and “Store”. “Warehouse” has the responsibility of “Shipping the product” whereas “Store” has the responsibility of “Sales” so these can be treated as a bounded context.

3. Services: Service is the communication channel between different Bounded Contexts. You need services to keep different bounded contexts decoupled but still communicable.

4. Value Objects: A value object has no distinct identity and it is immutable. It is defined by its properties. For example Name, Address, etc are value objects.

5. Entities: Entities are domain objects. Entities do only specific things within the bounded context. These are identified by a unique identifier. Some examples of entities can be User, Order, Job, Message, etc. An entity is mutable and it can change its identity. For example, an order might change its statues from ordered to shipped and it has a unique identifier “order_id” to be identified.

6. Ubiquitous Language: This denotes the language-specific within the context. For example, if you talk about an object “book” in the “store” context then you are more concerned about properties like price, authors, etc which are related to “sell the product” whereas an object “book” in the “warehouse” context should be more related to properties like weight, size, etc which are related to “ship the product”. So clearly the same object might have different languages of conversation in different contexts. Ubiquitous Language is not just dependent upon the NOUN level or properties level as defined above, this is related to the VERB level too or in easy words, what you do with those objects. For example, the “book” object should be used to “sell or organize” the books in the “store” context whereas it should be used to “box and ship” the books in the “warehouse” context. Overall, ubiquitous language defines the language within the context and when someone talks about “book” in the “warehouse” context then it will be always about properties like weight and size and no one should talk about the name of the book at all in that context.

How DDD can be implemented?

DDD approach can be used to both analyze the domain as well as develop the code and this process is called “Event Storming”. We talk about domain and identify events that might happen at the domain level during “Event Storming”. Remember that its a collaborative and incremental, so you have to involve both businesses as well as developers to define the events and model around it. Let’s take an E-Commerce business as an example to determine the DDD approach. Please note that this is not a complete example. I am using this just as a reference. While adopting the DDD approach we should focus on three W’s and those are “WHEN”, “WHAT” and “WHO”.

You should start your DDD approach by first defining the domains of the business along with their responsibilities. An E-Commerce company might have different domains like “warehouse”, “store” etc. The next step is to define the responsibilities of those domains. For example, the “warehouse” has the responsibility to ship the books whereas the “store” domain has the responsibility to sell the books. Both domains have their bounded context and certainly need not interfere in each other work or know each other working style. So this is where we can decouple the context and keep individual context and its implementation hidden from the outside world.

Now we need to identify the events related to domains. In “Warehouse” and “Store” context that can be “Order placed”, “Shipping label generated”, “Order shipped” etc. These events can be either manually driven or automatic. In DDD, it’s an important concept that we do not care about the actor or individual, we care about the ROLE and its associated events. For example if a user login and create an invoice then we don’t care about that individual user, we care about the user’s role i.e. “accounts” and the event “create an invoice” which the user performs. Basically while deriving events we are defining the “WHEN” part of the domain.

Once events are identified then we determine “Actions”. Actions are triggered by events. For example “order initiated” event might trigger “Check inventory, Initiate the payment process &Issue invoice” actions in the business. By defining the actions we define “WHAT” will happen in correspondence to the events. We need to simply associate events and actions to create a flow and end the flow with “X” where you feel that flow ends and no action is required further.

Once events and actions are been determined then we need to determine the ROLE to define “WHO” will be doing that action or who will respond to those events. This definition of “WHO” is important to derive the boundaries as well as work associated with actions. There can be different actions associated with the same object in different contexts. For example, an Order entity means something different for both “store” and “warehouse” context. A new order means create an invoice and generate shipping labels from the store context. Whereas a new order means shipping the product in a warehouse context. So we have to define “WHO” will handle that object or event or action. By this action, you can even determine how many processes or actions are man power-dependent and how many actions can be automated.

Orchestration

Bounded context can communicate via services. Orchestration of different bounded contexts can be done in two models i.e. Declarative model and reactive model.

Declarative — As the name implies, this model works by declaring the work which needs to be performed by different entities. One entity tells other entities what to do. It has a natural flow of declaration in the top-down approach which creates a tight relation between different entities. This tight relation often leads to an inherent maintenance problem that if you change any downstream entity then all upstream context has to check and changed if required. For example, once an order is placed then it can invoke the services or methods or other microservices to “create shipping label” and “generate receipt”. Now if you want to make a change in “generating receipt” then you need to check the “order entity” too to make sure that the change in the “generate receipt” entity does not effect to “order entity”. In the declarative model “order entity” has to know about “generate receipt” to call its methods which makes this strongly bounded and non decoupled.

Reactive — In the reactive model, no two entities in different bounded contexts know each other implementation details. They are completely isolated and decoupled from each other and do not know about the existence of others in the world. They communicate via the PUB SUB model where the “order” entity publishes an event “order placed” and all other entities who are subscribed to that event are designed to perform defined work. In the reactive model if you change the “generate receipt” entity then you need not change anything in the “order entity” as the published event is still “order placed” and as long as the “generate receipt” entity can respond to that event and perform the assigned task then everything is normal.

The right way to achieve the true microservice architecture is by Reactive Model. There are several services in the market like Kafka, RabbitMQ, ZeroMQ, AQS SQS, etc which can be used to create these event queues to achieve the decoupled architecture.

Advantages of Domain-Driven Design

  1. Effective communication — Use of common language helps better collaboration between developers, business users, and stakeholders. Everyone can talk in the same language about an object in the defined context.
  2. Effective Control — DDD approach helps business owners to keep control over the implementation of technology while not knowing anything about technology because of the common ubiquitous language.
  3. Incremental Approach — DDD allows business users and developers to focus on the current problems and provide quick incremental updates rather than waiting for a big release date.
  4. Flexible — It is flexible because of the strong encapsulation of bounded contexts which make it less painful to adopt and deliver changes.

Disadvantages of Domain-Driven Design

  1. High initial investment — DDD approach requires high investment initially to derive a DDD diagram. It also requires the active involvement of the business user which also increases the investment of the product.
  2. Business user availability — It requires the continuous availability of business users to derive the correct language used in the content. It also requires developers to learn the domain before starting the code.

But overall, I feel that DDD along with microservices can be a good approach to design medium or large size applications to keep both business and developers involved actively in each incremental update but this can be an overhaul for small scale products.

--

--