实现微服务架构下的领域驱动设计

云的事情不好说 2024-06-04 02:19:15

将DDD应用到微服务中有什么挑战?答案在于微服务的特性,其中每个微服务都是:

独立且完整的功能单元独立的开发单元独立的部署单元独立的扩展单元不直接暴露数据存储提供API来访问其功能

而在DDD中,我们需要处理不同级别的粒度:

实体(Entity)和值对象(Value Object)聚合根(Aggregate Root)有界上下文(Bounded Context)应用程序(Application)

一方面有作为独立和自包含的微服务,另一方面在DDD中有不同级别的粒度,这引发了一个问题。我们在哪个DDD粒度级别上构建微服务?我们应该为每个有界上下文构建一个微服务吗?或者可能是每个实体,或者介于两者之间?显然不是每个应用程序一个微服务,否则我们最终会得到面向服务架构(SOA)或单体架构风格。

正确的粒度是什么?

简短的答案是“这取决于……”。是的,没有明确的答案,但我们仍然可以弄清楚。让我们仔细看看每个粒度级别,并找出在该级别创建微服务的优点和缺点。

应用程序(Application)

这个级别表示整个应用程序。如果整个应用程序领域模型很好地适应单个服务或单体,那么也许我们不需要用微服务架构风格设计系统。随着应用程序的增长,我们可能会决定迁移到微服务,然而过早这样做可能会变成一个巨大的问题。很难预测我们的领域模型将来会如何演变,因此过早地分割成微服务可能会在以后的重构中造成很多问题。在微服务之间移动领域对象并不像在单个服务或单体中那样容易。

有界上下文(Bounded Context)

有界上下文可能是微服务的一个良好候选,它自然地与其他有界上下文隔离,每个上下文都基于自己的模型和通用语言。有界上下文通过接口、API、隔离层等进行通信,并有明确的边界。然而,当有界上下文很大时,将其用作单个微服务的基础是不合理的,甚至是不可能的。有些团队拥有一个单一的领域模型,因此也就拥有一个单一的有界上下文。在任何情况下,这些团队都能够将整个模型打包成一个微服务。因此,有界上下文可以用作微服务的边界,但只有在模型很小,以至于微服务确实是“微”的时候。

聚合根

简短的答案是聚合根可能是围绕微服务构建的最佳选择。根据定义,聚合根封装了业务逻辑和规则,与实体和值对象的存储和检索相关的所有复杂性。它负责维护模型的不变性,并通过接口向外界暴露其功能。所有这些都与微服务的目的非常吻合。

从聚合根创建微服务的技术通常简单直接。聚合根已经暴露了一个接口,因此我们可以采取这个接口,并通过微服务API暴露它。

实体和值对象

值对象是没有身份的领域对象。很难想象围绕一个值对象构建一个微服务,因此没有必要进一步讨论。

实体是具有身份的领域对象,尽管身份在聚合根内有效,但仍然可以围绕实体构建一个微服务,公开CRUD操作,但这并不意味着我们应该这样做。在大多数情况下,每个实体一个微服务变得过于复杂,并对整个系统的可靠性和性能产生负面影响。为什么呢?

微服务通过网络通信,网络调用既不像进程内调用那样可靠,也不那么快。过度细粒度的微服务倾向于增加网络通信量,使整个系统变慢,可靠性降低。

另一个缺点是增加了复杂性和维护成本。拥有许多微小的微服务使得理解整个系统并导航依赖关系变得极其困难。整个系统变得更加类似于单体,甚至更糟,分布式单体。

增加的耦合是另一个缺点。对于每个实体一个微服务,很可能最终会将紧密耦合的实体分割成不同的微服务。增加的耦合导致了需要对耦合的微服务进行编排部署,这与微服务架构风格的主要优势——每个微服务独立部署——完全相反。

小结

那么,我们可以在DDD的哪个粒度级别上创建微服务?

实际上,最佳选择是在聚合根级别尝试。在某些情况下,有界上下文也是一个合理的选择,但实体很少是一个好选择。在做出决策时,并不总是能够严格按照聚合根进行。有时,一个微服务可能包含多个聚合根,这没问题,只要我们保持微服务的特性。

想象在有界上下文和聚合根之间绘制微服务边界。

当我们做出设计选择时,我们需要有意识地了解我们的目标。任何设计决策都是最小化复杂性的权衡。为什么?复杂系统维护成本高昂,引入新特性需要花费大量时间,并且更改风险很大。这是任何架构和设计决策背后基本思想,包括在哪里绘制微服务边界。



0 阅读:5

云的事情不好说

简介:感谢大家的关注