你好,我是秦粤。上一课中,我们学习了后端 BaaS 化的重要模块:微服务。现在我们知道微服务的核心理念就是先拆后合,拆解功能是为了提升我们功能的利用率。同步我们也了解了实现微服务的 10 要素,这 10 要素要真讲起来够单独开一门课的。如果你不熟悉,我向你推荐杨波老师的《微服务架构核心 20 讲》课程。 BaaS 化的核心其实就是把我们的后端应用封装成 RESTful API,然后对外提供服务,而为了后端应用更容易维护,我们需要将后端应用拆解成免运维的微服务。这个逻辑你要理解,这也是为什么我要花这么多篇幅给你谈微服务的关键原因。
上节课我们将“待办任务”Web 服务的后端,拆解为用户微服务和待办任务微服务。但为什么要这样拆?是凭感觉,还是有具体的方法论?这里你可以停下来想想。
微服务的拆解和合并,都有一个度需要把握,因为我们在一拆一合之间,都是有成本产生的。如果我们拆解得太细,就必然会导致我们的调用链路增长。调用链路变长,首先影响的就是网络延迟,这个好理解,毕竟你路远了,可能“堵车”的地方也会变多;其次是运维成本的增加,调用链路越长,整个链条就越脆弱,因为其中一环出现问题,都会导致整个调用链条访问失败,而且我们排查问题也变得更加困难。
反过来看,如果我们拆解得太粗,调用链路倒是短了,但是这个微服务的复用性就差了,更别提因为高耦合带来的复杂且冗余的数据库表结构,让我们后续难以维护。我画了个图,你感受下。

拆解程度示意图
拆之,领域驱动设计
那我们要合理地拆解微服务,应该怎么拆解呢?上节课其实我有提到,目前主流的解决方案就是领域驱动设计,也叫 DDD[1]。DDD 是 Eric Evans 在其 2004 年的同名书中提出来的一个思想,但一直仅仅局限在 Java 的圈子里,直到 2014 年,微服务兴起后大家才发现它可以指导微服务的拆分,这才走进了大多数人的视野。用一句话简单总结,DDD 就是一套方法论:通过对业务分层抽象,分析定义出领域模型,用领域模型驱动我们设计系统,最终将复杂的业务模型拆解为独立运维的领域模型。
实际我自己在使用微服务开发的过程发现,微服务整体应该是一个动态网络结构[2],随着业务的发展,这个网络结构也会发生变化。DDD 能帮助我们前期分析出一个较好的网络结构,但实际上,我们更应该思考的是如何整体优化动态网络:减少核心节点,保护核心节点,降低网络深度等等。
怎么理解动态网络优化呢?我们可以做个思维实验:假设我们将所有的功能都拆解成微服务,任意的微服务节点相互之间都可以调用,调用越频繁它们之间的距离就越近。那么我们考虑一下,当我们网站的访问请求流量稳定后,我们整个微服务节点组成的网络状态是怎么样的?
首先网络节点的相互制约总会让那些相互之间强依赖的、高耦合的节点,越走越近,最后聚集成一团节点。其次那些跟业务逻辑无关的节点,逐渐被边缘化,甚至消失。我们看这些聚集成团的节点,如果团里的点聚合太近了,其实是不适合拆分的,它们整体应该作成一个微服务。等这些节点太近的团合并成一个微服务节点后,我们再看那些聚集在一起、又不太近的节点就是一个个微服务了。
所以,我们在启动项目时,不用太过纠结应该如何去拆解微服务。而应该持续关注,并思考每个微服务节点的合理性。就像看待动态网络一样,持续地调整优化,去除核心节点。最终它会伴随你业务的发展阶段,达到各个阶段的稳定动态网络结构。
就像我们上节课“待办任务”Web 服务一样,我们可以先简单地将我们的项目后端分为:用户微服务和待办任务微服务。当然这里我们目前的业务太简单了,用 DDD 去分析,也是大材小用。随着我这个项目的业务发展,我们添加的功能会越来越多。让微服务根据业务一起成长演变就可以了。这并不是说我们就放任微服务不管了,而是从整体网络的角度思考,去看我们的微服务如何演进。

微服务演进过程
合之,Streaming
看完拆解,我们再看合并。合并呢,换个高大上的词其实就是前面课程中提到的编排。目前为止,我们整个“待办任务”Web 应用架构的设计基本完成了,而且所有节点都是 Stateless 的了。变成 Stateless 节点后,其实对于前端的同学来说,一点都不陌生,比如 React 的单向数据流中的 State 也要求我们 Immutable,这 Immutable 其实就是 Stateless。
我们上面已经看到了,拆解后的架构是个动态网络,那我们应该怎么合并或者编排呢?当然你像 SFF 那样通过传统的函数,将每个 HTTP 数据的请求结果通过数组或对象加工处理,再将这些结果返回也是可以的。但我在这里想向你介绍另外一种编排思路,工作流。