DDD 与微服务(2. 服务边界)

420 阅读7分钟

做微服务架构时遇到的第一个问题就是如何划分服务的边界,但是在面对这个问题时似乎并没有什么 golden rule 可以指引我们,基本都靠开发者自己摸索。在实际项目中通常会采用两种不同的方式划分服务边界,即通过业务职能(Business Capability) 或是 DDD 的限界上下文(Bounded Context)。这两者在使用过程中还是存在着一定的差异,作为架构师也需要处理不同的问题,下面开始分享一些我的实践经验。

业务职能(Business Capability)

业务职能的定义很好理解,简单的理解就是由公司内部不同部门提供的职能。例如客户服务部门提供客户服务的职能,财务部门提供财务相关的职能等等。按照公司内部不同部门的职能拆分服务边界是比较常见,也是相对符合人们思维习惯的做法。在一个保险公司中,通常由以下的这些职能部门,或是岗位:

其中新契约部门负责保单的承保和续期,精算部门负责产品费率的定义,核保部门负责保单的审核,而理赔与客户服务部门则负责客户的理赔申请与服务。原始的保险核心业务系统都是一个大型的单体应用,当需要拆分为微服务时,最直观的想法就是按照这种部门职能来拆分。这种做法的优点很明显,就是简单且易于实施。如果原始系统也不是走的 DDD 架构,只是按照业务的不同模块进行了区分,那么我们的工作相对轻松。只需按照原来模块的边界把代码搬迁出来即可(当然还需要处理模块间调用,数据库耦合的问题)。

但是这种做法也带来以下的这些问题。

首先我们称之为的业务职能,很大部分等于组织结构,这也就意味着拆分后的微服务结构将会服从「康威定理」,即系统的架构与使用它的组织结构保持一致。随之而来的问题就是拆分后的微服务丧失了继续进化的能力,服从于公司固有的组织结构。假设将来保险公司增加专门负责互联网渠道的新部门,那么我们就需要为新部门重新开发一套服务或是系统,而不能复用原有的服务。这就和采用微服务的初衷背道而驰了。

第二个问题是如果将来某个部门的职能扩大,业务变得更为复杂,又该如何拆分呢?此时我们同样面临一个划分服务边界的问题,但此时部门内部的职能划分可能就没有那么的明显了,需要我们对业务有着更深的了解才能做出合理的选择。

按照业务职能划分服务边界带来的问题为了拆而拆,虽然在一开始的确能够把单体应用按照一定的逻辑拆分成数个不同的服务,但是受限于业务职能,并没有真正达到松耦合的要求。最终的结果很可能就是把一个大型的单体应用拆分成了几个小型的单体应用。

限界上下文(Bounded Context)

限界上下文是 DDD 中用来划分不同业务边界的元素,这里业务边界的含义是解决不同业务问题的问题域和对应的解决方案域。这样的说法可能有些抽象,但究其根本其实也不难理解,你可以认为限界上下文的存在是为了解决某种类型的业务问题。写到这里你其实应该能够看出业务职能与限界上下文这两种不同的划分服务方式的差异,即一个是从组织结构的角度出发,而另一个则更加贴近领域知识,也就是业务。现在就看看如果采用限界上下文的方式如何拆分保险核心业务系统:

上图中已经有了许多的不同,核保服务拆分为核保 API 服务与核保规则定义这两个独立的服务,将规则的执行与定义做了拆分,更加贴近业务。新契约,理赔和保全都进一步拆分,形成了一个新的独立服务 —— 保单管理。由于从业务角度出发,新契约,理赔,保全大部分的业务的最终结果都是对保单状态的维护,所以可以将类似的业务抽取出来形成一个新的服务。而剩下的功能只需要关注具体的业务流程即可。

原本的产品服务也有了变化,从原本的产品精算模块拆分为核心计算服务与产品定义服务。其中核心计算模块会按照不同的产品类型和对应的费率表提供诸如保费计算,保单贷款,理赔金额,红利等相关的计算 API 服务。

产品定义的功能则更为强大。原本保险新产品的推出是个很繁琐的工作,需要修改几乎系统中的所有模块,包括新契约(牵涉到保费计算),理赔(理赔金额的计算),保全(可能需要重新试算保费),非常容易出错。但是有了产品定义服务后,可以在集中的地方定义产品的所有相关特性,然后用事件的方式进行广播,有其他服务主动的同步产品最新信息,真正的达到了松耦合的效果。

完整的保险核心业务系统服务远比图中的例子复杂,但是划分这些服务的核心思路没有发生变化,更多的从业务所解决的问题出发,而不是单纯参考部门结构和流程。

一些实践经验

很多人会问我同样的问题,如果开发一个新系统是否一开始就采取微服务的架构,我的答案是否定的。无论对于一个大型公司中的成熟团队还是创业公司中小而精的团队,微服务并不是一个轻量级的架构解决方案,它对于开发人员的能力要求,DevOps,以及基础设施都提出了相当高的要求。在项目或是产品开发的初始阶段,我们更多的应该是关注产品本身的价值,而不是一个完美无瑕的技术解决方案。所以大概率你进行的是微服务改造工作,也就是把一个出现各种各样问题的遗留系统改造为微服务。

对于这样的改造项目,我的经验是,如果你对于业务知识有着相当的积累,而原有的单体应用也或多或少有些 DDD 的痕迹,那么不妨多花些时间在设计与架构上,采取 DDD 划分限界上下文的方式来划分服务边界。但如果你面对的是一个大泥球,或者你对业务知识也是一知半解,那还是先按照业务职能做出第一版的设计,在把系统切分出来的过程中学习更多的业务知识,积累微服务的开发经验,当多个系统都稳定后,再考虑 DDD 的限界上下文,毕竟罗马不是一天建成的。

以上就是我在划分服务边界时的一些心得,希望能够对你有启发,很多时候规矩是死的,实践之后才能找到自己想要的答案。

欢迎关注我的微信号「且把金针度与人」,获取更多高质量文章