Why we need Domain Driven Development
- Authors
- Name
- Rafał Kostecki
- Author Poyters
Greetings everyone! After immersing myself in the world of Domain-Driven Design for some time now, I'm thrilled to provide you with a brief overview and, more importantly, persuade you to incorporate some of the DDD rules into your practical toolkit.
Drawing inspiration from Eric Evans, Domain-Driven Design book. I'll be delving into key principles that truly set DDD apart. We'll explore the power of a shared vocabulary, explore various facets of projects, and unravel other intriguing concepts that significantly influence our approach to software design.
Let's keep it straightforward and uncover how DDD is can be truly practically adapted into commercial projects.
Getting Cozy with DDD
At its core, DDD is about putting the problem domain front and center in the software development circus
. It's like bringing everyone to the same table – developers, domain experts, stakeholders – to cook up a shared understanding of what the heck we're building.
Breaking Down the Silos
Imagine the typical software development scene: developers buried in code, domain experts navigating the business maze, and stakeholders juggling their expectations. It's like a three-ring circus, each ring spinning independently. Now, bring in DDD – it's the grand tent under which everyone gathers.
DDD breaks down the silos, bringing all the players to the same table. Developers aren't just translating requirements; they're actively involved in discussions with domain experts and stakeholders. It's a collaborative kitchen where everyone tosses in their ingredients to cook up something amazing.
Ubiquitous Language
In this setup, ubiquitous language
is the secret sauce. Instead of developers nodding along to complex business jargon like they're deciphering ancient hieroglyphics, everyone speaks in terms that make sense to both the techies and the business brains. It's not just about coding; it's about crafting a shared narrative that tells the story of what needs to be built.
The concept of Ubiquitous Language has proven to be one of the most valuable in my career. In general, he advocates consistent language throughout the text, with particular emphasis on fostering shared understanding between business and development teams.
Let's illustrate this with an example to better understand the ubiquitous language.
Consider a fintech project where various types of users are involved, each with different capabilities. One user can perform action X, while others can perform action Y. They are divided into different hierarchies, each of which is closely tied to specific business rules. These business rules are crucial for all stakeholders, including the business team, security developers and programmers.
Now, let's imagine you're a programmer tasked with displaying information in the details panel. The information varies for each type of user. It's evident that the task description requires clarifications on what actions each user can perform. This information needs consensus with business and security teams to formulate new policies.
This is where ubiquitous language comes into play. We can define our internal domain language, such as:
Action types
- Manage - can manage workflow
- Restricted view - can view only publicly available data
- Extend view - can view secret data
User types
- Merchant - can perform action manage and extend view.
- Inspector - can perform extend view.
- Guest - can perform only resttricted view.
And such a ubiquitous language document must
be available to everyone, and we should only use such ubiquitous language to keep our common understanding.
By specifying such details, we streamline communication across different departments. When a programmer mentions implementing a detailed view for the Merchant in a demo meeting, the business person understands that the business requirement is met, and the security person recognizes the need to check specific rules.
The key is that everyone shares the same understanding of domain-specific terms.
Now imagine an environment that includes the project code base, business documentation, DevOps charts, etc., all of which use the same ubiquitous language, for example referring to the user as "Merchant" instead of "User". This consistency clarifies communication, speeds up discussions and eliminates the need for multiple clarifications of requirements.
Furthermore, this approach should extend to coding, pipelines, tests, and other aspects, maintaining strict adherence to our domain language.
In a nutshell an ubiquitous language:
- Speeds up communication due to common clarification
- Provides the same meaning, error prone
- Speeds up development phase due to common understanding and the same naming in code base.
But such language have some drawbacks:
- At the beginning needs some time to create a domain dictionary
- Requires discipline to use ubiquitous language language everywhere which can be tricky at the beginning
Domain
The domain is like a specialized realm or area of focus within a larger software project. It's not just about the technical aspects of coding; it's about understanding and solving real-world problems or addressing specific business needs.
We can have several domains in a project, and each domain can have several limited contexts.
Imagine building a university management platform:
Admissions Domain
Handles student enrollment, admission criteria, and related processes. Terms like "applicant status" and "enrollment dates" are specific here.
Academic Affairs Domain
Manages course-related tasks, grading, and academic records. Concepts like "course registration" and "transcript" are significant within this domain.
Finance Domain
Deals with financial transactions, tuition payments, and budgeting. "Tuition fees" and "financial aid" are key terms here.
Clear Boundaries
Each department operates independently with clear rules and definitions for terms specific to its domain.
Collaboration Between Departments:
Departments communicate seamlessly; for instance, Admissions validates enrolled students with Academic Affairs, and Finance relies on data from both.
Integrated University Platform:
The entire platform integrates these domains, ensuring smooth information flow without conflicts.
Bounded context
In DDD, a Bounded Context is like drawing a clear line around a specific part of a complex system. This line sets the rules and meanings for everything inside it. Imagine it as a zone where terms and concepts have specific definitions. This helps teams avoid confusion and ensures everyone in that zone shares the same understanding of the problem. Moreover, the implementation of such boundaries guarantees the encapsulation of a specific context from others. Thus, different teams can work with different bounded contexts without any hassle, or we can replace an entire boundary context without affecting others (when ensuring the same interface).
Let's consider an example of a monolithic modular architecture for an e-commerce system, where different modules represent distinct Bounded Contexts
:
Order Processing Module (Bounded Context):
Within the monolith, there's a module specifically dedicated to order processing. This module has its own set of models, logic, and database tables related to orders. It defines terms like "order status," "payment confirmation," and "shipping details" with specific meanings within its context.
User Management Module (Bounded Context):
Another module is responsible for user management. It handles user authentication, registration, and profile details. In this Bounded Context, terms like "user role," "authentication token," and "profile information" have meanings specific to user management.
Inventory Management Module (Bounded Context):
There's also a module dealing with inventory management. It keeps track of product availability, restocking, and inventory levels. Terms such as "stock quantity," "reorder point," and "product availability" have clear definitions within this Bounded Context.
Model
The model is a representation of the key concepts, entities, relationships, and behaviors within a specific Bounded Context. It serves as a conceptual framework for understanding and solving the problems related to a particular domain. Here are the key components of a model in DDD:
Entities
Entities are objects within the domain that have a distinct identity and are typically the primary actors in the system. They have a lifecycle, and changes to their state are crucial for the business.
class Product {
constructor(
public productId: string,
public name: string,
public description: string,
public price: number
) {}
}
Value Objects
Value Objects are objects without a distinct identity. They are defined by their attributes and are immutable. Value Objects are used to represent descriptive aspects of the domain.
class Address {
constructor(
public street: string,
public city: string,
public zipCode: string
) {}
}
Aggregates
Aggregates are clusters of related entities and value objects that are treated as a single unit. They ensure consistency and encapsulate business rules within a specific part of the domain.
class Order {
constructor(
public orderId: string,
public customerName: string,
public products: Product[], // List of Product entities
public shippingAddress: Address // Address value object
) {}
}
Services
Services represent operations or actions that don't naturally fit into the concept of an entity. They provide a way to express behavior that involves multiple entities or doesn't belong to a specific object.
class OrderService {
calculateTotalPrice(order: Order): number {
// Logic to calculate total price based on products in the order
// (Assuming products have 'price' property)
return order.products.reduce((total, product) => total + product.price, 0)
}
processOrder(order: Order): void {
// Logic to process an order, update inventory, etc.
}
}
DDD hierarchy
So overall DDD hierarchy looks like:
- Domain: The overarching problem space or business area.
- Bounded Contexts: Explicit boundaries within the domain where specific models apply.
- Models: A conceptual representation of the domain within a Bounded Context, capturing essential entities, value objects, aggregates, and services.
- Bounded Contexts: Explicit boundaries within the domain where specific models apply.
- Project can have multiple domains.
- Domain can hve multiple bounded context.
- Each bounded context can have several models.
Strategic and tactical DDD
The tactical and strategic design patterns in DDD are closely related to the concepts we discussed earlier. In essence, these patterns provide a comprehensive set of guidelines for both the internal organization of a Bounded Context (tactical) and the relationships between different Bounded Contexts (strategic). Together, they form a powerful framework for designing software systems that accurately model complex domains.
When use DDD?
When you're thinking about jumping into Domain-Driven Development, it's like picking the right outfit for the occasion - you want something that fits just right. DDD really comes in handy for those big, complicated projects
, with huge and difficult domain.
But if you're working on something smaller, more laid-back, DDD might feel a bit like wearing a three-piece suit to a backyard barbecue. It's nice and all, but maybe a bit too fancy for the vibe. Sometimes, you just want to kick back in jeans and a T-shirt.
Simply
: use DDD for huge and difficult domain projects. Otherwise it may be overkill.
Let's wrap it up
So, wrapping it up, Domain-Driven Development isn't just about giving us fancy software design tools - it's about shaking things up in how we tackle building apps. Instead of diving straight into the code, DDD gets us chatting about the real nuts and bolts of the problem we're trying to solve.
And let me tell you, bringing DDD into the mix can be a game changer
, when you're dealing with projects that are all over the map in terms of complexity and scope.
Additional resources
- Greate example of implementing modular monolith architecture with DDD
- Domain-Driven Design book
- Modular monolith - domain centric design