"Domain Driven Design DDD" core knowledge carding notes

This book is the second professional book I read after graduation. The first book I just read at work taught how to make concrete implementations more elegant at the code level. This book aims to improve abstraction capabilities, top-level design, and domain modeling ability. In the process of reading the book, the book answered many confusions in the previous work, such as how to define boundaries between teams and project modules? Is there a good way to get the project code out of an increasingly uncontrollable ending? In addition, more importantly, in the process of reading books, we should not limit our thinking on the content to the work itself. The ideas in it can completely guide how to model problems in the field of life and experience the novelty and beauty of life. There are many resonances in the book and a lot of content. Due to lack of work experience, I don’t have a deep understanding of it. At present, I still have little intake of the essence of this monument. I look forward to continuing to confirm and figure out the things in the book in the follow-up work and life, and to encourage everyone!

Part 1 Using the Domain Model

1. What is the role of models in domain-driven design?

  1. Model and design core interplay
  2. The model is the backbone of a common language used by all members of the team: thanks to the association between the model and the implementation, developers can use this language to discuss programs.
  3. Models are condensed knowledge

2. The core of the software?
At the heart of software is its ability to solve domain-related problems for users. All other features, no matter how important,
serve this basic purpose.
3. Model and implementation binding

If software is designed without concepts, then software is at best a mechanized product -
performing useful functions without explaining why it works.

If the entire program design or its core part does not correspond to the domain model, then the model is worthless and the correctness of the software is questionable. At the same time, the overly complex correspondence between the model and design functions is difficult to understand, and in
actual projects, it is impossible to maintain this relationship when the design changes. If there is a serious divergence between analysis and design, then the knowledge gained in the analysis and design activities cannot be shared with each other
.

The design of each part of the software system should faithfully reflect the domain model in order to reflect the clear correspondence between the two. Models should be iteratively checked and modified so that software implements them more naturally, even when trying to reflect deeper domain concepts. The model we need should not only meet these two requirements, but also support a robust UBIQUITOUS LANGUAGE (universal language). Terminology for program design and assignment of basic responsibilities is derived from the model. Let the program code be the expression of the model, and changes in the code may be changes in the model. And its impact is bound to affect the next corresponding project activities. Implementations that rely entirely on models typically require software development tools and languages ​​that support modeling paradigms, such as object-oriented programming.

4. Why are models important to users?

When a program is designed based on a model that reflects the fundamental concerns of users and domain experts, that design can convey its intent to users more clearly than could otherwise be done. Letting users understand the model will give them more opportunities
to exploit the potential of the software, and it will also make the behavior of the software reasonable and consistent.

  1. HANDS-ON MODELER (hands-on modeller)

If the people who write the code don't think they are responsible for the model, or don't know how to make the model serve the application, then the model has nothing to do with the program. If developers don't realize that changing the code means changing the model, their refactoring of the program will not only strengthen the model, but weaken it. Similarly, if the modeler does not participate in the process of program implementation, then the constraints on program implementation will not be felt personally, and even if they are, they will be quickly forgotten. One of the two basic elements of MODEL-DRIVEN DESIGN (that is, the model must support effective implementation and abstract key domain knowledge) has been lost, and the final model will become no longer practical. Finally, if the division of labor blocks the collaboration between designers and developers, so that they cannot convey the details of the realization of MODEL-DRIVEN DESIGN, experienced designers cannot pass on their knowledge and technology to developers.

Any technical person involved in modeling, regardless of their primary responsibility on the project, must take the time to understand the code. Anyone responsible for modifying the code must learn to express the model in code. Every developer must participate in model discussions to varying degrees and maintain contact with domain experts. People involved in different jobs must consciously exchange ideas about models with those who touch the code through UBIQUITOUS LANGUAGE in a timely manner.

Part II Building Blocks of Model-Driven Design

image.png

1. Why is there a layered architecture?

In object-oriented programs, support codes such as user interface and database access are often written directly in business objects. And some business logic will be embedded in user interface components and database scripts. This is done in order to complete the development work in the shortest possible way.
Viewing and analyzing domain code can become extremely difficult if domain-related code is scattered among a large number of other codes. Simple modifications to the user interface are likely to actually change the business logic, and adjustments to business rules are likely to require careful screening of user interface code, database operation code, or other program elements. This makes it less likely to achieve consistent, model-driven objects and makes automated testing difficult. Given the vast amount of logic and technology involved in the various activities in a program, the program itself must be simple and straightforward, or it will be incomprehensible.

Layer complex applications. Design within each layer separately so that it is cohesive and only depends on the layers below it. Adopt standard architectural patterns and only loosely couple with the upper layer. Put all domain model-related code in one layer and separate it from user interface, application, and infrastructure code. Domain objects should focus on how to express the domain model, without considering their own display and storage issues, and without managing application tasks, etc. This makes the model meaningful enough and structured enough to capture essential business knowledge and use it effectively.

2. THE SMART UI "anti-pattern"

If an inexperienced project team wants to complete a simple project but decides to use MODEL-DRIVEN DESIGN and LAYERED ARCHITECTURE, then the project team will go through a difficult learning process. Team members had to master complex new technologies and learn object modeling the hard way. (Even with the help of this book, this is still a challenging task!) The management of the infrastructure and layers makes an otherwise simple task take a long time to complete. Simple projects have shorter development cycles and lower expectations. So, the project would be canceled long before the project team could complete the task, let alone demonstrate the many exciting possibilities for this approach.
Even with more time on the project, team members are less likely to master these techniques without expert help. In the end, if they do manage to overcome these difficulties, they will probably only develop a simple system. Because this project does not need rich features.

therefore:

Implement all business logic in the user interface. Divide the application program into small functional modules, implement them into user interface respectively, and embed business rules in it. Use a relational database as a shared data repository. Use the most automated user interface creation tools and visual programming tools available.

3. The model represented in the software?

  1. Association : Every traversable association in the model must have the same attribute mechanism in the software.
  2. ENTITY : Some objects are not primarily defined by their attributes. They actually represent a "Thread of Identity" (A Thread of Identity), which spans time and often undergoes multiple different representations. Sometimes such an object must be matched with another object with different properties. And sometimes an object must be distinguished from another object with the same properties. Incorrect identification can corrupt data. Objects primarily defined by identity are called ENTITY
  3. VALUE OBJECT : An object used to describe an aspect of the domain without a conceptual identity is called a VALUE OBJECT (value object). After VALUEOBJECT is instantiated, it is used to represent some design elements. For these design elements, we only care about what they are, not
    who they are.
  4. SERVICE : Sometimes, an object is not a thing. In some cases, the clearest and most practical design will include special operations that don't conceptually belong to any object. Instead of forcing them into one category, it is better to introduce a new element in the model naturally, which is SERVICE (service).

When an important process or transformation operation in the domain is not the natural responsibility of ENTITY or VALUE OBJECT, an operation should be added to the model as an independent interface and declared as SERVICE. Use a model language when defining interfaces, and ensure that operation names are terms in the UBIQUITOUS LANGUAGE. Also, the SERVICE should be made stateless.

  1. MODULE: : MODULE provides two ways for people to observe the model, one is to view the details in the MODULE without being overwhelmed by the whole model, and the other is to observe the relationship between modules without considering its internal details. Modules describe the domain from a larger perspective.

Everyone uses modules, but few see them as a full-fledged part of the model. The code is broken down into various categories, sometimes according to the technical architecture, and sometimes according to the developer's task division. Even those developers who do a lot of refactoring tend to use some modules formed early in the project.
As we all know, there should be low coupling between modules, but high cohesion inside the module. Interpretations of coupling and cohesion make MODULE sound like a technical indicator, as if judging them mechanically based on the distribution of associations and interactions. However, MODULE is not just a division of code, but also a division of concepts. The things that a person considers at one time are limited (hence the need for low coupling). Incoherent thoughts are as difficult to understand as "one-pot porridge" thoughts (hence the high cohesion).

therefore:

Choose a module that can describe the system and make it contain a cohesive set of concepts. This usually results in low coupling between modules, but if this is not ideal, you should look for a way to change the model to remove the coupling between concepts, or find a concept that can be used as a basis for the module (this concept may have been overlooked before. ), modules organized around this concept can group elements together in a meaningful way. Find a loosely coupled way to organize concepts so that they can be understood and analyzed independently of each other. The model is refined until it can be partitioned according to high-level domain concepts without coupling the corresponding code.
The name of the module should be a term in UBIQUITOUS LANGUAGE. Modules and their names should reflect deep knowledge of the domain.

4. The life cycle of domain objects

image.png

1. AGGREGATE
We need to use an abstraction to encapsulate the references in the model. AGGREGATE is a collection of related objects, we
regard it as the unit of data modification. Each AGGREGATE has a root (root) and a boundary (boundary). Boundaries define what is inside the AGGREGATE. The root is a specific ENTITY included in the AGGREGATE. For AGGREGATE, external objects can only refer to the root, while objects inside the boundary can refer to each other. ENTITYs other than the root have local identities, but these identities only need to be distinguished inside the AGGREGATE, because external objects cannot see other objects than the root ENTITY.

We should gather ENTITY and VALUE OBJECT into AGGREGATE by category, and define the boundaries of each AGGREGATE. In each AGGREGATE, choose an ENTITY as the root, and control all access to other objects within the boundary through the root. Only outer objects are allowed to keep references to the root. Temporary references to internal members can be passed around, but only for one operation. Since the root controls access, it cannot be bypassed to modify internal objects. This design is beneficial to ensure that the objects in the AGGREGATE satisfy all the fixed rules, and also ensure that the AGGREGATE as a whole satisfies the fixed rules when any state changes.

2. FACTORY

Object creation can be a major operation in itself, but the objects being created are not well suited for complex assembly operations. Mixing these responsibilities can lead to poorly understood designs. Making the client directly responsible for creating objects messes up the client's design and breaks the encapsulation of the assembled object or AGGREGATE, and leads to too tight coupling between the client and the implementation of the object being created.

Complex object creation is the responsibility of the domain layer, however this task does not belong to the objects used to represent the model.

therefore:

The responsibility for creating instances of complex objects and AGGREGATE should be shifted to a separate object, which may not itself have responsibility in the domain model, but which is still part of the domain design. Provide an interface that encapsulates all complex assembly operations, and this interface does not require clients to refer to the concrete class of the object to be instantiated. Create the AGGREGATE as a whole and make sure it satisfies the fixed rules

3. REPOSITORY

The goal of Domain Driven Design is to create better software by focusing on the domain model rather than the technology. Suppose the developer constructs a SQL query and passes it to a certain query service in the infrastructure layer, and then extracts the required information according to the result set of table row data obtained, and finally passes the information to the constructor or FACTORY. When developers perform this series of operations, the model is no longer the focus. We naturally regard objects as containers to store the queried data, so the whole design turns to the data processing style. While the exact technical details are different, the problem remains - the client is dealing with the technology, not the model concept.

Clients need an efficient way to obtain references to existing domain objects. If the infrastructure provided this facility, developers could add a lot of traversable associations, which would clutter the model very much. On the other hand, developers may use queries to extract the data they need from the database, or to extract specific objects directly, rather than getting these objects through the root of the AGGREGATE. This results in domain logic going into query and client code, while ENTITY and VALUE OBJECT become mere data containers. Adopting most of the technical complexity of dealing with database access quickly clutters client code, which leads developers to simplify the domain layer and ultimately renders models irrelevant.

Random database queries can break domain object encapsulation and AGGREGATE. Exposure of technical infrastructure and database access mechanisms adds complexity to clients and prevents model-driven
design.

Only provide REPOSITORY for those AGGREGATE roots that really need direct access. Let customers always focus on the model, and leave all object storage and access operations to REPOSITORY.

5. How do you model which less obvious concepts?

  • Displayed constraints: abstract the details of the constraints separately, and then express the concept explicitly
  • Model the process as a domain object: Constraints and processes are two major types of model concepts. Objects are used to encapsulate processes, so that we only need to consider the business purpose or intent of the object. Typical is actually the strategy pattern we often use
  • SPECIFICATION: Encapsulate the rules, the interface expresses the rules explicitly, and facilitates the testing of the rules

The third part deepens understanding through refactoring

image.png

A lot of overengineering gets plausible in the name of flexibility. However, too many layers of abstraction and indirect design often become a stumbling block for projects. Look at software design that actually brings great power to the user, and you'll often find something simple. Simple is not easy to do.

Listen to the language spoken by domain experts. Are there terms that express complex concepts succinctly? Have they corrected your wording (maybe a tactful reminder)? Have they lost the bewilderment on their face when you use a particular word expressions? These all hint at a concept that might improve the model.

1. INTENTION-REVEALINGINTERFACES

If a developer has to study its implementation in order to use a component, then the value of encapsulation is lost. When an object or operation developed by someone is used by others, if the new developer who uses this component has to infer its use based on its implementation, then what he infers may not be the main purpose of that operation or class. If that's not what that component is for, the code works for the time being, but the conceptual basis of the design has been misused, and the intent of the two developers has diverged.

2. SIDE-EFFECT-FREE FUNCTION

The results of the interaction or combination of calculations of multiple rules are difficult to predict. When a developer calls an operation, in order to predict the result of the operation, he must understand its implementation and the implementation of other methods it calls. If the developer has to "lift the veil off the interface", the abstraction of the interface is limited. Without abstractions whose results can be safely foreseen, developers must limit the "combinatorial explosion"1, which limits the richness of the system's behavior.

Therefore :

Put the logic of the program in functions as much as possible, because functions are operations that only return results without obvious side effects. Strictly isolate commands (methods that cause obvious state changes) to very simple operations that do not return domain information. When you find a concept that is very suitable for taking on complex logic responsibilities, you can move this complex logic into a VALUE OBJECT, which can further control side effects.

3. ASSERTION

If the side effects of operations were only implicitly defined by their implementation, cause and effect would become a mess in a system with a large number of inter-call relationships. The only way to understand a program is to follow its execution along its branching paths. Encapsulation completely loses its value. Tracing concrete executions also makes abstraction meaningless.

Therefore :

Clearly express the post-conditions of the operation and the fixed rules of the class and AGGREGATE. If ASSERTIONs cannot be written directly in your programming language, write them as automated unit tests. They can also be written to documents or diagrams (if it fits the project development style).
Look for models that are conceptually cohesive so that it is easier for developers to reason about expected ASSERTIONs, thereby speeding up the learning process and avoiding code inconsistencies.

4. CONCEPTUAL CONTOUR

If all the elements of a model or design are placed in an overall larger structure, their functions will be duplicated. The external interface cannot give all the information that the client may care about. As different concepts are mixed together, their meaning becomes difficult to understand.
On the other hand, it may also be pointless to break up classes and methods, which will complicate the client and force the client object to understand how the various pieces fit together. Worse, some concepts may be lost entirely. Half of the uranium atoms are not uranium. Moreover, the size of the granularity is not the only issue to be considered, we also need to consider in which occasion the granularity is used.

Therefore :

Break down design elements (operations, interfaces, classes, and AGGREGATEs) into cohesive units, taking into account your intuitions about any important divisions in the domain. Observe the regularities that change and ensure stability during the continuous refactoring process, and look for the underlying CONCEPTUAL CONTOUR that can explain these changing patterns. Match the model to those consistent aspects of the domain that make the domain a useful body of knowledge

5. STANDALONE CLASS

Even within a module, the design becomes increasingly difficult to understand as dependencies are added. This burdened
our thinking, thereby limiting the design complexity developers could handle. Implicit concepts add
more burden than explicit references.

Low coupling is an essential element of object design. Keep coupling as low as possible. Extract all other irrelevant concepts out of the
object. This way the class becomes completely independent, which allows us to study and understand it individually. Each such independent class
greatly reduces the burden of understanding the module.

Try to extract the most complex calculations into a STANDALONE CLASS (independent class). One way to achieve this is

Model a VALUE OBJECT from a class that has a lot of dependencies.

Low coupling is the most basic way to reduce conceptual overload. Independent classes are the ultimate in low coupling.

6. CLOSURE OF OPERATION

Where appropriate, define an operation so that its return type is the same as the type of its arguments. If the implementer's state will be used in the computation, then the implementer is actually a parameter of the operation, so the parameters and return value should have the same type as the implementer. Such an operation is a closure operation on the collection of instances of that type. The closure operation provides a high-level interface without introducing any dependencies on other concepts.

This pattern is more commonly used for VALUE OBJECT operations.

7. Declarative Design

Usually refers to a programming method - writing a program or part of a program as an executable specification (specification). When using declarative design, the software is actually governed by some very precise property descriptions. It is a design concept and a working mode, and what is transmitted through it is the philosophy of "leave the convenience to others and the trouble to yourself"

Part IV Strategic Design

image.png

1. BOUNDEDCONTEXT
Multiple models will exist for any large project. And when code based on different models is combined, the software becomes buggy, unreliable, and difficult to understand. Communication among team members becomes chaotic. People often get confused in which context a model should not be used.
therefore:

Explicitly define the context in which the model applies. Set the boundaries of the model in terms of the organization of the team, the usage of the various parts of the software system, and the physical representation (code, database schema, etc.). Strictly maintain model consistency within these boundaries without being disturbed and confused by issues outside the boundaries.
2. CONTINUOUS INTEGRATION

CONTINUOUS INTEGRATION refers to merging all the work in a context together frequently enough and keeping them consistent so that when the model splits, problems can be spotted and corrected quickly. Like other methods in domain-driven design, CONTINUOUS INTEGRATION operates at two levels: (1) integration of model concepts; (2) integration of implementations.

Therefore :

Establish a process for frequently merging all code and other implementation artifacts together, and use automated testing to quickly pinpoint model splits. Strictly adhere to the use of UBIQUITOUS LANGUAGE so that when different concepts evolve in different people's minds, everyone can reach a consensus on the model.

3.CONTEXTMAP

image.png

Having only one BOUNDED CONTEXT does not provide a global view. The context of other models may still be unclear and changing.

People on other teams are not very aware of the boundaries of CONTEXT, and they will unknowingly make changes that blur the boundaries or complicate the interconnection. When different contexts must be connected to each other, they may overlap each other.

Therefore :

Identify each model that plays a role in the project, and define its BOUNDED CONTEXT. This includes implicit models of non-object-oriented subsystems. Name each BOUNDED CONTEXT and add the name to UBIQUITOUS LANGUAGE.

Describe the points of contact between the models, identify any transformations needed for communication, and highlight any shared content. First describe the current situation. Make changes later. **

As President Reagan famously said when negotiating nuclear arms reductions **“Trust, but confirm”**. This sentence gets to the heart of the bilateral affair—another metaphor for connecting contexts.

4. Mode: SHAREDKERNEL

Pick a subset of the domain model that both teams agree to share. Of course, in addition to this subset of the model, a subset of the code related to the model part, or a subset of the database design is also included. This section of explicitly shared content has a special status and should not be changed by one team without consulting the other. Functional systems need to be integrated frequently, but the frequency of integration should be lower than the frequency of CONTINUOUS INTEGRATION in the team
. As these integrations take place, both teams run tests.

5. CUSTOMER/SUPPLIERDEVELOPMENTTEAM

The travel team depends on the upstream team, but the upstream team is not responsible for the product delivery of the downstream team. It takes a lot of extra energy to figure out what to use to influence the opposing team, whether it's human nature, or time pressure, or something like that. Therefore, formalizing the relationship between teams makes it easier for everyone to work. In this way, the development process can be organized to handle the needs of both user groups in a balanced manner and to schedule work according to the features required downstream.

Therefore :

A clear customer/supplier relationship is established between the two teams. In planning meetings, the downstream team is the customer of the upstream team. Negotiate and budget for the tasks that need to be performed based on the needs of the downstream team so that everyone is aware of the commitment and progress of both parties.
Both teams work together to develop automated acceptance tests that verify the expected interfaces. Add these tests to the upstream team's test suite to run as part of their continuous integration. These tests allow the upstream team to make changes without worrying about side effects on the downstream team.

6. CONFORMIST

When two development teams have an upstream/downstream relationship, if the upstream team has no incentive to meet the needs of the downstream team, then the downstream team cannot do anything. Upstream developers may make promises out of altruism, but they may not keep them. Downstream teams, with good intentions, will believe these promises, making plans 361 based on features that will never materialize. Downstream projects can only be put on hold until the team finally learns to use existing conditions to fend for themselves. Downstream teams won't get an interface tailored to their needs.

If the quality of the upstream design is not bad, and the style is compatible, then it is better not to develop a separate model. In this case, the CONFORMIST (follower) mode can be used.

Therefore :

The complexity of converting between BOUNDED CONTEXTs can be removed by strictly following the upstream team's model. Although this restricts the downstream designer's style and may not result in an ideal application model, choosing the CONFORMITY pattern can greatly simplify integration. Additionally, this allows the UBIQUITOUS LANGUAGE to be shared with the supplier team. Vendors are in the driver's seat, so it's best to make communication easy. They share information with you from an altruistic standpoint.

7. ANTICORRUPTIONLAYER

image.png

Create an isolation layer to provide functionality to clients based on their own domain model. This layer talks to another system through its existing interface with little or no modification to that system. Internally, this layer performs the necessary bidirectional transformations between the two models.

8. SEPARATEWAY

Integration is always expensive, and sometimes the benefits are small.

therefore:

Declaring a BOUNDED CONTEXT that has no connection to other contexts enables developers to find simple, specialized solutions within this small scope .

9. OPEN HOST SERVICE

When a subsystem must be integrated with a large number of other systems, customizing a translation layer for each integration can slow down the team's analysis paralysis, which refers to the speed at which a project bogs down in the face of a large analysis effort. There will be more and more things to maintain, and more and more things to worry about when making changes

Therefore :

Define a protocol to treat your subsystem as a set of SERVICE for other systems to access. Open up the protocol so that anyone who needs to integrate with your subsystem can use it. This protocol is enhanced and extended as new integration needs arise, except for the special needs of individual teams. The solution to this particular need is to augment the protocol with one-off transformers in order to keep the shared protocol simple and cohesive.

10. PUBLISHEDLANGUAGE

slightly

Guess you like

Origin blog.csdn.net/lsgqjh/article/details/109381682