烂翻译系列之学习领域驱动设计——第十四章:微服务
In the mid-2010s, microservices took the software engineering industry by storm. The intent was to address modern systems’ need to change quickly, scale, and fit the distributed nature of cloud computing naturally. Many companies made the strategic decision to decompose their monolithic codebases in favor of the flexibility provided by the microservices-based architecture. Unfortunately, many such endeavors didn’t end well. Instead of flexible architectures, these companies ended up with distributed big balls of mud—designs that are much more fragile, clumpy, and expensive than the monoliths the companies wanted to break apart.
2010年中期,微服务在软件工程行业掀起了一场风暴。其目的是满足现代系统快速变化、扩展和自然适应云计算分布式特性的需求。许多公司做出了战略决策,为了微服务架构所提供的灵活性,而分解他们的单体代码库。不幸的是,许多这样的尝试并没有取得好的结果。这些公司最终得到的不是灵活的架构,而是分布式的“大泥球”——这些设计比他们想要分解的单体更加脆弱、笨拙和昂贵。
Historically, microservices are often associated with DDD, especially with the bounded context pattern. Many people even use the terms bounded context and microservices interchangeably. But are they really the same thing? This chapter explores the relationship between domain-driven design methodology and the microservices architectural pattern. You will learn the interplay between the patterns, and more importantly, how you can leverage DDD to design effective microservices-based systems.
从历史上看,微服务通常与领域驱动设计(DDD)相关联,尤其是与有界上下文模式相关联。许多人甚至将有界上下文和微服务这两个术语互换使用。但它们真的是同一回事吗?本章将探讨领域驱动设计方法与微服务架构模式之间的关系。你将了解这些模式之间的相互作用,更重要的是,你将学习如何利用领域驱动设计来设计有效的基于微服务的系统。
Let’s start with the basics and define what exactly are services and microservices.
让我们从基础开始,先定义一下什么是服务和微服务。
What Is a Service?
什么是服务?
According to OASIS, a service is a mechanism that enables access to one or more capabilities, where the access is provided using a prescribed interface. The prescribed interface is any mechanism for getting data in or out of a service. It can be synchronous, such as a request/response model, or asynchronous, such as a model that is producing and consuming events. This is the service public interface, as shown in Figure 14-1, which provides a means for communicating and integrating with other system components.
根据OASIS的定义,服务是一种机制,它能够通过规定的接口访问一个或多个功能。规定的接口是任何可以将数据传入或传出服务的机制。它可以是同步的,如请求/响应模型,也可以是异步的,如生产和消费事件的模型。这就是服务的公共接口,如图14-1所示,它提供了与其他系统组件进行通信和集成的方式。
Figure 14-1. Communication between services
图14-1. 服务之间的通信
Randy Shoup likens a service’s interface to a front door. All data going into or out of the service has to pass through the front door. Furthermore, a service’s public interface defines the service itself: the functionality exposed by the service. A well-expressed interface is enough to describe the functionality implemented by a service. For example, the public interface illustrated in Figure 14-2 explicitly describes the functionality of the service.
Randy Shoup将服务的接口比作前门。所有进出服务的数据都必须通过前门。此外,服务的公共接口定义了服务本身:即服务所公开的功能。一个表达良好的接口足以描述服务所实现的功能。例如,图14-2所示的公共接口明确描述了服务的功能。
Figure 14-2. A service’s public interface
图14-2. 服务的公共接口
This takes us to the definition of microservice.
这就引出了微服务的定义。
What Is a Microservice?
什么是微服务?
The definition of a microservice is surprisingly simple. Since a service is defined by its public interface, a microservice is a service with a micro-public interface: a micro-front door.
微服务的定义非常简单。由于服务是由其公共接口定义的,因此微服务就是具有微型公共接口(即微型前门)的服务。
Having a micro-public interface makes it easier to understand both the function of a single service and its integration with other system components. Reducing a service’s functionality also limits its reasons for change and makes the service more autonomous for development, management, and scale.
拥有微型公共接口使得理解单个服务的功能以及它与其他系统组件的集成变得更加容易。减少服务的功能也会限制其变更的原因,并使服务在开发、管理和扩展方面更加自主。
In addition, it explains the practice of microservices not exposing their databases. Exposing a database, making it a part of the service’s front door, would make its public interface huge. For example, how many different SQL queries can you execute on a relational database? Since SQL is quite a flexible language, the likely estimate would be infinity. Hence, microservices encapsulate their databases. The data can only be accessed through a much more compact, integration-oriented public interface.
此外,这也解释了微服务不暴露其数据库的做法。将数据库暴露出来,使其成为服务前门的一部分,会使公共接口变得巨大。例如,你可以在关系数据库上执行多少种不同的SQL查询?由于SQL是一种非常灵活的语言,可能的估计值是无穷大。因此,微服务会封装其数据库。数据只能通过更加紧凑、面向集成的公共接口进行访问。
Method as a Service: Perfect Microservices?
方法即服务: 完美的微服务?
Saying that a microservice is a micro-public interface is deceptively simple. It may sound as though limiting service interfaces to a single method would result in perfect microservices. Let’s see what will happen if we apply this naïve decomposition in practice.
说微服务是一个微型公共接口,这听起来简单得有些误导。它听起来好像将服务接口限制为单个方法就能得到完美的微服务。让我们来看看,如果我们在实践中应用这种天真的分解,会发生什么。
Consider the backlog management service in Figure 14-3. Its public interface consists of eight public methods, and we want to apply the “one method per service” rule.
思考图14-3中的待办事项管理服务。它的公共接口由八个公共方法组成,我们希望应用“每个服务一个方法”规则。
Figure 14-3. Naïve decomposition
图14-3 初始分解
Since these are well-behaved microservices, each encapsulates its database. No one service is allowed to access another service’s database directly; only through its public interface. But currently, there is no public interface for that. The services have to work together and synchronize the changes each service is applying. As a result, we need to expand the services’ interfaces to account for these integration-related concerns. Furthermore, when visualized, the integrations and data flow between the resultant services resemble a typical distributed big ball of mud, as shown in Figure 14-4.
由于这些都是表现良好的微服务,因此每个微服务都封装了自己的数据库。不允许一个服务直接访问另一个服务的数据库;只能通过其公共接口访问。但目前还没有这样的公共接口。服务必须协同工作,并同步每个服务所做的更改。因此,我们需要扩展服务的接口,以解决这些与集成相关的问题。此外,当可视化时,所得服务之间的集成和数据流类似于典型的分布式泥球架构,如图14-4所示。
Figure 14-4. Integration complexity
图 14-4. 集成复杂性
Paraphrasing Randy Shoup’s metaphor, by decomposing the system to such fine-grained services, we definitely minimized the services’ front doors. However, to implement the overarching system’s functionality, we had to add enormous “staff only” entrances to each service. Let’s see what we can learn from this example.
用Randy Shoup的比喻来说,通过将系统分解为如此细粒度的服务,我们确实将服务的前门最小化了。然而,为了实现整个系统的功能,我们不得不在每个服务上添加大量的“员工通道”入口。让我们来看看我们能从这个例子中学到什么。
Design Goal
设计目标
Following the simplistic decomposition heuristic of having each service expose only a single method proved to be suboptimal for many reasons. First, it’s simply not possible. Since the services have to work together, we were forced to expand their public interfaces with integration-related public methods. Second, we won the battle but lost the war. Each service ended up being much simpler than the original design, however the resultant system became orders of magnitude more complex.
遵循每个服务只暴露一个方法的简单分解启示被证明由于许多原因而并非最佳。首先,这根本不可能实现。由于服务必须协同工作,我们不得不通过添加与集成相关的公共方法来扩展它们的公共接口。其次,我们赢得了战役却输掉了战争。每个服务最终都比最初的设计简单得多,然而最终系统的复杂性却增加了几个数量级。
The goal of the microservices architecture is to produce a flexible system. Concentrating the design efforts on a single component, but ignoring its interactions with the rest of the system, goes against the very definition of a system:
微服务架构的目标是构建一个灵活的系统。将设计精力集中在单个组件上,却忽视其与系统中其他组件的交互,这违背了系统的定义:
- A set of connected things or devices that operate together 一组相互连接、共同运作的事物或设备
- A set of computer equipment and programs used together for a particular purpose 一组为了特定目的而共同使用的计算机设备和程序
Hence, a system cannot be built out of independent components. In a proper microservices-based system, however decoupled, the services still have to be integrated and communicate with each other. Let’s take a look at the interplay between the complexity of individual microservices and the complexity of the overarching system.
因此,系统不能由独立的组件构建而成。然而,在一个真正的基于微服务的系统中,无论服务如何解耦,它们仍然必须集成并彼此通信。让我们来看看单个微服务的复杂性与整个系统的复杂性之间的相互作用。
System Complexity
系统复杂性
Forty years ago, there was no cloud computing, there were no global-scale requirements, and there was no need to deploy a system every 11.7 seconds. But engineers still had to tame systems’ complexity. Even though the tools in those days were different, the challenges—and more importantly, the solution—are relevant nowadays and can be applied to the design of microservices-based systems.
四十年前,还没有云计算,没有全球规模的需求,也没有必要每11.7秒就部署一次系统。但工程师们仍然必须控制系统的复杂性。尽管那时的工具与现在不同,但所面临的挑战——更重要的是解决方案——在当今仍然适用,并可以应用于基于微服务的系统的设计。
In his book, Composite/Structured Design, Glenford J. Myers discusses how to structure procedural code to reduce its complexity. On the first page of the book, he writes:
在《组合/结构化设计》一书中,Glenford J. Myers讨论了如何构建过程代码以降低其复杂性。在该书的第一页,他写道:
There is much more to the subject of complexity than simply attempting to minimize the local complexity of each part of a program. A much more important type of complexity is global complexity: the complexity of the overall structure of a program or system (i.e., the degree of association or interdependence among the major pieces of a program).
关于复杂性的主题,远不止于仅仅试图最小化程序中每个部分的局部复杂性。一种更为重要的复杂性是全局复杂性:即程序或系统的整体结构的复杂性(即,程序中主要部分之间的关联或相互依赖的程度)。
In our context, local complexity is the complexity of each individual microservice, whereas global complexity is the complexity of the whole system. Local complexity depends on the implementation of a service; global complexity is defined by the interactions and dependencies between the services. Which of the complexities is more important to optimize when designing a microservices-based system? Let’s analyze both extremes.
在我们的上下文中,局部复杂性是指每个单独微服务的复杂性,而全局复杂性是指整个系统的复杂性。局部复杂性取决于服务的实现;全局复杂性由服务之间的交互和依赖关系定义。在设计基于微服务的系统时,优化哪种复杂性更为重要?让我们来分析一下这两种极端情况。
It’s surprisingly easy to reduce global complexity to a minimum. All we have to do is eliminate any interactions between the system’s components— that is, implement all functionality in one monolithic service. As we’ve seen earlier, this strategy may work in certain scenarios. In others, it may lead to the dreaded big ball of mud: probably the highest possible level of local complexity.
将全局复杂性降低到最低限度是非常容易的。我们所要做的就是消除系统组件之间的任何交互——也就是说,在一个单体服务中实现所有功能。正如我们前面所看到的,这种策略在某些情况下可能有效。在另一些情况下,它可能会导致可怕的大泥球:可能是局部复杂性的最高可能级别。
On the other hand, we know what happens when we optimize only the local complexity but neglect the system’s global complexity—the even more dreaded distributed big ball of mud. This relationship is shown in Figure 14-5.
另一方面,我们知道当我们只优化局部复杂性却忽视系统的全局复杂性时会发生什么——更加可怕的分布式泥球架构。这种关系如图14-5所示。
To design a proper microservices-based system, we have to optimize both global and local complexities. Setting the design goal of optimizing either one individually is a local optima. The global optima balances both complexities. Let’s see how the notion of micro-public interfaces lends itself to balancing global and local complexities.
为了设计一个适当的基于微服务的系统,我们必须同时优化全局复杂性和局部复杂性。将设计目标设定为单独优化其中任何一种都是局部最优。全局最优需要平衡这两种复杂性。让我们来看看微公共接口的概念如何有助于平衡全局复杂性和局部复杂性。
Microservices as Deep Services
微服务作为深度服务
A module in a software system, or any system, for that matter, is defined by its function and logic. A function is what the module is supposed to do—its business functionality. The logic is the module’s business logic—how the module implements its business functionality.
软件系统或任何系统中的模块都由其功能和逻辑来定义。功能是模块应该做什么——即其业务功能。逻辑是模块的业务逻辑——即模块如何实现其业务功能。
In his book, The Philosophy of Software Design, John Ousterhout discusses the notion of modularity and proposes a simple yet powerful visual heuristic for evaluating a module’s design: depth.
在《软件设计的哲学》一书中,John Ousterhout讨论了模块性的概念,并提出了一种简单而强大的视觉启示来评估模块的设计:即深度。
Ousterhout proposes to visualize a module as a rectangle, as shown in Figure 14-6. The rectangle’s top edge represents the module’s function, or the complexity of its public interface. A wider rectangle represents broader functionality, while a narrower one has a more restricted function and thus a simpler public interface. The area of the rectangle represents the module’s logic, or the implementation of its functionality.
Ousterhout提出将模块可视化为一个矩形,如图14-6所示。矩形的上边缘代表模块的功能,或其公共接口的复杂性。更宽的矩形代表更广泛的功能,而更窄的矩形则具有更受限的功能,因此具有更简单的公共接口。矩形的面积代表模块的逻辑,或其功能的实现。
Figure 14-6. Deep modules
图14-6. 深模块
According to this model, effective modules are deep: a simple public interface encapsulates complex logic. Ineffective modules are shallow: a shallow module’s public interface encapsulates much less complexity than a deep module. Consider the method in the following listing:
根据这个模型,有效的模块是深层的: 一个简单的公共接口封装了复杂的逻辑。无效的模块是浅层的: 浅模块的公共接口封装的复杂性比深模块低得多。考虑下面清单中的方法:
1 int AddTwoNumbers(int a, int b) 2 { 3 return a + b; 4 }
This is the extreme case of a shallow module: the public interface (the method’s signature) and its logic (the methods) are exactly the same. Having such a module introduces extraneous “moving parts,” and thus, instead of encapsulating complexity, it adds accidental complexity to the overarching system.
这是一个浅模块的极端情况:公共接口(方法的签名)及其逻辑(方法)完全相同。拥有这样的模块会引入多余的“活动部分”,因此,它不仅没有封装复杂性,反而给整个系统增加了偶然复杂性。
Microservices as Deep Modules
微服务作为深度模块
Apart from different terminology, the notion of deep modules differs from the microservices pattern in that the modules can denote both logical and physical boundaries, while microservices are strictly physical. Otherwise, both concepts and their underlying design principles are the same.
除了术语不同之外,深层模块的概念与微服务模式的不同之处在于,模块既可以表示逻辑边界,也可以表示物理边界,而微服务是严格的物理边界。除此之外,这两个概念及其背后的设计原则是相同的。
The services implementing a single business method, shown in Figure 14-3, are shallow modules. Because we had to introduce integration-related public methods, the resultant interfaces are “wider” than they should have been.
图14-3中所示的实现单个业务方法的服务是浅模块。由于我们必须引入与集成相关的公共方法,因此所得接口比应有的“更宽”。
From a system complexity standpoint, a deep module reduces the system’s global complexity, while a shallow module increases it by introducing a component that doesn’t encapsulate its local complexity.
从系统复杂性的角度来看,深模块降低了系统的全局复杂性,而浅层模块则通过引入一个无法封装其局部复杂性的组件而增加了系统的全局复杂性。
Shallow services are also the reason why so many microservices-oriented projects fail. The mistaken definitions of a microservice as a service having no more than X lines of code, or as a service that should be easier to rewrite than to modify, concentrate on the individual service while missing the most important aspect of the architecture: the system.
浅服务也是许多面向微服务的项目失败的原因。将微服务错误地定义为只有不超过 X 行代码的服务,或者将微服务错误地定义为应该更容易重写而不是修改的服务,这些定义都过于关注单个服务,而忽略了架构中最重要的方面:系统。
The threshold upon which a system can be decomposed into microservices is defined by the use cases of the system that the microservices are a part of. If we decompose a monolith into services, the cost of introducing a change goes down. It is minimized when the system is decomposed into microservices. However, if you keep decomposing past the microservices threshold, the deep services will become more and more shallow. Their interfaces will grow back up. This time, due to integration needs, the cost of introducing a change will go up as well, and the overall system’s architecture will turn into the dreaded distributed big ball of mud. This is depicted in Figure 14-7.
系统可以分解为微服务的阈值是由微服务所属系统的用例定义的。如果我们将一个单体分解为服务,那么引入变更的成本就会降低。当系统被分解为微服务时,这个成本达到最低。然而,如果你继续分解超过微服务的阈值,深服务将变得越来越浅。它们的接口将再次增长。这一次,由于集成需求,引入变更的成本也会上升,整个系统的架构将变成可怕的分布式泥球架构。这如图14-7所示。
Now that we’ve learned what microservices are, let’s take a look at how domain-driven design can help us find the boundaries of deep services.
既然我们已经了解了微服务是什么,那么让我们来看看领域驱动设计是如何帮助我们找到深服务的边界的。
Domain-Driven Design and Microservices’ Boundaries
领域驱动设计及微服务的边界
As microservices, many of the domain-driven design patterns discussed in the previous chapters are about boundaries: the bounded context is the boundary of a model, a subdomain bounds a business capability, while aggregate and value objects are transactional boundaries. Let’s see which of these boundaries lends itself to the notion of microservices.
和微服务一样,前面几章中讨论的许多领域驱动设计模式都是关于边界的:有界上下文是模型的边界,子域界定业务能力,而聚合和值对象是事务边界。让我们来看看这些边界中哪些适用于微服务的概念。
Bounded Contexts
有界上下文
The microservices and bounded context patterns have a lot in common, so much so that the patterns are often used interchangeably. Let’s see whether that’s really the case: do bounded contexts’ boundaries correlate with the boundaries of effective microservices?
微服务和有界上下文模式有很多相似之处,以至于这两种模式经常可以互换使用。让我们来看看是否真的是这样:有界上下文的边界是否与有效的微服务的边界相关?
Both microservices and bounded contexts are physical boundaries. Microservices, as bounded contexts, are owned by a single team. As in bounded contexts, conflicting models cannot be implemented in a microservice, resulting in complex interfaces. Microservices are indeed bounded contexts. But does this relationship work the other way around? Can we say that bounded contexts are microservices?
微服务和有界上下文都是物理边界。作为有界上下文的微服务由一个团队拥有。与有界上下文一样,微服务中不能实现冲突的模型,否则会导致接口复杂。微服务确实是有界上下文。但是这种关系反过来也成立吗?我们能说有界上下文就是微服务吗?
As you learned in Chapter 3, bounded contexts protect the consistency of ubiquitous languages and models. No conflicting models can be implemented in the same bounded context. Say you are working on an advertising management system. In the system’s business domain, the business entity Lead is represented by different models in the Promotions and Sales contexts. Hence, Promotions and Sales are bounded contexts, each defining one and only one model of the Campaign entity, which is valid in its boundary, as shown in Figure 14-8.
正如你在第3章中所学的,有界上下文保护了通用语言的连贯性和模型的一致性。在同一个有界上下文中不能实现冲突的模型。假设你正在开发一个广告管理系统。在该系统的业务领域中,业务实体“Lead(潜在客户)”在“促销”和“销售”上下文中由不同的模型表示。因此,“促销”和“销售”是有界上下文,每个上下文都定义了唯一一个在其边界内有效的“Campaign(活动)”实体模型,如图14-8所示。
For simplicity’s sake, let’s assume there are no other conflicting models in the system besides Lead. This makes the resultant bounded contexts naturally wide—each bounded context can contain multiple subdomains. The subdomains can be moved from one bounded context to another one. As long as the subdomains do not imply conflicting models, all the alternative decompositions in Figure 14-9 are perfectly valid bounded contexts.
为了简单起见,我们假设系统中除了“Lead(潜在客户)之外没有其他冲突的模型。这使得最终的有界上下文自然很宽——每个有界上下文可以包含多个子域。子域可以从一个有界上下文移动到另一个有界上下文。只要子域不隐含冲突的模型,图14-9中的所有备选分解都是有效的有界上下文。
Figure 14-9. Alternative decompositions to bounded contexts
图14-9. 有界上下文的其他分解方式
The different decompositions to bounded contexts attribute different requirements, such as different teams’ sizes and structures, lifecycle dependencies, and so on. But can we say that all the valid bounded contexts in this example are necessarily microservices? No. Especially considering the relatively wide functionalities of the two bounded contexts in decomposition 1.
不同的有界上下文分解方式对应着不同的需求,如不同团队的规模和结构、生命周期依赖关系等。但是我们能说这个例子中的所有有效的有界上下文都一定是微服务吗?不能。尤其是考虑到分解1中的两个有界上下文的功能相对宽泛。
Therefore, the relationship between microservices and bounded contexts is not symmetric. Although microservices are bounded contexts, not every bounded context is a microservice. Bounded contexts, on the other hand, denote the boundaries of the largest valid monolith. Such a monolith should not be confused with a big ball of mud; it’s a viable design option that protects the consistency of its ubiquitous language, or its model of the business domain. As we will discuss in Chapter 15, such broad boundaries are more effective than microservices in certain cases.
因此,微服务和有界上下文之间的关系并不是对称的。虽然微服务是有界上下文,但并不是每个有界上下文都是微服务。另一方面,有界上下文表示最大有效单体的边界。这样的单体不应该与泥球架构混淆;它是一种可行的设计选择,可以保护其通用语言的连贯性或其业务领域模型。我们将在第15章中讨论,在某些情况下,这种宽泛的边界比微服务更有效。
Figure 14-10 visually demonstrates the relationship between bounded contexts and microservices. The area between the bounded contexts and microservices is safe. These are valid design options. However, if the system is not decomposed into proper bounded contexts or is decomposed past the microservices threshold, it will result in a big ball of mud or a distributed big ball of mud, respectively.
图14-10直观地展示了有界上下文和微服务之间的关系。有界上下文和微服务之间的区域是安全的。这些都是有效的设计选择。然而,如果系统没有被分解成适当的有界上下文,或者分解超过了微服务的阈值,那么它将分别导致泥球架构或分布式泥球架构。
Next, let’s examine the other extreme: whether aggregates can help find the microservices’ boundaries.
接下来,让我们研究另一个极端:聚合是否有助于找到微服务的边界。
Aggregates
聚合
While bounded contexts impose limits on the widest valid boundaries, the aggregate pattern does the opposite. The aggregate’s boundary is the narrowest boundary possible. Decomposing an aggregate into multiple physical services, or bounded contexts, is not only suboptimal but, as you will learn in Appendix A, leads to undesired consequences, to say the least.
有界上下文对最宽的有效边界施加限制,而聚合模式则相反。聚合的边界是可能的最窄边界。将一个聚合分解成多个物理服务或有界上下文不仅是次优的,而且如附录A所述,至少会导致不良后果。
As bounded contexts, aggregates’ boundaries are also often considered to drive the boundaries of microservices. An aggregate is an indivisible business functionality unit that encapsulates the complexities of its internal business rules, invariants, and logic. That said, as you learned earlier in this chapter, microservices are not about individual services. An individual service has to be considered in the context of its interactions with other components of the system:
作为有界上下文,聚合的边界通常也被认为是驱动微服务的边界。聚合是一个不可分割的业务功能单元,它封装了其内部业务规则、不变性和逻辑的复杂性。也就是说,正如您在本章前面所了解的,微服务与单独的服务无关。必须在与系统其他组件的交互上下文中考虑单独的服务:
翻译:与有界上下文一样,聚合的边界也经常被认为是驱动微服务边界的因素。聚合是一个不可分割的业务功能单元,它封装了其内部业务规则的复杂性、不变性和逻辑。也就是说,正如你在本章前面所学到的,微服务并不是关于单个服务的。必须在与系统其他组件交互的上下文中考虑单个服务:
Does the aggregate in question communicate with other aggregates in its subdomain?
该聚合是否与其子域中的其他聚合进行通信?
- Does it share value objects with other aggregates? 它是否与其他聚合共享值对象?
- How likely will the aggregate’s business logic changes affect other components of the subdomain and vice versa? 该聚合的业务逻辑变化有多大可能性会影响子域中的其他组件,反之亦然?
The stronger the aggregate’s relationship is with the other business entities of its subdomain, the shallower it will be as an individual service.
聚合与其子域中的其他业务实体的关系越强,它作为单个服务就越浅。
There will be cases in which having an aggregate as a service will produce a modular design. However, much more often such fine-grained services will increase the overarching system’s global complexity.
在某些情况下,将聚合作为服务会产生模块化设计。然而,更多时候,这种细粒度的服务会增加整个系统的全局复杂性。
Subdomains
子域
A more balanced heuristic for designing microservices is to align the services with the boundaries of business subdomains. As you learned in Chapter 1, subdomains are correlated with fine-grained business capabilities. These are the business building blocks required for the company to compete in its business domain(s). From a business domain perspective, subdomains describe the capabilities—what the business does —without explaining how the capabilities are implemented. From a technical standpoint, subdomains represent sets of coherent use cases: using the same model of the business domain, working on the same or closely related data, and having a strong functional relationship. A change in the business requirements of one of the use cases is likely to affect the other use cases, as shown in Figure 14-11.
设计微服务时更平衡的启发式方法是将服务与业务子域的边界对齐。正如你在第1章中所学的,子域与细粒度的业务能力相关联。这些是公司在其业务领域进行竞争所需的业务构建块。从业务领域的角度来看,子域描述了能力——即业务所做的事情——而没有解释如何实现这些能力。从技术角度来看,子域代表了一组连贯的用例:使用相同的业务领域模型,处理相同或密切相关的数据,并具有强烈的功能关系。如图14-11所示,其中一个用例的业务需求发生变化很可能会影响其他用例。
Figure 14-11. Subdomains
图14-11. 子域
The subdomains’ granularity and the focus on the functionality—the “what” rather than the “how”—makes subdomains naturally deep modules. A subdomain’s description—the function—encapsulates the more complex implementation details—the logic. The coherent nature of the use cases contained in a subdomain also ensures the resultant module’s depth. Splitting them apart in many cases would result in a more complex public interface and thus shallower modules. All of these things make subdomains a safe boundary for designing microservices.
子域的粒度以及对功能的关注——“做什么”而不是“怎么做”——使得子域自然地成为深模块。子域的描述(即功能)封装了更复杂的实现细节(即逻辑)。子域中包含的用例的一致性也确保了结果模块的深度。在许多情况下,将它们分开会导致更复杂的公共接口,从而使模块变得更浅。所有这些因素都使得子域成为设计微服务时的安全边界。
Aligning microservices with subdomains is a safe heuristic that produces optimal solutions for the majority of microservices. That said, there will be cases where other boundaries will be more efficient; for example, staying in the wider, linguistic boundaries of the bounded context or, due to nonfunctional requirements, resorting to an aggregate as a microservice. The solution depends not only on the business domain but also on the organization’s structure, business strategy, and nonfunctional requirements. As we discussed in Chapter 11, it’s crucial to continuously adapt the software architecture and design to changes in the environment.
将微服务与子域对齐是一种安全的启发式方法,可以为大多数微服务生成最佳解决方案。也就是说,在某些情况下,其他边界可能更有效;例如,保持在有界上下文的更广泛的语言边界内,或者由于非功能性需求,将聚合作为微服务。解决方案不仅取决于业务领域,还取决于组织的结构、业务策略和非功能性需求。正如我们在第11章中所讨论的,不断使软件架构和设计适应环境的变化至关重要。
Compressing Microservices’ Public Interfaces
压缩微服务的公共接口
In addition to finding service boundaries, domain-driven design can help make services deeper. This section demonstrates how the open-host service and anticorruption layer patterns can simplify the microservices’ public interfaces.
除了找到服务边界之外,领域驱动设计还可以帮助服务变得更深。本节将演示开放主机服务和防腐层模式如何简化微服务的公共接口。
Open-Host Service
开放主机服务
The open-host service decouples the bounded context’s model of the business domain from the model used for integration with other components of the system, as shown in Figure 14-12.
开放主机服务将业务领域的有界上下文模型与用于与系统的其他组件集成的模型解耦,如图14-12所示。
Introducing the integration-oriented model, the published language, reduces the system’s global complexity. First, it allows us to evolve the service’s implementation without impacting its consumers: the new implementation model can be translated to the existing published language. Second, the published language exposes a more restrained model. It is designed around integration needs. It encapsulates the complexity of the implementation that is not relevant to the service’s consumers. For example, it can expose less data and in a more convenient model for consumers.
引入面向集成的模型(即发布语言)可以降低系统的全局复杂性。首先,它允许我们在不影响服务消费者的情况下演进服务的实现:新的实现模型可以转换为现有的发布语言。其次,发布语言暴露了一个更加受限的模型。它是围绕集成需求设计的。它封装了与服务消费者不相关的实现复杂性。例如,它可以暴露更少的数据,并且以更适合消费者的模型暴露。
Having a simpler public interface (function) over the same implementation (logic) makes the service “deeper” and contributes to a more effective microservice design.
在同一实现(逻辑)上拥有更简单的公共接口(功能)会使服务“更深”,并有助于更有效的微服务设计。
Figure 14-12. Integrating services through a published language
图14-12. 通过发布语言集成服务
Anticorruption Layer
防腐层
The anticorruption layer (ACL) pattern works the other way around. It reduces the complexity of integrating the service with other bounded contexts. Traditionally, the anticorruption layer belongs to the bounded context it protects. However, as we discussed in Chapter 9, this notion can be taken a step further and implemented as a standalone service.
防腐层(ACL)模式的工作方式正好相反。它降低了将服务与其他有界上下文集成的复杂性。传统上,防腐层属于它所保护的有界上下文。然而,正如我们在第9章中所讨论的,这个概念可以更进一步,作为一个独立的服务来实现。
The ACL service in Figure 14-13 reduces both the local complexity of the consuming bounded context and the system’s global complexity. The consuming bounded context’s business complexity is separated from the integration complexity. The latter is offloaded to the ACL service. Because the consuming bounded context is working with a more convenient, integration-oriented model, its public interface is compressed—it doesn’t reflect the integration complexity exposed by the producing service.
图14-13中的防腐层服务降低了消费(者)有界上下文的局部复杂性和系统的全局复杂性。消费有界上下文的业务复杂性与集成复杂性是分开的。后者被下放到防腐层服务。因为消费有界上下文使用的是更方便、面向集成的模型,所以它的公共接口被压缩了——它没有反映出生产(者)服务所暴露的集成复杂性。
Figure 14-13. Anticorruption layer as a stand-alone service
图14-13 作为独立服务的防腐层
Conclusion
总结
Historically, the microservice-based architectural style is deeply interconnected with domain-driven design, so much so that the terms microservice and bounded context are often used interchangeably. In this chapter, we analyzed the connection between the two and saw that they are not the same thing.
从历史上看,基于微服务的架构风格与领域驱动设计紧密相连,以至于微服务和有界上下文这两个术语经常可以互换使用。在本章中,我们分析了这两者之间的联系,并看到它们并不是一回事。
All microservices are bounded contexts, but not all bounded contexts are necessarily microservices. In its essence, a microservice defines the smallest valid boundary of a service, while a bounded context protects the consistency of the encompassed model and represents the widest valid boundaries. Defining boundaries to be wider than their bounded contexts will result in a big ball of mud, while boundaries that are smaller than microservices will lead to a distributed big ball of mud.
所有微服务都是有界上下文,但并非所有有界上下文都必然是微服务。本质上,微服务定义了服务的最小有效边界,而有界上下文则保护所包含模型的一致性,并代表最宽的有效边界。定义比有界上下文更宽的边界将导致一个大泥球,而比微服务更小的边界将导致一个分布式的大泥球。
Nevertheless, the connection between microservices and domain-driven design is tight. We saw how domain-driven design tools can be used to design effective microservice boundaries.
尽管如此,微服务与领域驱动设计之间的联系是紧密的。我们看到了如何使用领域驱动设计工具来设计有效的微服务边界。
In Chapter 15, we will continue discussing high-level system architecture but from a different perspective: asynchronous integration through event-driven architecture. You will learn how to leverage the different kinds of event messages to further optimize microservices’ boundaries.
在第15章中,我们将继续讨论高级系统架构,但从不同的角度:通过事件驱动架构进行异步集成。您将学习如何利用不同类型的事件消息来进一步优化微服务的边界。
Exercises
练习
1. What is the relationship between bounded contexts and microservices? 有界上下文和微服务之间的关系是什么?
a. All microservices are bounded contexts. 所有微服务都是有界上下文。
b. All bounded contexts are microservices. 所有有界上下文都是微服务。
c. Microservices and bounded contexts are different terms for the same concept. 微服务和有界上下文是同一概念的不同术语。
d. Microservices and bounded contexts are completely different concepts and cannot be compared. 微服务和有界上下文是完全不同的概念,无法进行比较。
答案:a。All microservices are bounded contexts. (But not all bounded contexts are microservices.) 所有微服务都是有界上下文(但并非所有有界上下文都是微服务)。
2. What part of a microservice should be “micro”? 微服务的哪一部分应该是“微”的?
a. The number of pizzas required to feed the team implementing the microservices. The metric has to take into account the team members’ different dietary preferences and average daily calorie intakes. 为实现微服务的团队提供食物所需的披萨数量。该指标必须考虑到团队成员不同的饮食偏好和平均每日卡路里摄入量。
b. The number of lines of code it takes to implement the service’s functionality. Since the metric is agnostic of the lines’ widths, it’s preferable to implement microservices on ultrawide monitors. 实现服务功能所需的代码行数。由于这个度量标准不能确定行的宽度,所以最好在超宽显示器上实现微服务。
c. The most important aspect of designing microservices-based systems is to get microservices-friendly middleware and other infrastructural components, preferably from microservices-certified vendors. 设计基于微服务系统的最关键要素是获取与微服务兼容的中间件和其他基础设施组件,最好是从经过微服务认证的供应商那里获取。
d. The knowledge of the business domain and its intricacies exposed across the service’s boundary and reflected by its public interface. 业务知识及其复杂性在服务边界上的暴露,并通过其公共接口反映出来。
答案:d。
3. What are the safe component boundaries? 什么是安全的组件边界?
a. Boundaries wider than bounded contexts. 比有界上下文更宽泛的边界。
b. Boundaries narrower than microservices. 比微服务更狭窄的边界。
c. Boundaries between bounded contexts (widest) and microservices (narrowest). 有界上下文(最宽)和微服务(最窄)之间的边界。
d. All boundaries are safe. 所有的边界都是安全的。
答案:c。
4. Is it a good design decision to align microservices with the boundaries of aggregates? 将微服务与聚合的边界对齐是一个好的设计决策吗?
a. Yes, aggregates always make for proper microservices. 是的,聚合总是构成合适的微服务。
b. No, aggregates should never be exposed as individual microservices. 不,聚合不应该被暴露为单独的微服务。
c. It’s impossible to make a microservice out of a single aggregate. 不可能从单个聚合中创建出微服务。
d. The decision depends on the business domain. 这个决定取决于业务领域。
答案:d。
1 Reference model for service-oriented architecture v1.0. (n.d.). Retrieved June 14, 2021, from OASIS. 面向服务的体系结构 v1.0的参考模型(n.d.) ,检索于2021年6月14日,来自 OASIS。
