Domain-Driven Vocabulary Collection

2022年3月29日 22点热度 0人点赞 0条评论
内容目录

What is DDD

Domain Model

What is a domain model? A domain model is a software model that pertains to a specific business domain. Typically, a domain model is implemented through an object model that encapsulates both data and behavior, expressing accurate business meaning.

The domain model is the modeling emphasized in DDD. To design a model, three points must be considered:

  • Why build a model;
  • How to build a model properly;
  • What exactly is meant by "domain" model.

Why build a model; how to build a model properly; what exactly is meant by "domain" model.

DDD divides the model into four layers:

  • UI layer, responsible for interface presentation.
  • Application Layer, responsible for business processes.
  • Domain Layer, responsible for domain logic.
  • Infrastructure Layer, responsible for providing infrastructure.

The basis for classification is: the higher up the layers, the more frequent the expected changes; the lower down, the less frequent the expected changes.

The models that appear in the Domain Layer are known as domain models.

It is easy to confuse the Application Layer and the Domain Layer. In these two layers exist application models and domain models.

According to DDD's definition, a domain model should capture "business rules" or "domain logic," while the application model captures "application logic." A rough judgment method for determining which layer a model belongs to is as follows: If it is an entity and operations related to addition, deletion, modification and query on that entity, it belongs to the Domain Layer; if it is a scenario, such as an option appearing on a UI menu, it belongs to the Application Layer.

The "domain model" is the "solution space," a visual representation of key entities and their relationships within a specific domain, constructed to accurately define the problems that need to be solved. It maps the business functional scenarios into the software system, aiming to build a unified understanding of the software system.

Handling Domain Complexity

When using DDD, we initially aim to apply it in the most critical business scenarios. For software that can be easily replaced, you would not invest in it. Conversely, what is worth your investment are the important and complex aspects, as they can yield considerable returns. For this reason, we name such models the Core Domain, while those that are relatively less significant are referred to as Supporting Subdomains. Now, we need to clarify what "complex" truly means.

The Role of DDD is to Simplify, Not Complicate

When using DDD, we should adopt the simplest means to model a complex domain rather than making the problem more complicated. Different business domains have varying definitions of complexity. Additionally, different companies face distinct challenges; their maturity levels vary; their software development capabilities differ. Thus, rather than defining what is complex, it is more useful to define what is important. At this point, your team and management need to decide whether the software system you are developing is worth the investment in DDD.

Scorecard

To decide whether your project is worth the investment in DDD, evaluate your project's situation based on the descriptions in the rows; assign corresponding scores in the right column, and sum these scores for a total. If the total score is 7 or above, you should consider using DDD.

This means we should evaluate simplicity and complexity early in the project planning, which can save much time and expense and avoid numerous troubles. Once we make important architectural decisions and have developed extensively under that architecture, we are usually bound to that architecture, so it is crucial to be cautious during decision-making.

Anemia and Amnesia

Anemia and amnesia are serious conditions that significantly endanger human health and may have dangerous side effects. When Anemic Domain Objects were first introduced, they were not praised; they described domain objects lacking internal behavior. Interestingly, people's attitudes towards Anemic Domain Objects are mixed. The problem lies in the fact that most developers consider such domain objects normal, unaware that it is a severe issue. Do you want to check the health of the model you built? If you suddenly suffer from a technical "depression," you can conduct a self-assessment. You could feel joyful or terrified. Start checking using the steps in Table 1.2.

How to DDD

Let’s temporarily set aside discussions on implementation details and examine one of DDD's most powerful features: the ubiquitous language. Ubiquitous language and bounded contexts constitute the two main pillars of DDD and complement each other.

Context Terminology
For now, a bounded context can be seen as a conceptual boundary within the application. Within this boundary, every domain term, phrase, or sentence -- that is, the ubiquitous language -- has a defined contextual meaning. Outside the boundary, these terms may signify different meanings. We will delve into bounded contexts in Chapter 2.

Ubiquitous Language
Ubiquitous language is the language shared by the team. Domain experts and developers communicate using the same ubiquitous language. In fact, everyone on the team uses the same ubiquitous language. Regardless of your role in the team, as long as you are a member of the team, you will use the ubiquitous language.

Business Value of DDD

file

Refer to https://weread.qq.com/web/reader/f5032ce071fd5a64f50b0f6k9bf32f301f9bf31c7ff0a60

Continuous Modeling and Tools

DDD is not about drawing model diagrams; it is about transforming the mental models of domain experts into useful business models. DDD does not create a model of the real world, but mimics reality.

Within a certain modeling boundary, the team will use tactical modeling tools: Aggregates, Entities, Value Objects, Domain Services, and Domain Events.

Subdomains, Bounded Contexts, and Context Mapping Diagram

  • Understanding domains, subdomains, and bounded contexts
  • Understanding the importance of strategic design
  • Learning about a real domain that contains multiple subdomains
  • Understanding bounded contexts

Domain

Broadly speaking, a domain is everything that an organization does, along with everything it contains. Every organization has its own business scope and ways of operation. This business scope and the activities conducted within it constitute the domain. When developing software for an organization, you are facing that organization’s domain. This domain should be clear to you, as you work within it. It is essential to note that the term "domain" may carry too many meanings. A domain can refer to the entire business system or a specific core domain or supporting subdomain. In this book, I will strive to distinguish these concepts clearly. When referring to a specific aspect of the business system, I will use terms like "core domain" or "subdomain" to indicate the distinction.

Since "domain model" includes the word "domain," we might assume that we should create a single, cohesive, fully functional model for the entire business system. However, this is not the goal of utilizing DDD. Quite the opposite, in DDD, a domain is divided into several subdomains, and the domain model is developed within bounded contexts. In fact, when developing a domain model, our focus is usually only on a particular aspect of this business system. Attempting to create a fully functional domain model is very challenging and easily leads to failure. As discussed in this chapter, dividing a domain can help us succeed. Given that a domain model cannot encompass the entire business system, how should we partition the domain model? Almost all software domains contain multiple subdomains, which does not relate significantly to the complexity of the software system itself. Sometimes, the success of a business system hinges on the various functionalities it offers, and treating these functions separately can be beneficial.

Unified language must be expressed within the domain model, primarily reflected in the names within the domain model.

Subdomain

The domain of a retailer can be divided into four major subdomains: Product Catalog, Order, Invoicing, and Shipping. The upper half of Figure 2.1 represents such an e-commerce system.

Focusing on the Core Domain
file

The following image shows that the electricity billing context ultimately involves four subdomains: billing instance, billing processing, clearing billing, and payment management subdomain.
file

Understanding Bounded Context

Do not forget that a bounded context is an explicit boundary within which the domain model resides. The domain model expresses the ubiquitous language as a software model. The reason for creating boundaries is that within the boundary, every model concept, including its attributes and operations, has a special meaning. If you are part of a modeling team, you should understand the exact meaning of these concepts.

Bounded Context is Explicit and Full of Semantics
A bounded context is an explicit boundary within which the domain model exists. Within this boundary, all terms and phrases in the ubiquitous language have specific meanings, and the model must accurately reflect the ubiquitous language.

file

A bounded context refers to an environment with spatial or temporal boundaries, which determines the scope of applicability for each model, reflecting logical consistency within this scope.

A challenge here is that a bounded context is intangible; it cannot be directly reflected, and its logical consistency can only be manifested through the model. The internal definitions of the model exhibit the context in which they exist. For example, one can infer whether a person speaking has a southern or northern accent based on the accent itself; the southern or northern context is the spatial boundary, and the accent is the internal definition of the model.

For example, for a person, the model might be defined as follows:

file

Note that the name "Person" might be misleading; an individual has both work and life aspects, so it is permissible to include attributes related to both work and life within "Person." This introduces the naming issue. The name explicitly delineates the bounded context in which the model resides. This name can be referred to as the ubiquitous language, but it is not a unified language in the entire domain; rather, it is a unified language within the boundary of the bounded context.

Within this model, attributes like employee ID and position reflect a workplace-related context. If the model also includes an attribute like "number of children," conflicts arise with attributes like position and employee ID.

What a Bounded Context Contains

How many components from the domain model can be included in a bounded context? For example, modules, aggregates, domain events, and domain services. Concepts outside of the core domain should not be included in the bounded context.

Architecture

Layered Architecture

Figure 4.1 illustrates a traditional layered architecture employed by a typical DDD system, where the core domain is situated in one of the layers, above it is the User Interface layer and Application Layer, and below is the Infrastructure Layer.

file

An important principle of layered architecture is that each layer can only couple with the layer directly beneath it. Layered architecture also has several variations: in a Strict Layers Architecture, a layer may only couple with the layer directly below it; a Relaxed Layers Architecture allows any layer above to couple with any layer below. Since the User Interface layer and application services often need to interact with infrastructure, many systems are based on a relaxed layered architecture.

When a domain model publishes Domain Events, the application layer can register subscribers to any number of events, allowing for event storage and forwarding. Meanwhile, the domain model only needs to focus on its core logic; the Domain Event Publisher can remain lightweight without relying on the infrastructure of the messaging mechanisms.

Entity

Developers tend to focus on data rather than the domain. This is often the case for newcomers to DDD, as databases still dominate software development. Initially, we consider data attributes (corresponding to database columns) and relationships (foreign key relations) rather than rich behavior of domain concepts.

  • Why consider using entities when modeling things with "uniqueness."
  • Learn how to generate a unique identifier for entities.
  • Learn how to capture the ubiquitous language from entity designs.
  • Learn how to express the roles and responsibilities of entities.
  • Learn how to validate and persist entities.

When we need to consider the individuality of an object or distinguish different objects, we introduce the domain concept of an entity. An entity is a unique thing that can evolve significantly over an extended period. We can modify an entity multiple times, so an entity object may differ considerably from its previous state. However, because they share the same identity, they remain the same entity.

The relationship between Tenant, User, and UserPassword helps to understand entities, aggregates, and aggregate roots.

  • A User exists under a specific Tenant and is controlled by that Tenant.
  • Users must be authenticated within the system.
  • Users can manage their personal information, including names and contact details.
  • User's personal information can be modified by themselves and their Manager.
  • A User's secure password can be changed.

A User should have a unique identifier to distinguish it from other Users. A User should also support various modifications throughout its lifecycle. Clearly, at this point, the User can be regarded as an entity. Here, we do not concern ourselves with how to model the internal structure of the User.

Team members need to clarify the first requirement above:

  • A User exists under a specific Tenant and is controlled by that Tenant.

The team could add some annotations or modify wordings to explain that this means "the Tenant owns the User," but they did not. Team members should be particularly cautious here, as they should not get bogged down in technical and tactical modeling details. In the end, they modified their description of the User as follows:

  • The Tenant can invite multiple Users for registration.
  • The Tenant can be either active or inactive.
  • The system must authenticate Users, and authentication can only occur when the Tenant is active.

file
file

The Tenant entity acts as a factory for the User entity and is the only class in the same module that can access the User constructor. Thus, only the Tenant can create User instances.

file
file

How do we ensure that properties remain in a valid state? As I have emphasized elsewhere in this book, I strongly recommend using self-encapsulation to validate properties.

Value Objects

A purely entity-focused mindset is neither necessary nor efficient and unnecessarily wastes development time. When designed appropriately, we can create and pass around instances of value objects, even discarding them directly once they're done. We need not worry about clients modifying value objects. The lifecycle of a value object can be long or short; it is like a harmless visitor moving in and out of the system.

Although creating a value object type is simple, even experienced DDD developers sometimes face difficulty deciding whether to model as an entity or a value object. Alongside how to implement value objects, I aim to clarify these vague concepts in this chapter.

file

When a value object truly exists within your model, regardless of whether you realize it, it should not be treated as an object in your domain but merely a concept used to measure or describe something in that domain. A person has an age; here, age is not a tangible thing but rather a measure of how many years you have been alive. A person also has a name, which similarly is not a tangible thing but describes how to address that individual.

A value object cannot change after its creation.

So, when is an operation not part of an entity or a value object? It is challenging to provide a comprehensive list of reasons; however, the following points can be listed: You can use domain services to:

  • Execute a significant business operation process.
  • Transform domain objects.
  • Use multiple domain objects as input for a calculation that results in a value object.

It should be noted that the computation process in the last point should exhibit the characteristics of a "significant business operation process," making it a common application scenario for domain services that may require several aggregates as input. When a method is unsuitable for inclusion under an entity or value object, using a domain service is the best solution. Ensure that domain services are stateless and articulate the ubiquitous language clearly within the bounded context.

What are Domain Services

In the comprehensive entity structure of Tenant and User, each has its own operational methods. Depending on the purpose of creating a domain service, sometimes modeling a domain service can be very simple. You need to decide if the domain service you create requires an independent interface. If so, your domain service interface may resemble the following:

file

Is an Independent Interface Necessary?
Since the AuthenticationService here does not have a technical implementation, do we really need to create an independent interface and separate it from the implementation class in different layers and modules? It is not necessary; we simply need to create an implementation class with the same name as the domain service.

The above example is also applicable to domain services. We might even find such an example more fitting since we know no additional implementation classes are likely to arise. However, different tenants might have different security authentication standards, leading to varied authentication implementation classes. Nonetheless, the SaaSOvation team members decided to abandon the independent interface and instead follow the implementation method shown above.

Naming Implementation Classes for Domain Services
In the Java world, a common method for naming implementation classes is to append "Impl" to the interface name. Using this method, our authentication implementation class will be named AuthenticationServiceImpl. Furthermore, implementation classes and interfaces are usually placed in the same package. Is this a good practice? In fact, if you adopt this naming method for implementation classes, it often indicates that you do not need an independent interface at all. Therefore, careful thought is required when naming an implementation class. Here, AuthenticationServiceImpl is not a good name for an implementation class, nor is DefaultEncryptionAuthenticationService particularly better.

For these reasons, the SaaSOvation team members decided to remove the independent interface and instead use AuthenticationService directly as the implementation class.

If a domain service has multiple implementation classes, naming should reflect the characteristics of these various implementations, often implying specific behavioral functions within your domain.

file

file

A dependency inversion container (like Spring) will take care of service instance injection. As the client is not responsible for instantiating the service, it does not know whether the interface class and the implementation class are separated or merged.

Domain Events

Before discussing domain events, let us first look at the current definition of domain events: Some events that occur within the domain and concern domain experts. Modeling activities occurring in the domain as a series of discrete events. Each event is represented using domain objects... Domain events are part of the domain model that indicates events that occur in the domain. [Evans, Ref, p.20]

When modeling domain events, we should name events and their attributes based on the ubiquitous language within the bounded context. If events result from command operations on aggregates, we usually name the domain events following the name of that operation method. For the earlier example, when we submit pending items to a sprint, we will publish the corresponding domain event:

Aggregate

While assembling entities and value objects into aggregates within a consistency boundary may seem easy at first, among the many tactical guidelines of DDD, this concept is often the most challenging to understand.

Initially, Product was modeled as a very large aggregate. Here, Product acts as a root object, containing all BacklogItems, Releases, and Sprints, while the interface design of Product prevents clients from accidentally deleting its contained data. The implementation code for Product is as follows; for the corresponding UML diagram, refer to Figure 10.1: Product modeled as a cumbersome aggregate.

file

This massive aggregate may seem tempting, but it is impractical. When the system operates in a multi-tenant environment, transaction failures often arise. Let’s look further into how clients interact with this technical model. During persistence, we applied optimistic concurrency measures to avoid multiple clients modifying a single Product instance simultaneously. As mentioned for entities, the persistent objects carry an incrementing version number that increases with each modification. If the version number in the database exceeds that of the client, the server will reject the client's request.

Second Attempt: Multiple Aggregates
Now, let’s consider an alternative approach, as shown in Figure 10.2, which employs four separate aggregate classes. They are associated through ProductId, the unique identifier of Product. At this point, Product acts as the parent aggregate of the other three aggregates.
file

When a large Product aggregate is divided into four relatively smaller aggregates, the method signatures of the Product class will also change. The method signatures for the previous massive Product are as follows:

file

Design aggregates to be as small as possible.
file

If we compare bounded contexts to delineating land, then constructing houses on that demarcated land resembles aggregates; within those houses exist primary and secondary structures as a group. Aggregates are also a group of objects, featuring a hierarchical relationship.

The relationship between a group of buildings and the land is similar to the relationship between aggregation and bounded context. Aggregation is a domain model, and its significance depends on the bounded context in which it resides. The core concept of logical consistency within the bounded context must also be embodied through domain models such as aggregation. This is a primary design principle.

file

If bounded context deals with the segregation within a domain, then aggregation addresses the segregation among objects within a bounded context. The so-called segregation means putting closely related items together, loosening the loosely related ones, or even having no relation at all. This reflects a trend of contraction within DDD, where various domains are designed with aggregation as the core focus, as shown in Figure 3-2.

file

Aggregation Internal Structure:
Aggregation is a group of objects that are highly consistent logically in behavior. Note that it is a collective term for a group of objects. The internal structure of an aggregation resembles a tree, where each aggregation has a root, and the relationship between other objects and the aggregation root is akin to branches and roots of a tree. Figure 3-3 illustrates the internal structure of an aggregation.

file

The benefit of this ordered structure is that only the "root" can reference or point to other objects, and the "root" itself cannot be referenced by any other object; the "root" is analogous to a group leader in a team, to whom team members must report. This is the source of the aggregate root design, which possesses ownership of data within its boundary and management authority over behavioral responsibilities.

file

Aggregation Design Example:
https://weread.qq.com/web/reader/95932e2072052ac7959169dk98f3284021498f137082c2e

痴者工良

高级程序员劝退师

文章评论