Frequent domain modeling patterns

by Askretkova Valentina, vdaskretkova@edu.hse.ru

The modern world is full of information systems. They differ in complexity, scope of application, size, time of existence. Each of them is designed to describe a part of the world, simulate it, simplify and accelerate interaction with it. However, people who implement the system are not necessarily specialists in the subject matter that the system models. In this regard, the importance of sharing and exchanging information among the areas of responsibility became clear. Domain modeling plays a big role in this exchange. This essay will describe the development of domain modeling approaches and highlight the most common ones.

The history of domain-driven design as an established software development approach spans over 20 years [1]. However, the initial mentions of the necessity to focus on domain modeling in software development can be traced back to Gerald Weinberg's book, “An Introduction to General Systems Thinking,” authored in 1975 [2]. As with any sufficiently long-standing field dedicated to a specific domain, domain-driven design has witnessed the development of widely accepted methodologies and solutions. However, before proceeding to enumerate groups of patterns, it is essential to comprehend the challenges [1] confronted by professionals during system development.

First and foremost, it is worth mentioning a lack of a common language. Insufficient attention to shared terminology in exchange of information between different domains leads to exponentially increasing complexities in system maintenance, error detection, and system expansion.

Additionally, there is the issue of a Lack of Focus on the Problem Domain. Programming, in itself, merely serves as a tool for expressing thoughts and necessities. Its primary complexity lies in crafting code that satisfies all business needs with all their nuances.

Lastly, but not in terms of significance, is the lack of organization [1]. The initial incarnation of a system that meets basic business needs is often achievable in a concise and comprehensive manner. However, extending the initial system without due attention to organization and domain focus becomes troublesome. The codebase may be complete, concise, and well-documented, yet it may lack the necessary tools for synergy with other systems which leads to increasing development costs.

Millet and Scott, in their book “Patterns, Principles, and Practices of Domain-Driven Design” [1] categorize domain-driven design patterns into tactical and strategic ones.

The Tactical Patterns of DDD, also known as model-building blocks, constitute a set of patterns aimed at creating effective models for complex bounded contexts. Many of the coding patterns presented within this collection of tactical patterns had already gained widespread adoption before Eric Evans's work. It's important to note that these patterns aren't universally applicable to all models, and each must be evaluated on its own merit, with the appropriate architectural style applied.

Conversely, the strategic patterns, initially described by Eric Evans [3], are focused on refining the problem within the domain and shaping the architecture of the application. They separate the system that addresses the problem from auxiliary components, thus aiding in breaking down complex tasks into simpler ones, reducing the time and cost of decision-making.

Patterns within this group are particularly important when developing applications with complex logic involving different teams. Communication can break down, leading to subtly conflicting interpretations of the model. Models can change at distinct times by various individuals with varying levels of system understanding. Company servers may host codes of different versions. Martin Fowler, a practicing developer and independent consultant who authored the book “Patterns of Enterprise Application Architecture” [4] and actively maintained his website [5] faced these practical challenges. Drawing from Eric Evans's work, which has become a de facto classic [3], he identifies the following patterns:

  1. Bounded Context: Divides the original complex model into multiple simpler ones, explicitly specifying their interactions.
  2. Continuous Integration: Provides a development methodology idea where all work within the context is unified and becomes consistent to a degree that ensures prompt defect and failure resolution.
  3. Shared Kernel: Isolates mutual features from the model of interacting teams and relocates them to a separate model. This model serves as a form of precise synchronization for everyone operating with it, as updates performed simultaneously across all teams.

Patterns in this group serve to build a subsystem based on conventional principles. They are extensively cataloged in numerous practical programming publications, by authorities such as Martin Fowler in “Patterns of Enterprise Application Architecture” [4] and Erich Gamma in “Design Patterns: Elements of Reusable Object-Oriented Software” [6]. Some of the most common examples include:

  1. Transaction Script. Organizes domain logic within a single transaction that atomically alters the data state in the database.
  2. Entity. Represents a concept in your domain defined by its identity rather than its attributes.
  3. Domain Service. Encapsulates domain logic and concepts that cannot be naturally modeled as value objects or entities in your model.

Over time, more complex groups of patterns modeling intricate interactions of varying complexity between systems have emerged based on model descriptions and simple integrations. Such are vividly described in the book “Patterns and Best Practices for Asynchronous Messaging” by Gregor Hophe [7]:

  1. Publish-Subscribe Channel. Provides the capability to securely store a message in temporary storage, allowing multiple subscribers to read it repeatedly while preserving the message order.
  2. Zero-Payload Events. A variation of message exchange where the message broker forwards only the event type and its identifier.
  3. Outbox [8]. Utilizes the Transaction Script pattern to save essential data in persistent storage before forwarding it to the message broker. It resolves the issue of atomic database updates and message transfer to systems in other domains.

Domain modeling has become an essential stage in the software development process. Throughout its developmental history, numerous domain modeling patterns have emerged. They represent a collection of widely accepted practices for constructing a system, ranging from entity description to the extensive planning of interactions between complex systems. Despite the seemingly comprehensive coverage of the subject area, new DDD (Domain-Driven Design) patterns regularly emerge, which must be taken into consideration when developing a new system. These patterns develop in response to the advent of new technologies and the evolution of communication methods among people.

  1. Millet, Scott; Tune, “Patterns, Principles, and Practices of Domain-Driven Design”, 2015, p. 3-7.
  2. Gerald Weinberg, “An Introduction to General Systems Thinking”, February 12, 1975
  3. Eric Evans, “Domain-Driven Design: Tackling Complexity in the Heart of Software”, 2003. p. 293-453
  4. Martin Fowler, “Patterns of Enterprise Application Architecture”. November 5, 2002
  5. Martin Fowler, Bounded context https://martinfowler.com/bliki
  6. Erich Gamma, “Design Patterns: Elements of Reusable Object‐Oriented Software.”, 1994
  7. Gregor Hophe, Bobby Woolf, “Patterns and Best Practices for Asynchronous Messaging”, October 10, 2003