The concept of domain-driven Design (DDD) was first proposed by Eric Evans in 2003. The concept of microservices did not yet exist. Therefore, DDD was basically introduced to solve the problem of a large overall code base. In the singleton world, once the code base begins to grow as the business grows, it becomes difficult to maintain the organization and structure of the code it was originally designed for. Monolithic applications designed using the MVC architecture have a good separation between the business layer and the presentation layer. But in the absence of strict architectural guidelines, the business layer does not provide specific rules to maintain boundaries of responsibility between different modules and classes. That’s why as the code base grows, it increases the risk of logic breaking down,

DDD tries to address these challenges by bringing your applications close to a real-world system or, more precisely, to relevant businesses. In DDD, the application logic revolves around a business problem with a defined boundary context. DDD focuses on domain modeling. Let’s discuss the key features of DDD.

  1. Collaboration: DDD is collaborative. Business entities, stakeholders, and developers work together to solve specific business problems. It is more of an agile approach where collaboration is the key.
  2. Data modeling: Data modeling defines that the structure of the 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 part of the business. The field can be accounting, storefront, warehouse, etc. The idea behind mapping the code model to this domain is to make the high-level structure of the application understandable to business users. So when the business expands and an area of the business requires some enhancement to the application, you can change the models and logic associated with that area and leave another part of the application unchanged.
  3. Incremental: DDD is incremental, so you don’t need to design the entire business architecture up front. You just need to solve the current problem and then evolve the code as the domain or business grows. DDD is again aligned with the AGILE methodology and has the characteristics of this incremental approach. The incremental approach is a process of rapidly and incrementally releasing new functionality.

What are microservices?

In short, microservices are about solving a specific problem. In Monolith design, we have a huge codebase that is data-driven and tightly coupled. Therefore, every time we need to change the data model, the overall design is affected. Even the incremental approach is cumbersome, as the entire application must be tested in all cases and scenarios, with different dependency checks to ensure that updates do not cause any failures. Microservices architecture solves this problem of monolithic design and provides the freedom to extend applications in any direction.

How does DDD relate to microservices?

Microservices may be an ideal way to implement DDD, because in microservices we also discuss contextual and incremental approaches, which are also key features of DDD. Both DDD and microservices are modeled around business concepts. Some basic features of microservices are as follows:

  1. Small: Microservices should be small enough to solve a single problem. It can be as small as a class in OOPS or as small as a function in Functional design.
  2. Independently deployable: Microservices can be independently deployable. So, basically, you don’t need to update the entire application as if you were adding a fully functional whole. You can start deployment by simply changing the relevant microservices and checking their context boundaries.
  3. Separation: Individual microservices should be separated, isolated, and their implementation details hidden from the world. Microservices should be accessed only through services to provide decoupling.

Key building blocks for DDD

  1. Domain: This is the logical area where different business entities are defined. For example, a transportation business might have an inventory domain, an accounting domain, a tracking domain, and so on.
  2. Bounded context: These are logical boundaries in code that can solve domain-specific problems. It can be a single class or a group of files encoded to solve a particular problem. The different bounded contexts are completely isolated from each other and do not exist. 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.” The “warehouse” is responsible for “shipping” and the “store” is responsible for “selling”, so they can be thought of as limited contexts.
  3. Services: Services are communication channels between different bounded contexts. You need services to decouple different bounded contexts but still communicate.

  1. Value objects: Value objects have no unique identity and are immutable. It is defined by its attributes. For example, names, addresses, and so on are value objects.
  2. Entities: Entities are domain objects. Entities only perform specific operations in a limited context. These are identified by unique identities. Some examples of entities could be users, orders, jobs, messages, and so on. Entities are mutable and can change their identity. For example, an order might change its status from ordered to shipped and have a unique identity “order_id” to identify.
  3. Ubiquitous language: This represents a language-specific language in context. For example, if you are talking about the object “book” in the context of “store”, you will be more concerned with properties related to “selling products”, such as price, author, etc., whereas the object in “warehouse” is “books”. The context should be more concerned with properties related to “shipping products”, such as weight, size, etc. Therefore, it is obvious that the same object may have different conversational languages in different contexts. Ubiquitous languages depend not only on the NOUN or attribute levels defined above, but also on the VERB level, or, in simpler terms, on how you treat those objects. For example, the “book” object should be used to “sell or organize” books in the “store” context, but should be used to “box” books in the “warehouse” context.

How is DDD implemented?

DDD methods can be used to analyze domains and develop code, a process known as “event storms.” We discuss domains and identify events that are likely to occur at the domain level during an event storm. Keep in mind that it is collaborative and incremental, so you must involve both the enterprise and developers to define the event and model around it. Let’s take an e-commerce business as an example to determine the DDD approach. Note that this is not a complete example. I use this as a reference only. WHEN adopting the DDD approach, we should focus on the three W’s, namely “WHEN”, “WHAT” and “WHO”. You should begin the DDD approach by defining the business domain and its responsibilities. E-commerce companies may have different areas, such as “warehouse”, “store”, etc. The next step is to define the responsibilities of these areas. For example, the “warehouse” delivers books, while the “store” domain sells books. Both areas have their limitations, and there is no need to interfere with each other’s work or understand each other’s work styles. So here we can separate the context and hide the individual context and its implementation from the outside world.

Now we need to identify domain-specific events. In the “warehouse” and “Store” contexts, you can place an order, generate a shipping label, place an order, and so on. These events can be manually driven or automatically driven. In DDD, this is an important concept, we don’t care about actors or individuals, we care about roles and their related events. For example, if a user logs in and creates an invoice, we don’t care about the individual user, but rather the user’s role (that is, the “account”) and the event that the user performs, “create an invoice.” Basically, when deriving events, we define the “when” part of the realm.

After determining the event, we will determine the “operation.” Actions are triggered by events. For example, an “Order started” event might trigger the “Check inventory, start the payment process, and issue an invoice” action in an enterprise. By defining actions, we define “events” that correspond to events. Simply associate events and actions to create a process and end the process with an “X”, and you will feel that the process is finished and no further action is required.

After events and actions are identified, we need to identify roles to define WHO will perform the actions or WHO will respond to these events. This definition of the World Health Organization is important for drawing boundaries and for operations-related work. In different contexts, there may be different actions associated with the same object. For example, the Order entity means different things to the “store” and “warehouse” contexts. A new order means creating an invoice and generating shipping labels based on the store context. A new order means shipping the product in a warehouse environment. Therefore, we must define “WHO” that will handle the object, event, or operation. With this action, you can even determine how many processes or operations are human-related and how many operations can be automated.

choreography

Bounded contexts can communicate through services. Choreography of different bounded contexts can be done in two models, the declarative model and the reactive model.

statement

— As the name implies, this model works by declaring work that needs to be performed by different entities. One entity tells other entities what to do. The top-down approach has a natural declaration flow that creates tight relationships between different entities. This tight relationship often leads to an inherent maintenance problem, that is, if you change any downstream entities, all upstream contexts must be checked and changed as needed. For example, once an order is placed, it can invoke services or methods or other microservices to “create shipping labels” and “generate shipping receipt.” Now, if you want to change Build Receive, you also need to check the Order Entity to ensure that the changes in the Build Receive entity do not affect the Order Entity.

reactive

– In a reactive model, no two entities in different bounded contexts know the implementation details of each other. They are completely isolated and separate from each other, and unaware of the existence of other people in the world. They communicate through the Pub-Sub model, where the “order” entity publishes the event “Placed order” and all other entities subscribed to this event are intended to perform the defined work. In the reaction model, if you change the Generate Receive entity, you don’t need to change anything in the Order Entity, because the published event is still “Placed order,” and as long as the Generate Receive entity can respond to the event and then perform the assigned task, everything is fine.

The right way to achieve a true microservice architecture is through the Reactive Model. There are several services on the market, such as Kafka, RabbitMQ, ZeroMQ, AQS SQS, etc., that can be used to create these event queues to achieve a separate architecture.

Advantages of domain-driven design

  1. Effective Communication – Using a common language facilitates better collaboration between developers, business users and stakeholders. Everyone can talk about objects in the same language in a defined context.
  2. The effective Control-DDD approach helps business owners control the implementation of technology while remaining ignorant of it due to a common common language.
  3. Incremental approach – DDD enables business users and developers to focus on current issues and provide rapid incremental updates without having to wait for numerous release dates.
  4. Flexible – Flexible because of strong encapsulation of constrained context, which takes the pain out of adopting and delivering change.

Disadvantages of domain-driven design

  1. High initial investment – THE DDD approach initially requires a large investment to produce DDD charts. It also requires the active involvement of business users, which increases the investment in the product.
  2. Business user availability – It requires business users to have continuous availability to derive the correct language used in the content. It also requires developers to learn the domain before starting the code.

But overall, I think DDD with microservices can be a good way to design medium – or large-sized applications so that both business and developers are actively involved in each incremental update, but it can be a complete check on small-scale products.