Bounded Contexts: Behavior Over Data Structures - Part I
Using Domain-Driven Design Key Concepts for Modularization: Bounded Context and Aggregates.
You've probably heard discussions about modularization in software development, or even used it in your own projects. Concepts such as separation of concerns (SoC) are familiar territory. Modularity is a specialization of SoC, holds to principles such as information hiding, loose coupling, and high cohesion. Those familiar with my writings know that I've explored these essential principles in depth. However, a recurring question is how to effectively build modular systems in practice? This is where Domain-Driven Design (DDD) introduces two key concepts: Bounded Context and Aggregates, which provide an iterative approach to identifying modules.
Unlike the more linear methodologies promoted by object-oriented analysis and design, which typically follow a waterfall-like process from requirements through design, implementation, and verification, DDD takes a distinctly iterative stance. Eric Evans' groundbreaking work, Domain-Driven Design, published over two decades ago, shed light on the modularization of complex systems at a vertical level. It's a common misconception that SoC is only about separating engineering concerns horizontally, through architectures such as Clean Architecture or Layered Architecture. However, the purpose of this article is to explore the vertical modularization of complex systems to ensure longevity, maintainability, and comprehensibility.
I will focus on modularization through Bounded Contexts in this article.
Shifting Perspectives: From Nouns to Verbs
The traditional approach to object orientation in software development has been overwhelmingly noun-centric. This perspective focuses on identifying entities such as customers and orders, emphasizing private attributes, methods, and a plethora of getters and setters. The primary goal becomes designing data models to meet every conceivable requirement, often resulting in implementations that are confusing at best. Typically, behavior is relegated to external services tasked with manipulating these data models (they also call it entities), resulting in code that's not only difficult to read, but also hard to understand. This approach leads to what's known as anemic domain models, which are characterized by a lack of business logic. Despite its pitfalls, this methodology persists throughout the industry.
DDD, however, takes a very different approach, prioritizing verbs over nouns. The focus shifts from data to behavior, focusing on what the software needs to do rather than what data it needs to store. In DDD, the key to identifying a Bounded Context lies in understanding the business rules, decisions, and policies - all articulated through a ubiquitous language. This language, defined within each Bounded Context, uses precise terminology that carries specific meaning, ensuring clarity and consistency in communication among all project stakeholders.
The importance of this shift became clear during a discussion I had with a team that included traditional-minded requirements engineers. When I stressed the need for a shared, consistent language within a bounded context, one engineer dismissed the idea, arguing that the success of a software project doesn't depend on the choice of words. This response not only underscored a misunderstanding of the principles of DDD, but also highlighted a common oversight: the failure to adequately model behavior. The belief that semantics are secondary in software development overlooks the critical role that language plays in shaping our understanding of the domain and, by extension, the software we build to operate within it. In DDD, modeling behavior and defining a consistent language are essential to laying the foundation for a robust and coherent domain model.
Understanding Context Through Example
The concept of context is central to disciplines ranging from linguistics and philosophy to software development. To understand the importance of context, let's examine the avocado, a familiar object that undergoes a remarkable transformation when viewed through different lenses.
In the culinary world, avocados are primarily considered a vegetable. They're the star of savory dishes, from guacamole to salads, where their creamy texture and rich flavor enhance the flavor profiles of these preparations. This classification is convenient and shapes the way avocados are marketed, sold and consumed.
However, when we shift our perspective to botany, the classification of the avocado changes dramatically. Botanically speaking, an avocado is a fruit, or more specifically, a berry. This classification is based on specific criteria: it develops from the flower of the avocado tree, contains a seed, and has a pulp.
This discrepancy in classification between culinary and botanical contexts clearly illustrates how the meaning and categorization of an object can change dramatically depending on the context. Just as the identity of the avocado changes from vegetable to fruit when it moves from the kitchen to the laboratory, so do concepts and models within different bounded contexts in software development.
Bounded Contexts
The avocado analogy sheds light on an essential aspect of DDD: the purpose and application of Bounded Contexts. Understanding that an avocado can be considered both a fruit and a vegetable, depending on the context, parallels the notion that a single entity can have different meanings and roles within different Bounded Contexts in domain modelling. This understanding challenges a common misconception in the field - that something like an entity should serve as a universal solution covering all possible interpretations as shown in the example below.
Entity: User
Attributes:
- UserID: INT, Primary Key
- Username: VARCHAR
- Password: VARCHAR
- Email: VARCHAR
- FirstName: VARCHAR
- LastName: VARCHAR
- DateOfBirth: DATE
- RegistrationDate: DATETIME
- LastLogin: DATETIME
- PhoneNumber: VARCHAR
- Address: VARCHAR
- UserType: ENUM('customer', 'admin', 'moderator', 'vendor', 'guest')
- PaymentInformation: VARCHAR
- ShippingAddress: VARCHAR
- BillingAddress: VARCHAR
- Preferences: TEXT
Relationships:
- HasMany: Orders
- BelongsTo: UserGroup
- HasMany: Reviews
- HasOne: ShoppingCart
- HasMany: Tickets (for customer support)
- HasMany: Posts (for forums or blogs)
The concept of a Bounded Context is purposefully bounded; it's not about for instance creating an all-encompassing, vague User entity that tries to be everything to everyone. Instead, it focuses on modeling behavior that is specific to the purpose of the context. A Bounded Context defines the boundaries of a model tailored to a specific application, ensuring that the language and interactions within it are precise and meaningful.
In software development, however, problems arise when the focus shifts from behaviors (verbs) to entities (nouns). This shift often leads to the creation of a data model that acts as a structural boundary around which behavior is then defined and constructed. The problem with this approach is that when a data model - or object - becomes too broad, it loses its ability to effectively serve a specific purpose. As a result, the language used in this context becomes more general and less precise, reducing the value and usefulness of the model.
This drift toward genericism not only obscures the clarity of the model, but also undermines the fundamental principles of DDD. By anchoring our models in behavior rather than structure, we maintain the focus on the specific actions and interactions that define a particular context. This approach not only ensures that our models remain purposeful and coherent, but also facilitates more effective communication and understanding within the development team and across the project as a whole.
Conclusion
As we conclude this first part of the modularization journey through Domain-Driven Design, we've highlighted the central role of Bounded Contexts. Shifting the perspective from static entities to dynamic behaviors within specific contexts clarifies the model. It also increases the flexibility and coherence of a systems.
This exploration sets the stage for a deeper dive into DDD's capabilities in our subsequent discussion. My next focus will be on effectively modelling aggregates within these Bounded Contexts.
Join me in the next part of this series as we continue to unpack the practical aspects of DDD, further empowering you to tackle the complexities of software development with confidence and precision.
Cheers!