Chapter 3, Strategic Patterns
As you learned in previous chapters, the biggest challenge at the start of any project—whether it’s a greenfield project or a refactoring effort—is the language. By now, the importance of understanding the domain and defining a shared language to build knowledge should be evident. However, creating a ubiquitous language comes with hidden challenges, often stemming from human behavior.
如前几章所述,任何项目启动时最大的挑战——无论是全新项目还是重构工作——都是语言问题。此时,理解领域并定义构建知识的共同语言的重要性应当显而易见。然而,创建通用语言会带来隐藏的挑战,这些挑战往往源于人类行为。
Now that you know how to avoid these mistakes, you are ready to discover the patterns that domain-driven design gives us and learn how to use them during the refactoring of your system.
现在你已经知道如何避免这些错误,你已准备好发现领域驱动设计为我们提供的模式,并学习如何在系统重构过程中使用它们。
To do so, we’ll cover the following main topics in this chapter:
为此,本章将涵盖以下主要主题:
* Defining terms clearly 清晰定义术语
* Dividing the domain 划分领域
* Dealing with communication between bounded contexts
处理有界上下文之间的通信
Defining terms clearly will solve half of the problem
明确术语将解决一半问题
In his book, Evans makes clear the importance of using the ubiquitous language in conversation with domain experts, and with all team members. To ensure that the comprehension of the problem is clear and without ambiguity due to an incorrect translation from the domain language to the technical language, it is important that the language used in our code base is as close as possible to the domain model. That is because the product that we deploy in production uses the technical language, not the business language.
在书中, Evans 阐明了在与领域专家以及所有团队成员交流时使用通用语言的重要性。为了确保对问题的理解清晰且没有因领域语言到技术语言的错误翻译而产生的歧义,我们代码库中使用的语言应尽可能接近领域模型。这是因为我们部署到生产环境中的产品使用的是技术语言,而不是业务语言。
To achieve that, during the conversation between teammates, no one needs to translate terms from one language to another. Since the software does not cope with ambiguity, we should base the conversation on the domain model. We will explain the domain model concept better later in the What is a bounded context? section of this chapter.
为了实现这一点,在团队成员之间的交流中,不需要将一个语言的术语翻译成另一个语言。由于软件无法处理模糊性,我们应该基于领域模型进行交流。我们将在本章的“什么是限界上下文?”部分更详细地解释领域模型的概念。
Evans is uncompromising about this:
埃文斯在这个问题上毫不妥协:
“By using the model-based language pervasively and not being satisfied until it flows, we approach a model that is complete and comprehensible, made up of simple elements that combine to express complex ideas…
“通过广泛使用基于模型的语言,并且不满足于直到它流畅为止,我们接近一个完整且易于理解的模型,它由简单的元素组成,这些元素组合起来表达复杂的思想……
“…Domain experts should object to terms or structures that are awkward or inadequate to convey domain understanding; developers should watch for ambiguity or inconsistency that will trip up design”
“……领域专家应该反对那些表达领域理解时显得笨拙或不充分的术语或结构;开发者应该警惕会导致设计陷阱的模糊性或不一致性”
The ubiquitous language is not just a vocabulary; it is a rigorous language between all teammates who aim to remove all ambiguity and create a base to identify the boundaries around the business problem they have to solve.
通用语言不仅仅是一套词汇;它是所有致力于消除所有模糊性并创建一个识别业务问题边界基础的所有团队成员之间的一种严谨语言。
During refactoring, it’s important to understand the model underlying the software we are working on before starting to modify the code. Having a common model of the business that every teammate can understand is the key to obtaining a clear architecture for the solution. It helps us to clearly identify the boundaries around the business problem, and having a common language is the first step. Try to think about a customer model; it seems easy to create a model of our customer, just including all properties such as invoice data, personal data, delivery addresses, headquarters addresses, and so on. We would then have a complete model of our customer. Most of the software that you are working on probably has a model similar to this one, and this is the primary pain point when you have to modify the behavior of the program or implement a new feature.
在重构过程中,开始修改代码之前,理解我们正在处理的软件背后的模型非常重要。让每个团队成员都能理解的一个共同业务模型是获得解决方案清晰架构的关键。它帮助我们清晰地识别业务问题的边界,而拥有共同语言是第一步。试着思考一下客户模型;创建我们的客户模型似乎很容易,只需包括所有属性,如发票数据、个人数据、送货地址、总部地址等。然后我们就会拥有一个完整的客户模型。你正在处理的绝大多数软件可能都有一个与此类似的模型,而当你必须修改程序行为或实现新功能时,这就是主要的痛点。
Figure 3.1 shows the telephone game pattern in a real business situation. A domain expert explains their problem to an analyst, who explains the same (are you sure?) problem to an architect, and so on until it reaches the development team. They create a solution that will be deployed. Can you see the problem? Any person involved in this pattern has their own interpretation of the problem, which is different from that of the previous person in the line.
图 3.1 展示了在真实商业场景中的电话游戏模式。领域专家向分析师解释他们的问题,分析师又向架构师解释相同的问题(你确定吗?),如此传递下去直到开发团队。他们创建一个解决方案并部署。你能看出问题所在吗?这个模式中的每个人都对问题有自己的理解,这与链中前一个人的理解不同。
How can you avoid this mistake? The first and simplest solution could be to remove the line between business experts and developers and get these people to communicate directly. Unfortunately, they use different languages, and this type of communication is impossible.
你如何避免这个错误?第一个也是最简单的解决方案可能是消除业务专家和开发者之间的界限,并让这些人直接沟通。不幸的是,他们使用不同的语言,这种类型的沟通是不可能的。
Figure 3.2 explains this solution. Domain experts and the development team talk among themselves directly, without any mediator. In this case, the problem isn’t the loss of information, but the language. Each of them uses a different language and cannot understand what the other says.
图 3.2 解释了这个解决方案。领域专家和开发团队直接相互交流,没有任何中介。在这种情况下,问题不是信息丢失,而是语言。他们各自使用不同的语言,无法理解对方所说的话。
Finally, you can see the solution to the problem in Figure 3.3. Using a ubiquitous language, each member of the team understands the other, and together they can build a domain model that’s helpful for everyone.
最后,你可以在图 3.3 中看到问题的解决方案。使用通用语言,团队成员之间能够相互理解,并一起构建对每个人有帮助的领域模型。
Figure 3.3 – Common domain model
Having a model aligned with the business simplifies the refactor because now you and your business experts can understand each other without the need for translations or interpreters, and every new request that arrives will be perfectly aligned with your model. In addition, if the new request involves changing the model, you can rest easy knowing that changing the model will not affect the other functionalities of the system.
拥有与业务一致的模型可以简化重构,因为现在你可以和你的业务专家无需翻译或中介就能相互理解,并且每个新到达的请求都将与你的模型完美契合。此外,如果新请求涉及改变模型,你可以放心,因为改变模型不会影响系统的其他功能。
However, why do you need to create many models, and not just one, for your whole business? If the goal is creating software that can evolve, you need to create many small models, each of which is isolated from the other one. This is what bounded contexts are responsible for.
然而,为什么您需要为整个业务创建许多模型,而不仅仅是一个呢?如果目标是创建能够演进的软件,您需要创建许多小的模型,每个模型都与其他模型相互隔离。这就是限界上下文所负责的。
What is a bounded context?
什么是限界上下文?
A bounded context is a fundamental pattern of domain-driven design. It is the focus of strategic patterns and a central concept when you deal with large models because it allows you to split a big problem into smaller problems or a large model into smaller models.
限界上下文是领域驱动设计的一个基本模式。它是战略模式的核心,当你处理大型模型时也是一个中心概念,因为它能让你将一个大问题分解为小问题,或把一个大模型分解为小模型。
Having small models helps us solve business problems and maintain our code in the future. To identify the boundaries around this fine-grained model, you have to use the ubiquitous language discovered previously to identify the pivotal events that move the discussion around the business problem from one context to another.
拥有小模型有助于我们解决业务问题,并在未来维护代码。为了确定这个细粒度模型的边界,你必须使用先前发现的通用语言来识别那些将业务问题的讨论从一个上下文转移到另一个上下文的关键事件。
You have to keep in mind that the core of domain-driven design is about designing software based on a model, one that all the teammates can understand. When this model will not map the business flow anymore due to the increased complexity and the components involved in it, you are definitely moving outside of the model boundary. When you deal with the refactoring of an old project, it is important to identify these boundaries, because the first thing you have to do after discovering them is to remove the coupling between objects that belong to different bounded contexts. Removing these coupling enables you to modify part of the system without the fear of breaking others, and that is a fundamental step!
你必须牢记,领域驱动设计的核心是基于一个模型来设计软件,这个模型是所有团队成员都能理解的。当由于复杂性的增加和涉及到的组件增多,这个模型不再能够映射业务流程时,你肯定已经超出了模型的边界。在处理旧项目的重构时,识别这些边界非常重要,因为在发现它们之后,你首先要做的是消除属于不同限界上下文的对象之间的耦合。消除这些耦合使你能够修改系统的部分内容,而不用担心破坏其他部分,这是一个基本步骤!
Another element that you can use to draw boundaries between contexts is human behaviors. Back to the example of the sales order created, the behavior of the people involved in the payment system will be different from the people involved in the logistics department because they will have different tasks to perform. You have to capture these differences and use them to identify the boundaries of your contexts.
另一个可用于划分上下文界限的元素是人类行为。回到创建销售订单的例子,参与支付系统的人员的行为将与物流部门的人员不同,因为他们需要执行不同的任务。你必须捕捉这些差异,并利用它们来识别你的上下文界限。
Once you have split the whole domain into many different bounded contexts, you need to find how these contexts communicate with one another. Like in the real world, each part of the system contributes to the final success, and the only way to achieve this is collaboration.
一旦你已经将整个领域划分为许多不同的限界上下文,你需要找到这些上下文之间是如何相互通信的。就像在现实生活中,系统的每个部分都对最终的成功做出贡献,而实现这一点的唯一方式是协作。
Dividing the domain into meaningful boundaries
将领域划分为有意义的边界
The most obvious advantage of having a ubiquitous language is the ability to easily identify the boundaries around a business problem and split the domain into many subdomains. A business domain is the main area of activity of a company. In the example we will use in this book, the business domain is brewery management. The service that our hypothetical company provides is the production and selling of the best beer in the world! Easy.
使用通用语言的明显优势是能够轻松识别业务问题的边界并将领域划分为多个子域。业务领域是公司主要的活动区域。在本书中我们将使用的示例中,业务领域是酿酒厂管理。我们假设的公司提供的服务是全球最棒的啤酒的生产和销售!简单。
It is important to understand that the business domain is not a software application. The software provides the solution you create to help your customer manage their business. A business domain is generally big and complex. So, as you have already discovered, having just one model to describe the whole business is impossible, and it is probably the situation that you will find yourself in when approaching refactoring. You can see the problem of having a “one-size-fits-all” model. It is a constraint for every new request and creates a fear of changing anything in your code due to the strong coupling between the components of your legacy application.
重要的是要理解业务领域并不是一个软件应用。软件为你提供的解决方案是用来帮助客户管理他们的业务。业务领域通常很大且复杂。因此,正如你已经发现的,用一个模型来描述整个业务是不可能的,当你开始重构时,这很可能是你面临的情况。你可以看到使用“一刀切”模型的弊端。它对新请求施加了限制,并且由于遗留应用程序组件之间强耦合,导致你害怕更改代码中的任何内容。
As in the real world, a company achieves its business domain goals by splitting the business itself into multiple areas. Domain-driven design does the same by splitting the domain into multiple subdomains. A subdomain is a small part of the whole domain and focuses on a particular aspect of the business. It is an important strategic pattern within domain-driven design. Therefore, a single subdomain is not enough to guarantee the success of a company. It is a cog in a more complex system: the domain.
如同现实世界中公司通过将业务本身划分为多个领域来实现其业务领域目标一样,领域驱动设计也是通过将领域划分为多个子领域来实现这一目标。子领域是整个领域的一个小部分,专注于业务的某个特定方面。它是领域驱动设计中的一个重要战略模式。因此,单个子领域不足以保证公司的成功。它是更复杂系统——即领域——中的一个齿轮。
In a complex system, each gear is assembled so that the whole system can fulfill its task; not every gear is important in the same way. In the context of a wheel, if you break a spoke, you can continue pedaling and finish your ride. However, if you puncture the tire, you must stop your ride, repair the tire, and then finish your ride. Both the tire and spoke are part of the wheel, but the tire is more important than the spoke. Likewise, if the goal of your company is to produce the best beer, having a good warehouse system will be important to stock the beers before selling them, but less important than having a perfect production department. None of these departments on their own will make your brewery profitable; all of these, and probably others, are necessary to achieve the goal, but it is imperative to identify the responsibilities of each one.
在一个复杂系统中,每个齿轮被组装起来,以便整个系统能够完成其任务;并非每个齿轮都以相同的方式重要。在车轮的语境中,如果你弄断了一根辐条,你可以继续蹬车并完成骑行。然而,如果你扎破了轮胎,你必须停止骑行,修理轮胎,然后才能完成骑行。轮胎和辐条都是车轮的一部分,但轮胎比辐条更重要。同样地,如果贵公司的目标是酿造最好的啤酒,拥有一个良好的仓库系统对于在销售前储存啤酒很重要,但比拥有一个完美的生产部门要次要。这些部门中的任何一个单独存在都不会使你的啤酒厂盈利;所有这些,以及可能还有其他一些,都是实现目标所必需的,但必须明确每个部门的职责。
To identify the boundaries around these subdomains, you have to use the ubiquitous language discovered in the exploration phase with the domain experts and stakeholders. Any inconsistency in the ubiquitous language is a warning that something should be investigated more. That is because we could be talking about the same things but with different names, or we could be looking at a totally different thing that might belong to a different bounded context. You need to learn how to force your business experts to stay inside the problem space because you know how important this is. You need to identify whether you are talking about the customer for a sale order, or the customer for a delivery. You are certainly talking about a customer in either case, but in two different ways. That means having two different models, and, in this case, two different subdomains. You already know that a subdomain is a smaller area of knowledge or activity within a larger domain. Now you have also discovered that, as in the case of a cog or wheel, some subdomains are more important than others and there are different types of them. Splitting the whole domain into many subdomains means that you can create a smaller model and talk with specific domain experts to go deeply into the modeling. During the first part of the refactor, distilling a domain is extremely important to start with a good strategic design. The refactoring process is intended to improve the design, structure, and implementation of the software, as well as its non-functional attributes, without changing the functionalities.
要识别这些子域的边界,你必须使用在探索阶段与领域专家和利益相关者发现的通用语言。通用语言中的任何不一致都是需要进一步调查的警告。这是因为我们可能谈论的是同样的事情但使用不同的名称,或者我们可能关注的是完全不同的事情,这可能属于不同的限界上下文。你需要学会如何迫使你的业务专家保持在问题空间内,因为你知道这有多重要。你需要确定你是在谈论销售订单的客户,还是配送的客户。在任何一种情况下,你当然都在谈论客户,但方式不同。这意味着需要有两个不同的模型,在这种情况下,也有两个不同的子域。你已经知道子域是较大领域中一个较小的知识或活动区域。现在你也发现,就像齿轮一样,某些子域比其他子域更重要,并且它们有不同的类型。 将整个领域划分为多个子域意味着你可以创建一个更小的模型,并与特定领域的专家深入探讨建模问题。在重构的第一阶段,提炼领域至关重要,这有助于从良好的战略设计开始。重构过程旨在在不改变功能性的情况下,改进软件的设计、结构、实现及其非功能性属性。
Your goal is to reduce accidental complexity and improve code readability without side effects. Remember that you cannot transform a big ball of mud into a microservices solution in one step. Always keep Gall’s law in mind:
你的目标是减少偶然复杂度并提高代码可读性,而不产生副作用。记住,你不能一步将一团乱麻转变为微服务解决方案。始终牢记 Gall 定律:
“A complex system that works is invariably found to have evolved from a simple system that worked. The inverse proposition also appears to be true: a complex system designed from scratch never works and cannot be made to work. You have to start over, beginning with a working simple system.”
一个能够正常运行的复杂系统,往往是从一个能够运行的简单系统演变而来的。反命题似乎也成立:从头设计的复杂系统永远不会运行,也无法使其运行。你必须从头开始,从一个能够运行的简单系统着手。
Generally, refactoring involves implementing a set of standardized small-scale modifications where each change is a minor adjustment to the source code of a computer program. These adjustments typically maintain the software’s existing functionality or, at the very least, do not alter its adherence to functional specifications. The author of Refactoring To Patterns, Joshua Kerievsky, wrote the following:
通常,重构涉及实施一系列标准化的、小规模的修改,其中每次变更是对计算机程序源代码的微小调整。这些调整通常保持软件的现有功能,或者至少不会改变其遵循功能规范的情况。重构到模式(Refactoring To Patterns)的作者约书亚·凯里夫斯凯(Joshua Kerievsky)写道:
“By continuously improving the design of code, we make it easier and easier to work with. This is in sharp contrast to what typically happens: little refactoring and a great deal of attention paid to expediently add new features. If you get into the hygienic habit of refactoring continuously, you’ll find that it is easier to extend and maintain code.”
通过不断改进代码设计,我们让代码越来越易于使用。这与通常发生的情况形成鲜明对比:重构很少进行,而大量注意力都集中在快速添加新功能上。如果你养成持续重构的卫生习惯,你会发现扩展和维护代码会更容易。
Splitting the whole domain into many subdomains is the first step of this process and is called context mapping.
将整个领域划分为多个子域是此过程的第一步,称为上下文映射。
It’s important to clarify that dividing a domain into multiple subdomains doesn’t necessarily require creating multiple autonomous services. The implementation could take the form of a modular monolith or a microservices-based solution. What truly matters is ensuring that a bounded context remains self-contained and does not span across multiple services or modules.
重要的是要明确,将领域划分为多个子域并不一定需要创建多个自治服务。实现形式可以是模块化单体或基于微服务的解决方案。真正重要的是确保一个限界上下文保持自包含,并且不跨越多个服务或模块。
Context mapping 上下文映射
Context mapping is a technique used to visualize and understand the relationships and interactions between bounded contexts within a system. It involves creating a map that highlights how contexts relate to each other, identifies integration points, and exposes potential issues. This map becomes a critical tool in managing the complexity of large systems.
上下文映射是一种用于可视化和理解系统内限界上下文之间关系和交互的技术。它涉及创建一张地图,突出显示上下文如何相互关联,识别集成点,并揭示潜在问题。这张地图成为管理大型系统复杂性的关键工具。
Drawing this map is not an easy task, however, and you should follow a series of steps to execute in a loop until you and your team reach a consensus on the outcome.
绘制这张地图并非易事,然而,你应该遵循一系列步骤,并在一个循环中执行,直到你和你的团队就结果达成共识。
As a first step, identify the bounded contexts within your system. Each context should have a clear boundary and a specific domain model. Once you have your boundaries, you can move on to the next step, which could seem daunting at first. It involves defining how these contexts interact with each other. Are they dependent on each other? Do they share data or services?
第一步是识别系统中的边界上下文。每个上下文都应该有明确的边界和特定的领域模型。一旦你确定了边界,就可以进入下一步,这最初可能看起来令人望而生畏。它涉及定义这些上下文如何相互交互。它们是否相互依赖?它们是否共享数据或服务?
Once you overcome this task with a reasonable, understandable web of connections, you should try to highlight the points where contexts integrate or need to communicate.
一旦你通过一个合理且易于理解的连接网络克服了这项任务,你应该尝试突出上下文集成或需要通信的点。
Lastly, you should be able to draw a visual representation of the contexts and their relationships. At the end of this chapter, you will end up with a complete diagram that illustrates the flow of information and its dependencies, as shown in Figure 3.12. However, before going straight to it, let’s break it up into smaller steps so as to fully understand context mapping.
最后,你应该能够绘制出上下文及其关系的视觉表示。在本章结束时,你将得到一个完整的图表,它说明了信息的流动及其依赖关系,如图 3.12 所示。然而,在直接进入它之前,让我们将其分解为更小的步骤,以便充分理解上下文映射。
Firstly, Figure 3.4 shows how we could draw the context map of our ERP using the bounded contexts of customer, sales, warehouse, and shipping.
首先,图 3.4 展示了我们如何使用客户、销售、仓库和运输的限界上下文来绘制 ERP 的上下文地图。
Types of subdomains 子域类型
In our list of tasks to achieve a good mapping, the first step is to define specific domain models. However, what does that mean?
在我们的任务列表中,为了实现良好的映射,第一步是定义具体的领域模型。然而,这意味着什么呢?
It means that every bounded context fills a specific role in your architecture, and you have to determine the importance of each one. This involves identifying which bounded contexts represent your core business logic, which ones support that core, and which ones are more generic in nature. By doing this, you can prioritize your efforts and resources effectively.
这意味着每个限界上下文都在你的架构中扮演着特定的角色,你必须确定每个上下文的重要性。这包括识别哪些限界上下文代表你的核心业务逻辑,哪些支持这些核心逻辑,以及哪些更具有通用性质。通过这样做,你可以有效地优先考虑你的努力和资源。
To achieve this, you need to analyze each bounded context and categorize it into one of the various subdomain types. This categorization helps you understand the strategic significance of each part of your system. Subdomains represent specific areas within a domain. They correspond to individual bounded contexts with their own set of rules and logic.
要实现这一点,你需要分析每个限界上下文,并将其归类为各种子域类型中的一种。这种分类有助于你理解系统每个部分的战略意义。子域代表领域内的特定区域。它们对应具有自己规则和逻辑的独立限界上下文。
In context mapping, there are several types of subdomains you can choose from based on their role in your system. These are as follows:
在上下文映射中,你可以根据它们在系统中的角色选择几种子域。具体如下:
* Core subdomain 核心子域
* Supporting subdomain 支持子域
* Generic subdomain 通用子域
By identifying which type each of your bounded contexts falls into, you can make informed decisions about how to approach their development, integration, and maintenance. For instance, you would likely invest more resources and innovation into your core domain, while perhaps looking for off-the-shelf solutions for generic domains.
通过识别每个限界上下文属于哪种类型,你可以就如何处理它们的开发、集成和维护做出明智的决策。例如,你可能会在核心域上投入更多资源和创新,而对于通用域则可能寻找现成的解决方案。
This process of identification and categorization is crucial as it forms the foundation of your context map, guiding your overall architectural strategy and helping to align your technical efforts with your business priorities. Also, it deeply impacts how you structure your teams and the members within each one. For example, the team working on the core domain will probably be composed of the more experienced developers at first and maybe, at a later point, you will move some of them to other teams to share knowledge.
这种识别和分类的过程至关重要,因为它构成了你上下文地图的基础,指导你的整体架构策略,并帮助你将技术工作与业务优先级相匹配。此外,它还深刻影响你如何组织团队以及每个团队中的成员。例如,负责核心领域的团队最初可能由经验更丰富的开发人员组成,而稍后,你可能会将其中一些人调到其他团队以分享知识。
Now that you understand context maps, let us see what exactly they are and what purposes they serve.
现在你已经了解了上下文映射,让我们来看看它们具体是什么以及它们的作用是什么。
Core subdomain 核心子域
The core subdomain is the heart of your business. It is where your company’s unique value proposition lies and where you have a competitive advantage. This is the most critical and complex part of your system, requiring the most attention and resources. For our ERP, the sales system is the core domain, as it is central to the business’ operations and success.
核心子域是您业务的中心。它体现了您公司独特的价值主张,也是您拥有竞争优势的地方。这是您系统中最关键和最复杂的部分,需要最多的关注和资源。对于我们 ERP 系统来说,销售系统是核心领域,因为它对业务运营和成功至关重要。
Supporting subdomain 支持子域
Supporting subdomains, while important, are not the primary focus of the business. They complement and enable the core subdomain but don’t provide the main competitive advantage. These subdomains are necessary for the business to function smoothly but aren’t where the company differentiates itself. In our ERP example, a customer service module would be a supporting subdomain – it’s crucial for customer satisfaction but not the main business driver.
支持子域虽然重要,但并非业务的主要焦点。它们补充并支持核心子域,但并不提供主要的竞争优势。这些子域对于业务的顺利运行是必要的,但并不是公司实现差异化的地方。在我们的 ERP 示例中,客户服务模块就是一个支持子域——它对于客户满意度至关重要,但不是主要的业务驱动因素。
Generic subdomain 通用子域
Generic subdomains are common across many businesses and aren’t specific to your particular enterprise. These are often areas where reinventing the wheel isn’t necessary, and you can leverage existing solutions or outsource them. The invoice system is a good example of a generic subdomain – it is essential for monitoring revenue, but it’s not where a brewery would focus its innovative efforts.
通用子域在许多企业中都很常见,并且并非特定于你的企业。这些通常是无需重新发明轮子的领域,你可以利用现有解决方案或外包。发票系统就是一个通用子域的例子——它对于监控收入至关重要,但啤酒厂不会将创新的重点放在这里。
Figure 3.5 shows a context map with the definition of every subdomain.
Understanding these different types of domains is crucial for effective context mapping and overall system design. It helps in prioritizing development efforts, allocating resources efficiently, and making strategic decisions about system integration and evolution. By recognizing which parts of your system fall into which subdomain categories, you can focus your innovative efforts where they matter most while finding efficient solutions for less critical areas.
理解这些不同类型的子域对于有效的上下文映射和整体系统设计至关重要。它有助于确定开发工作的优先级,高效分配资源,并就系统集成和演进做出战略决策。通过识别系统哪些部分属于哪些子域类别,你可以在最关键的地方集中创新努力,同时在不太重要的领域找到高效的解决方案。
Subdomains change over time
子域会随时间变化
What we designed in this chapter is the first context map based on the knowledge acquired during the conversations with the brewery’s business experts. It tries to satisfy the business’ actual needs. Over time, the knowledge of the domain will improve, and the business flows will change with or without requests from the business experts. So, our bounded contexts or their importance in the domain could change accordingly. Last but not least, if you worked with your teams’ rotations, every member should be capable of taking on almost any subdomains.
本章我们设计的是基于与啤酒厂业务专家对话期间所获得的知识的第一张上下文地图。它试图满足业务的实际需求。随着时间的推移,领域知识会不断改进,业务流程也可能会随着业务专家的请求或不请求而发生变化。因此,我们的边界上下文或其在领域中的重要性可能会相应地发生变化。最后但同样重要的是,如果你与团队进行了轮岗,那么每个成员都应该能够承担几乎所有子领域的工作。
Dealing with communication between bounded contexts
处理有界上下文之间的通信
Now that you have a solid understanding of the importance of well-defined boundaries, we can move on to the last aspect of bounded contexts: communication. To build a cohesive system, these bounded contexts must interact effectively. That is why proper communication between bounded contexts is crucial. Strategic design offers some patterns to help you identify the best solutions based on our context of operation. Your goal here is to have a clear picture of how different parts of the system interact and relate with each other.
现在你已经对良好定义的边界的重要性有了扎实的理解,我们可以继续探讨有界上下文的最后一个方面:通信。为了构建一个协调的系统,这些有界上下文必须有效互动。这就是为什么有界上下文之间的适当通信至关重要。战略设计提供了一些模式,帮助你根据我们的操作环境识别最佳解决方案。你的目标是在这里清晰地了解系统不同部分如何互动和相互关联。
Importance of correct communication
正确沟通的重要性
Effective communication between bounded contexts ensures that the system functions as a unified whole. When bounded contexts fail to communicate correctly, inconsistencies and integration issues arise, leading to a fragmented system. Proper communication mechanisms help in the following ways:
限界上下文之间的有效沟通确保系统能够作为一个整体运行。当限界上下文无法正确沟通时,会出现不一致和集成问题,导致系统碎片化。正确的沟通机制有助于以下方面:
* Maintaining consistency: Ensuring that data and behavior are consistent across the system
保持一致性:确保数据和行为在整个系统中保持一致
* Enhancing flexibility: Allowing bounded contexts to evolve independently without breaking the system
增强灵活性:允许限界上下文独立演进而不破坏系统
* Reducing coupling: Minimizing dependencies between different parts of the system, making it easier to manage and refactor
减少耦合:最小化系统不同部分之间的依赖关系,使其更易于管理和重构
* Improving clarity: Providing clear interaction patterns, helping in understanding and maintaining the system
提高清晰度:提供清晰的交互模式,有助于理解和维护系统
Communication patterns 通信模式
These strategic patterns are high-level design strategies used to address and manage the relationships between bounded contexts. They provide a blueprint for handling integration, collaboration, and governance across contexts. Some of the most relevant include the following:
这些战略模式是用于处理和管理限界上下文之间关系的高级设计策略。它们为跨上下文处理集成、协作和治理提供了蓝图。其中一些最相关的包括以下:
* Shared kernel 共享内核
* Customer-supplier 客户-供应商
* Conformist 遵从者
* Anticorruption Layer (ACL)
反腐败层 (ACL)
* Open Host Service (OHS) 开放主机服务 (OHS)
* Published language 出版语言
* Separate ways 分道扬镳
Shared kernel 共享内核
In the shared kernel pattern, two or more bounded contexts share a portion of the same domain model. This shared part is managed collaboratively by the teams responsible for each context. This pattern is useful when contexts have strong interdependency and require a common understanding of specific aspects of the model.
在共享内核模式中,两个或多个限界上下文共享部分相同的领域模型。这个共享部分由负责每个上下文的团队共同管理。当上下文之间有强相互依赖性,并且需要模型特定方面的共同理解时,这种模式很有用。
Using our ERP as an example, consider the warehouse and sales contexts that share common details about the beer and stock levels. This shared kernel ensures that both contexts have a consistent understanding of product data, reducing the risk of discrepancies. The warehouse context manages stock levels, beer details, and warehouse locations, while the sales context handles customer orders, order processing, and fulfillment.
以我们的 ERP 系统为例,考虑仓库和销售这两个共享啤酒和库存水平等共同信息的上下文。这个共享内核确保两个上下文对产品数据有统一的理解,从而降低差异风险。仓库上下文管理库存水平、啤酒详情和仓库位置,而销售上下文处理客户订单、订单处理和交付。
The shared kernel in this scenario includes details about the beer such as SKU, description, and price, which are critical for both contexts to function correctly.
在这个场景中,共享内核包括啤酒的详细信息,如 SKU、描述和价格,这些信息对两个上下文都能正常运行至关重要。
Figure 3.6 – Shared kernel example
图 3.6 - 共享内核示例
Customer-supplier 客户-供应商
The customer-supplier pattern establishes a clear upstream-downstream relationship between contexts. The upstream context provides services or data, while the downstream context depends on those services. The supplier (upstream) must ensure that the customer (downstream) can rely on its services, fostering a collaborative relationship.
供应商-客户模式在上下文之间建立了明确的上下游关系。上游上下文提供服务或数据,而下游上下文依赖这些服务。供应商(上游)必须确保客户(下游)能够依赖其服务,从而建立合作关系。
For instance, the customer context (supplier) provides customer information to the sales context (customer). The sales context depends on accurate customer data to manage orders effectively. The sales context manages orders and payment processing, while the customer context handles plafonds and special categorization. The customer context must ensure that customer information is accurate and available in a timely manner for the sales context to function correctly.
例如,客户上下文(供应商)向销售上下文(客户)提供客户信息。销售上下文依赖于准确的客户数据来有效管理订单。销售上下文管理订单和支付处理,而客户上下文处理限额和特殊分类。客户上下文必须确保客户信息准确并及时提供给销售上下文,以使其正常运行。
Figure 3.7 – Customer-supplier example
图 3.7 – 供应商-客户示例
Conformist 遵从者
In the conformist pattern, the downstream context (conformist) adopts the model and communication protocols of the upstream context without influencing it. This pattern is used when the downstream context has little control over the upstream context and must conform to its way of working.
在遵从者模式中,下游上下文(遵从者)采用上游上下文的模型和通信协议,而不影响其。当下游上下文对上游上下文控制力较弱,必须遵循其工作方式时,使用此模式。
For example, a third-party payment processing service dictates the data format and interaction protocols, and the internal payments context must conform to these requirements. The payment processing service (external) provides payment gateways, transaction processing, and fraud detection, while the internal payment context manages internal payment flows, user payment data, and transaction records. The internal payment context must adapt to the protocols and data formats specified by the external payment processing service to ensure seamless integration.
例如,第三方支付处理服务规定了数据格式和交互协议,内部支付上下文必须遵守这些要求。支付处理服务(外部)提供支付网关、交易处理和欺诈检测,而内部支付上下文管理内部支付流程、用户支付数据和交易记录。内部支付上下文必须适应外部支付处理服务指定的协议和数据格式,以确保无缝集成。
ACL
The ACL pattern introduces a layer that translates and adapts between the models of two bounded contexts. This layer prevents the downstream context from being corrupted by the upstream context’s model, allowing each context to maintain its integrity.
ACL 模式引入了一层翻译和适配层,用于在两个有界上下文的模型之间进行转换。这一层防止下游上下文被上游上下文的模型污染,允许每个上下文保持其完整性。
In our ERP platform, the sales context might use an ACL to translate data from the legacy CRM system into its own model. This ensures that the sales context is not polluted by the outdated CRM model. The legacy CRM system manages customer relationships, historical data, and sales interactions, while the sales context handles current sales processes, customer interactions, and order management. The ACL translates CRM data into the format and structure required by the sales context, ensuring data integrity and consistency.
在我们的 ERP 平台中,销售上下文可能会使用访问控制列表(ACL)将遗留 CRM 系统的数据翻译成自己的模型。这确保了销售上下文不会被过时的 CRM 模型污染。遗留 CRM 系统管理客户关系、历史数据和销售互动,而销售上下文处理当前的销售流程、客户互动和订单管理。ACL 将 CRM 数据翻译成销售上下文所需的格式和结构,确保数据完整性和一致性。
Figure 3.9 – ACL example
图 3.9 – ACL 示例
OHS
The OHS pattern involves exposing a bounded context’s capabilities through a well-defined service interface, making it an open host for other contexts to interact with. This pattern promotes loose coupling and clear contracts between contexts.
OHS 模式通过一个定义良好的服务接口暴露有界上下文的能力,使其成为其他上下文交互的开放主机。这种模式促进了上下文之间的松散耦合和清晰的契约。
For instance, the shipping context might provide an OHS that allows other contexts, such as sales and warehouse, to request shipping services through a standard API. The shipping context manages shipping rates, carriers, and tracking information. The sales context requests shipping services for order fulfillment, and the warehouse context requests shipping information for stock transfers and replenishments. The shipping context exposes its services via an API, enabling other contexts to interact with it without tight coupling.
例如,在运输上下文中,可能会提供一个 OHS,允许其他上下文(如销售和仓库)通过标准 API 请求运输服务。运输上下文管理运输费率、承运商和跟踪信息。销售上下文为订单履行请求运输服务,仓库上下文为库存转移和补货请求运输信息。运输上下文通过 API 公开其服务,使其他上下文能够与其交互而不存在紧密耦合。
Figure 3.10 – OHS example
图 3.10 – OHS 示例
Published language 已发布的语言
The published language pattern entails defining a shared language (a set of common terms and data structures) that multiple bounded contexts use for communication. This shared language reduces misunderstandings and integration complexity.
已发布的语言模式涉及定义一个共享语言(一组通用术语和数据结构),多个限界上下文使用它进行通信。这种共享语言减少了误解和集成复杂性。
In a microservices architecture, a common event schema might be used for inter-service communication. This ensures that all services understand the event data consistently. For example, the sales context publishes order events when new orders are placed. The warehouse context consumes order events to update stock levels, and the invoice context consumes order events to generate invoices. The published language defines a common schema for order events, ensuring that all services can interpret the data correctly.
在微服务架构中,通常会使用一个通用的事件模式来进行服务间通信。这确保了所有服务都能一致地理解事件数据。例如,销售上下文在创建新订单时发布订单事件。仓库上下文消费订单事件来更新库存水平,而发票上下文消费订单事件来生成发票。发布语言定义了一个订单事件的通用模式,确保所有服务都能正确地解释数据。
Separate ways 分离方式
The separate ways pattern addresses the complexity that arises when two distinct parts of a system need to evolve independently. This pattern involves identifying bounded contexts within the domain that have minimal interaction and can be developed and maintained separately. By explicitly separating these contexts, teams can focus on the specific needs and rules of each domain area without being hindered by the intricacies of the others. This separation reduces coupling and allows for more agile and responsive development cycles, enabling each context to evolve at its own pace and in its own direction.
分离方式模式解决了系统两个不同部分需要独立演进时产生的复杂性。这种模式涉及在领域中识别具有最小交互的边界上下文,这些上下文可以独立开发和维护。通过明确分离这些上下文,团队可以专注于每个领域特定需求和规则,而不会被其他领域的复杂性所阻碍。这种分离减少了耦合,并允许更敏捷和响应迅速的开发周期,使每个上下文能够以自己的节奏和方向演进。
Final context map 最终上下文图
In Figure 3.12, you can see the complete context map of our ERP, describing the communication patterns between each bounded context. You can start with a pattern, such as conformist, and then change to OHS because you need to maintain a high level of separation between bounded contexts. When you need to communicate with external systems, you can choose to be conformist with them or to use an ACL to maintain your language inside your domain model. You are not forced to use just one of them; you have to use the right tool in the right place.
在图 3.12 中,你可以看到我们 ERP 的完整上下文图,描述了每个限界上下文之间的通信模式。你可以从一个模式开始,比如遵从型,然后切换到 OHS,因为你需要保持限界上下文之间的高度分离。当你需要与外部系统通信时,你可以选择遵从它们,或者使用 ACL 来在领域模型内部保持你的语言。你不必只使用其中一种;你必须在正确的位置使用正确的工具。
Figure 3.12 – Complete context map
图 3.12 – 完整的上下文图
Refactoring complex systems with domain-driven design principles requires careful consideration of how bounded contexts communicate. Proper communication ensures consistency, flexibility, and clarity, enabling the system to function cohesively. By leveraging strategic patterns such as shared kernel, customer-supplier, conformist, ACL, OHS, and published language, teams can manage the relationships and interactions between bounded contexts effectively.
使用领域驱动设计原则重构复杂系统需要仔细考虑有界上下文之间的通信方式。适当的通信确保一致性、灵活性和清晰性,使系统能够协同运作。通过利用共享内核、客户-供应商、遵从者、访问控制列表、操作手册和发布语言等策略性模式,团队可以有效地管理有界上下文之间的关系和交互。
Summary 摘要
In this chapter, you delved deep into the strategic patterns of domain-driven design, discovering how vital it is to establish a ubiquitous language to eliminate ambiguity and ensure clear communication among all team members.
在本章中,你深入探索了领域驱动设计的策略性模式,发现建立通用语言对于消除歧义和确保所有团队成员之间清晰沟通至关重要。
You learned that aligning the language used in your code with the domain models helps create a clear and comprehensible architecture, simplifying modifications and reducing errors. You learned how splitting large models into smaller, manageable bounded contexts is crucial for maintaining a system that can evolve without fear of unintended consequences. This approach allows you to isolate different business areas, making it easier to modify parts of your system without disrupting others. Finally, you were also introduced to several strategic patterns that help manage the relationships and interactions between these contexts. These include the shared kernel, customer-supplier, conformist, ACL, PHS, and published language patterns.
你了解到,将代码中使用的语言与领域模型保持一致有助于创建清晰易懂的架构,简化修改并减少错误。你学习了如何将大型模型拆分为更小、更易于管理的边界上下文,这对于维护一个能够无惧意外后果而演进的系统至关重要。这种方法使你能够隔离不同的业务领域,从而更容易地修改系统的部分内容而不影响其他部分。最后,你还接触到了几种帮助管理这些上下文之间关系和交互的战略模式。这些模式包括共享内核、客户-供应商、遵从者、访问控制列表(ACL)、渐进式发布(PHS)和发布语言模式。
Each of these patterns offers a blueprint for maintaining consistency, flexibility, and clarity across the system, ensuring it functions as a cohesive whole. Understanding these patterns equips you to identify the boundaries within your domain and effectively manage the complexity of large systems.
这些模式中的每一种都为在整个系统中保持一致性、灵活性和清晰性提供了蓝图,确保系统作为一个有机整体运行。理解这些模式使你能够识别领域内的边界,并有效地管理大型系统的复杂性。
In the next chapter, you will delve into tactical patterns, which will provide you with practical tools and techniques to implement the concepts you’ve learned so far.
在下一章中,你将深入战术模式,这将为你提供实用的工具和技术来实施你到目前为止学到的概念。