Software development using microservices is the most scalable method. However, you must have a good architecture that enables development teams to operate independently and deploy without creating conflicts among team members; otherwise, you risk losing the advantages of scalability.
Domain-driven design helps in designing a microservice-based architecture by breaking the complex system into smaller, independent parts, comprehending their functions, and figuring out how these parts relate to one another. These microservices represent & solve distinct business goals and have specific configurations, domain contexts, and dependencies on a given infrastructure. They can be designed and maintained separately before combining them in a sophisticated application.
In this article, we will explore how we can design microservices using domain-driven principles and will compare it with other application development methods such as Test-Driven Development (TDD) and Behaviour-Driven development (BDD).
What is Domain-Driven Design?
Domain-Driven Design (DDD) is an application architecture design process in which software programmers design models according to specific domains and business goals.
To complete specific business goals or to solve a particular domain problem, it becomes necessary to understand a domain in-depth. How easily a person can solve a problem depends on their ability to understand a domain. Although brilliant individuals, developers cannot be experts in many subjects, and to align the application code with business goals, it becomes critical for them to work with a domain expert.
We can say that domain-driven models act as a framework for the design and development of software. Here, developers collaborate closely with domain experts to solve specific business problems by designing goals-oriented application design models.
It is the process of translating business domain principles into software artifacts. Most books and articles on this topic are based on “Domain Driven Design” by Eric Evans, which mostly tackles domain modeling and design from a conceptual and design perspective.
What are the Components of Domain Driven Design?
Domain-Driven Design has many fundamental components, such as entities, value objects, domain services, aggregates, repositories, and other ideas like ubiquitous language, bounded contexts, and an anti-corruption layer.
The two most crucial elements for developing microservices are bounded contexts and context mapping.
Bounded Context (BC)
Bounded Context (BC) is used to define similar objects together within a context. For example — when designing an e-commerce application, a bounded context can be the segregation of relatable objects like price, zip code, and weight. Here, this group of objects can be bounded together within a context, and we can name this context shipping.
All the Bounded Context (BC) terms have a clear and unambiguous meaning. Depending on the context, “book” can signify either a written piece of work or “to book a ticket.” An actual definition of a bounded context is a boundary that applies to a subdomain. While others don’t make sense in this situation, the particular subdomain does.
When the concept of domain-driven development wasn’t there, the development teams used to work with an application development model spanning the entire domain. The issue with this model is that finding a coherent and unified model becomes more challenging with increased scope.
When talking about the domain, developers and domain experts utilize a language known as the ubiquitous language, which is a term used to describe a technique.
This technique is essential since language disruptions may seriously affect initiatives. This disruption occurs because subject-matter specialists speak in their language. Tech experts converse on the field using their terminology at the same time.
We need to be accurate while writing software. Therefore, it is essential to define BCs since they provide a standard vocabulary, known as ubiquitous language, that can act as a base in talks between developers and domain experts.
As discussed above, Bounded Contexts (BCs) act as boundaries for multiple subdomains and establish communication between them. There’s a need to map them using different channels. This is what we called as context mapping.
For instance, the salesman should verify inventories before selling a product if we’re in the e-commerce industry. Once a product has been sold, it is up to shipping to deliver the item to the right destination. In DDD, context mapping acts as the basis for these connections.
Note: Here, shipping and inventory are two different Bounded Contexts or Subdomains.
Microservices using Domain-Driven Design (DDD)
In domain-driven design (DDD), you can design microservices in two distinct phases.
- Strategic Phase – This includes defining Bounded Contexts (BCs) and mapping them together.
- Tactical Phase – We model each BC following the subdomain’s business rules during this phase.
Let’s discuss both of these phases in detail.
The Strategic Phase
We bring together developers, subject matter experts, product owners, and business analysts at this phase to strategize, exchange expertise, and create a rough blueprint.
During this phase, business needs and models are defined, keeping in mind all the significant events within a domain using multiple focused groups and brainstorming sessions.
We approach design in the strategic phase from a high-level, top-to-bottom perspective. Firstly, you should identify all the business goals and create different Bounded Contexts (BCs) based on these goals.
All newly formed bounded contexts act as an opportunity to design at least one microservice.
Different types of relationships between Bounded Context (BC)
Here we decide how other bounded contexts couple and communicate. Most books and articles list six of them. Eric Evens lists seven types of relationships between Bounded Context, out of which four relationships are necessary to form a microservice design.
- Open Host Service Relationship – Using this service, you can create an open protocol that can be used by any Bounded Context (BC). This relationship has an abbreviation as OHS. Given that it is up to the consumers to follow the protocol, this is an ongoing relationship.
- Published Language Relationship– This relationship is established using a programming language appropriate for the domain, like XML, JSON, GraphQL, etc. Open Host Service and this kind of relationship can coexist. This relationship has an abbreviation as PL.
- Separate ways: This occurs when integration between two services is initially considered valuable but is later determined to be of little use. The bounded context has no connection and doesn’t need to interact, which is the polar opposite of a relationship.
- Anticorruption Layer Relationship: Consumers of services can protect themselves using the anti-corruption layer. This is an encapsulation and interpretation enclosing layer, which is typically put before a downstream service. When something upstream changes, the consumer service only needs to update this layer.
We receive a context map outlining the Bounded Contexts and their relationships at the end of this analysis.
The Tactical Phase
Buried deep, creating software is a modeling exercise where we express a real-world issue as a model and then use code to solve it. Earlier, we identified Bounded Contexts and plotted their relationships. We’ll focus on each context to create a thorough model in this stage, which calls for engineers familiar with domain-driven design theory.
The models created with domain-driven designs don’t depend on technology, or we can say it doesn’t speak about the technology stack used underneath. Here, we focus more on conceptualizing the models of the subdomain.
Our models’ fundamental components are:
Entities are objects with Bounded Contexts (BCs) that have a persistent identity. These are distinct individuals who also serve as behavioral data points. Although you can share entity identifiers between context borders, entities do not have to be identical across all BCs.
Value objects can exist with attributes but not independently. One such value object is the shipping address. The number of entities and value objects in large and complex systems is infinite.
The domain model requires a certain level of coherence, and for the same reason, it will group them logically into manageable categories. Aggregates are the name for these groupings. Intending to treat them as units, they represent a group of related things. Additionally, they have an aggregate root.
This layer contains the domain logic and belongs to the domain model. This stateless service is used to implement business logic. In addition, the application service is a further layer devoid of business logic. It is, however, above the domain model and serves to organize application activities.
When something happens, domain events let other services know. For instance, when a consumer purchases a product, a declined payment, or a user has logged in.
Repositories are permanent storage spaces for aggregates, frequently in the shape of databases.
Example of Domain-Driven Design
Consider an e-commerce application, where processing orders would be the business domain. A consumer must first browse the items if they wish to place a purchase. They then select the ones they want, confirm the purchase, decide on the shipping method, and make a payment. The application then processes the client’s data.
Consequently, a user app would have the following layers:
User Interface (UI)
This layer analyzes clients’ behavior using inputs and presents them with all the helpful information. The customer may discover all the details required to place a purchase on this layer. The commodities are located here in an e-commerce scenario.
Business logic is not a part of this layer. This section guides the user from one UI panel to another. Additionally, it communicates with other systems’ application layers. Its function is to arrange and assign domain objects to perform their duties. Additionally, it is the sole layer that other constrained contexts may access.
There you can find ideas from the business domain. This layer is responsible for holding all the required information about the business goals. It holds information like users or products in the form of entities, usually a combination of data and behavior.
Even when their characteristics alter, they maintain their unique identification, guaranteed using a special key. For instance, every order in an online retailer gets a unique identification number. For it to be an entity, it must undergo several processes, such as confirming and shipping.
Value objects, on the other hand, lack distinctive identifiers. They stand for characteristics that various entities may possess. For instance, several consumers can share the same last name.
This layer also includes services with clearly defined operational characteristics independent of any particular domain. They continue to fall under the business umbrella, though.
This is the most significant layer of an application, and it shouldn’t depend on any other layer or related framework.
The infrastructure layer governs the persistence of data in databases or other persistent storage after storing it in domain entities. In short, this layer facilitates layer-to-layer communication and may include UI layer support libraries.
Comparing Domain-Driven Design (DDD) with TDD and BDD
Doman-driven design is a theoretical framework for designing microservices-based applications. It is best suited for a more complex system where extra planning efforts can prove worth it.
Generally, techies prefer TDD – Test-Driven Development for smaller system designs because this development approach is quick to implement with a single to a few microservices-based systems.
For medium complexity systems, where domain-driven development may not be feasible, it’s better to go with BDD- Behaviour-Driven Development, where the system behavior is validated using integration and acceptance.
You can also design your system using all three methodologies (DDD. TDD & BDD). This can be done by choosing the best approach at each stage of the application development- For Example, You can use the strategic approach of Domain-Driven Design to find-out microservices and their relationships. You can then use the tactical approach to model those microservices and develop the system either through Test-Driven Development (TDD) or with Behaviour-Driven development (BDD), following the complexity of an entire system.
Designing microservices using domain-driven design methodology has numerous benefits over traditional methodologies. These benefits come in a package of better overall scalability, accessibility, and adaptability.
This blog aimed to define the fundamental concepts underlying domain-driven design. We hope this information will help you make better-informed decisions for developing your next business application.
Our experts at AnAr Solutions can help solve new business problems and optimize your current monolithic applications using domain-driven design or migrate them to microservices based on your business need.