极客时间已完结课程限时免费阅读

19 | 总结(一):微服务设计和拆分要坚持哪些原则?

19 | 总结(一):微服务设计和拆分要坚持哪些原则?-极客时间

19 | 总结(一):微服务设计和拆分要坚持哪些原则?

讲述:欧创新

时长18:19大小12.56M

你好,我是欧创新。
我们前面已经讲了很多 DDD 的设计方法和实践案例。虽然 DDD 的设计思想和方法很好,但由于企业发展历程以及企业技术和文化的不同,DDD 和微服务的实施策略也会有差异。那么面对这种差异,我们应该如何落地 DDD 和微服务呢?今天我们就来聊聊微服务的设计原则和演进策略。

微服务的演进策略

在从单体向微服务演进时,演进策略大体分为两种:绞杀者策略和修缮者策略。

1. 绞杀者策略

绞杀者策略是一种逐步剥离业务能力,用微服务逐步替代原有单体系统的策略。它对单体系统进行领域建模,根据领域边界,在单体系统之外,将新功能和部分业务能力独立出来,建设独立的微服务。新微服务与单体系统保持松耦合关系。
随着时间的推移,大部分单体系统的功能将被独立为微服务,这样就慢慢绞杀掉了原来的单体系统。绞杀者策略类似建筑拆迁,完成部分新建筑物后,然后拆除部分旧建筑物。

2. 修缮者策略

修缮者策略是一种维持原有系统整体能力不变,逐步优化系统整体能力的策略。它是在现有系统的基础上,剥离影响整体业务的部分功能,独立为微服务,比如高性能要求的功能,代码质量不高或者版本发布频率不一致的功能等。
通过这些功能的剥离,我们就可以兼顾整体和局部,解决系统整体不协调的问题。修缮者策略类似古建筑修复,将存在问题的部分功能重建或者修复后,重新加入到原有的建筑中,保持建筑原貌和功能不变。一般人从外表感觉不到这个变化,但是建筑物质量却得到了很大的提升。
其实还有第三种策略,就是另起炉灶,顾名思义就是将原有的系统推倒重做。建设期间,原有单体系统照常运行,一般会停止开发新需求。而新系统则会组织新的项目团队,按照原有系统的功能域,重新做领域建模,开发新的微服务。在完成数据迁移后,进行新旧系统切换。
对于大型核心系统我一般不建议采用这种策略,这是因为系统重构后的不稳定性、大量未知的潜在技术风险和新的开发模式下项目团队磨合等不确定性因素,会导致项目实施难度大大增加。

不同场景下的领域建模策略

由于企业内情况千差万别,发展历程也不一样,有遗留单体系统的微服务改造,也有全新未知领域的业务建模和系统设计,还有遗留系统局部优化的情况。不同场景下,领域建模的策略也会有差异。下面我们就分几类场景来看看如何进行领域建模。

1. 新建系统

新建系统又分为简单和复杂领域建模两种场景。
简单领域建模
简单的业务领域,一个领域就是一个小的子域。在这个小的问题域内,领域建模过程相对简单,直接采用事件风暴的方法构建领域模型就可以了。
复杂领域建模
对于复杂的业务领域,领域可能需要多级拆分后才能开始领域建模。领域拆分为子域,甚至子域还需要进一步拆分。比如:保险它需要拆分为承保、理赔、收付费和再保等子域,承保子域再拆分为投保、保单管理等子子域。复杂领域如果不做进一步细分,由于问题域太大,领域建模的工程量会非常浩大。你不太容易通过事件风暴,完成一个很大的领域建模,即使勉强完成,效果也不一定好。
对于复杂领域,我们可以分三步来完成领域建模和微服务设计。
第一步,拆分子域建立领域模型
根据业务领域的特点,参考流程节点边界或功能聚合模块等边界因素。结合领域专家和项目团队的讨论,将领域逐级分解为大小合适的子域,针对子域采用事件风暴,划分聚合和限界上下文,初步确定子域内的领域模型。
第二步,领域模型微调
梳理领域内所有子域的领域模型,对各子域领域模型进行微调。微调的过程重点考虑不同领域模型中聚合的重组。同步考虑领域模型和聚合的边界,服务以及事件之间的依赖关系,确定最终的领域模型。
第三步,微服务的设计和拆分
根据领域模型和微服务拆分原则,完成微服务的拆分和设计。

2. 单体遗留系统

如果我们面对的是一个单体遗留系统,只需要将部分功能独立为微服务,而其余仍为单体,整体保持不变,比如将面临性能瓶颈的模块拆分为微服务。我们只需要将这一特定功能,理解为一个简单子领域,参考简单领域建模的方式就可以了。在微服务设计中,我们还要考虑新老系统之间服务和业务的兼容,必要时可引入防腐层。

DDD 使用的误区

很多人在接触微服务后,但凡是系统,一概都想设计成微服务架构。其实有些业务场景,单体架构的开发成本会更低,开发效率更高,采用单体架构也不失为好的选择。同样,虽然 DDD 很好,但有些传统设计方法在微服务设计时依然有它的用武之地。下面我们就来聊聊 DDD 使用的几个误区。

1. 所有的领域都用 DDD

很多人在学会 DDD 后,可能会将其用在所有业务域,即全部使用 DDD 来设计。DDD 从战略设计到战术设计,是一个相对复杂的过程,首先企业内要培养 DDD 的文化,其次对团队成员的设计和技术能力要求相对比较高。在资源有限的情况下,应聚焦核心域,建议你先从富领域模型的核心域开始,而不必一下就在全业务域推开。

2. 全部采用 DDD 战术设计方法

不同的设计方法有它的适用环境,我们应选择它最擅长的场景。DDD 有很多的概念和战术设计方法,比如聚合根和值对象等。聚合根利用仓储管理聚合内实体数据之间的一致性,这种方法对于管理新建和修改数据非常有效,比如在修改订单数据时,它可以保证订单总金额与所有商品明细金额的一致,但它并不擅长较大数据量的查询处理,甚至有延迟加载进而影响效率的问题。
而传统的设计方法,可能一条简单的 SQL 语句就可以很快地解决问题。而很多贫领域模型的业务,比如数据统计和分析,DDD 很多方法可能都用不上,或用得并不顺手,而传统的方法很容易就解决了。
因此,在遵守领域边界和微服务分层等大原则下,在进行战术层面设计时,我们应该选择最适合的方法,不只是 DDD 设计方法,当然还应该包括传统的设计方法。这里要以快速、高效解决实际问题为最佳,不要为做 DDD 而做 DDD。

3. 重战术设计而轻战略设计

很多 DDD 初学者,学习 DDD 的主要目的,可能是为了开发微服务,因此更看重 DDD 的战术设计实现。殊不知 DDD 是一种从领域建模到微服务落地的全方位的解决方案。
战略设计时构建的领域模型,是微服务设计和开发的输入,它确定了微服务的边界、聚合、代码对象以及服务等关键领域对象。领域模型边界划分得清不清晰,领域对象定义得明不明确,会决定微服务的设计和开发质量。没有领域模型的输入,基于 DDD 的微服务的设计和开发将无从谈起。因此我们不仅要重视战术设计,更要重视战略设计。

4. DDD 只适用于微服务

DDD 是在微服务出现后才真正火爆起来的,很多人会认为 DDD 只适用于微服务。在 DDD 沉默的二十多年里,其实它一直也被应用在单体应用的设计中。
具体项目实施时,要吸取 DDD 的核心设计思想和理念,结合具体的业务场景和团队技术特点,多种方法组合,灵活运用,用正确的方式解决实际问题。

微服务设计原则

微服务设计原则中,如高内聚低耦合、复用、单一职责等这些常见的设计原则在此就不赘述了,我主要强调下面这几条:
第一条:要领域驱动设计,而不是数据驱动设计,也不是界面驱动设计。
微服务设计首先应建立领域模型,确定逻辑和物理边界以及领域对象后,然后才开始微服务的拆分和设计。而不是先定义数据模型和库表结构,也不是前端界面需要什么,就去调整核心领域逻辑代码。在设计时应该将外部需求从外到内逐级消化,尽量降低对核心领域层逻辑的影响。
第二条:要边界清晰的微服务,而不是泥球小单体。
微服务上线后其功能和代码也不是一成不变的。随着需求或设计变化,领域模型会迭代,微服务的代码也会分分合合。边界清晰的微服务,可快速实现微服务代码的重组。微服务内聚合之间的领域服务和数据库实体原则上应杜绝相互依赖。你可通过应用服务编排或者事件驱动,实现聚合之间的解耦,以便微服务的架构演进。
第三条:要职能清晰的分层,而不是什么都放的大箩筐。
分层架构中各层职能定位清晰,且都只能与其下方的层发生依赖,也就是说只能从外层调用内层服务,内层通过封装、组合或编排对外逐层暴露,服务粒度也由细到粗。应用层负责服务的组合和编排,不应有太多的核心业务逻辑,领域层负责核心领域业务逻辑的实现。各层应各司其职,职责边界不要混乱。在服务演进时,应尽量将可复用的能力向下层沉淀。
第四条:要做自己能 hold 住的微服务,而不是过度拆分的微服务。
微服务过度拆分必然会带来软件维护成本的上升,比如:集成成本、运维成本、监控和定位问题的成本。企业在微服务转型过程中还需要有云计算、DevOps、自动化监控等能力,而一般企业很难在短时间内提升这些能力,如果项目团队没有这些能力,将很难 hold 住这些微服务。
如果在微服务设计之初按照 DDD 的战略设计方法,定义好了微服务内的逻辑边界,做好了架构的分层,其实我们不必拆分太多的微服务,即使是单体也未尝不可。随着技术积累和能力提升,当我们有了这些能力后,由于应用内有清晰的逻辑边界,我们可以随时轻松地重组出新的微服务,而这个过程不会花费太多的时间和精力。

微服务拆分需要考虑哪些因素?

理论上一个限界上下文内的领域模型可以被设计为微服务,但是由于领域建模主要从业务视角出发,没有考虑非业务因素,比如需求变更频率、高性能、安全、团队以及技术异构等因素,而这些非业务因素对于领域模型的系统落地也会起到决定性作用,因此在微服务拆分时我们需要重点考虑它们。我列出了以下主要因素供你参考。
1. 基于领域模型
基于领域模型进行拆分,围绕业务领域按职责单一性、功能完整性拆分。
2. 基于业务需求变化频率
识别领域模型中的业务需求变动频繁的功能,考虑业务变更频率与相关度,将业务需求变动较高和功能相对稳定的业务进行分离。这是因为需求的经常性变动必然会导致代码的频繁修改和版本发布,这种分离可以有效降低频繁变动的敏态业务对稳态业务的影响。
3. 基于应用性能
识别领域模型中性能压力较大的功能。因为性能要求高的功能可能会拖累其它功能,在资源要求上也会有区别,为了避免对整体性能和资源的影响,我们可以把在性能方面有较高要求的功能拆分出去。
4. 基于组织架构和团队规模
除非有意识地优化组织架构,否则微服务的拆分应尽量避免带来团队和组织架构的调整,避免由于功能的重新划分,而增加大量且不必要的团队之间的沟通成本。拆分后的微服务项目团队规模保持在 10~12 人左右为宜。
5. 基于安全边界
有特殊安全要求的功能,应从领域模型中拆分独立,避免相互影响。
6. 基于技术异构等因素
领域模型中有些功能虽然在同一个业务域内,但在技术实现时可能会存在较大的差异,也就是说领域模型内部不同的功能存在技术异构的问题。由于业务场景或者技术条件的限制,有的可能用.NET,有的则是 Java,有的甚至大数据架构。对于这些存在技术异构的功能,可以考虑按照技术边界进行拆分。

总结

相信你在微服务落地的时候会有很多的收获和感悟。对于 DDD 和微服务,我想总结的就是:深刻理解 DDD 的设计思想和内涵,把握好边界和分层这个大原则,结合企业文化和技术特点,灵活运用战术设计方法,选择最适合的技术和方法解决实际问题,切勿为了 DDD 而做 DDD!

思考题

谈谈你在用 DDD 实践过程中踩过什么样的坑?有什么好的建议可以分享给大家?期待你的留言!
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 22

提建议

上一篇
18 | 知识点串讲:基于DDD的微服务设计实例
下一篇
20 | 总结(二):分布式架构关键设计10问
 写留言

精选留言(29)

  • 南山
    2019-11-27
    老师这篇是看的最慢的一篇,一边看,一边思考现在团队负责的业务以及服务,有很多很多值得警惕和借鉴以及深思的地方! 前面在看微服务实例讲解的时候一度陷入战术模型中出不来,想着现在团队微服务的结构以及实现方式像,也不像ddd。一直也知道战略比战术更重要,直到看到今天的文章,才恍然,从ddd学到的更多是他的分析过程的方法论,设计思想,至于实际落地,要结合各种因素来权衡利弊做更合适现状的架构设计方案和具体实现方式。 注重道而不是术!
    展开

    作者回复: 把DDD当成工具和方法,不要让它束缚了思想。

    28
  • silentyears
    2020-02-04
    请问老师,DDD,必须采用充血模型吗?

    作者回复: DDD主要采用面向对象的设计方式,不同于三层架构的贫血模型,实体只有get和set方法,所有的业务逻辑都放在业务逻辑层,耦合度比较高。采用充血模型后,会让领域模型更接近实际场景,也会让四层架构中各个实体行为,领域服务等职责更加清晰,便于进行服务和方法的组合和解耦。

    共 2 条评论
    9
  • Peter Yu
    2021-01-06
    老师,我有个疑惑:比如有个服务AB被拆分为服务A和服务B,在拆分之前有个service调用: Adto fetchAbyId(Long id),拆分后这个方法提供了远程调用,B服务调用这个远程方法时需要一个对象来接收结果,问题:是在B服务里面建一个类似Adto的类用于包装结果,还是引用A服务的一个common包依赖进来(里面有Adto的定义)。个人感觉如果用引入依赖的方式,会暴露较多信息,耦合度也高了;但是重新在B服务里面建一个新类,微服务拆分时工作量会比较高(A和B之间调用比较多的情况)。感觉都挺头疼的…望老师指教
    展开

    作者回复: 个人建议采用在B服务中建立一个新类的方法。 采用依赖包的方式会混淆限界上下文边界,A微服务中很多内容可能会不自觉的渗透到B微服务内,而且像你说的那样,两者之间会产生高耦合。

    共 5 条评论
    8
  • 悠闲喵
    2020-12-25
    欧老师你好,我们有个消息发送平台,主要是发短信、邮件、站内信功能;目前想拆分为 任务接收、任务拆分、消息发送、消息跟踪等几个微服务。请问是否存在过度拆分的问题?

    作者回复: 不清楚您这个消息发送平台数据和业务量大不大。我来分析一下,看看是否正确? 从业务边界来考虑,另外,再考虑尽量减少微服务之间的数据传输和交互,我感觉任务接收和任务拆分,属于任务管理的限界上下文,而且数据关联度相对较高,它们可以放在一个微服务内,这样可以减少微服务之间的数据交互。而消息发送以及消息跟踪,属于消息管理限界上下文,它们可以放在一个微服务内。 我们可以做好这四个不同聚合的边界,然后拆分为任务管理和消息管理两个微服务。如无必要,尽量不要过度拆分。

    5
  • 狮锅艺
    2020-03-18
    实践的过程中,要时常来回顾一下这些原则。
    5
  • 小谢同学
    2020-02-28
    感觉ddd和devops这些系统化概念都应该结合企业自身实际情况进行逐步的落地,一是不能盲目跟风,二是不能贪图一时之快,还是要因地制宜

    作者回复: 是的。

    3
  • Jxin
    2019-11-27
    感谢老师的分享。限于篇幅啊,意犹未尽的感觉。

    作者回复: 很多的感悟,^_^。

    3
  • vivi
    2019-12-05
    刚开课就跟着学习,课程结束了把新项目用DDD实践了下,收获满满。非常感谢作者~

    作者回复: 以后多交流。

    2
  • y3
    2019-11-29
    考虑新老系统之间服务和业务的兼容,必要时可引入防腐层。请问老师,这里的防腐层指的是什么?

    作者回复: 防腐层是为了屏蔽系统之间的相互影响,保证领域模型的纯洁性,在微服务增加一些代码进行业务适配,比如新老系统的协议转换等。

    2
  • by
    2021-02-06
    老师 我有一个疑问 最近项目也在做DDD的重构。看了老师的Demo,有一些不理解的地方。 1.LeaveRepositoryInterface 这样的命名方式是DDD的规范吗,确实很少看到这样的命名方式,是不是这样更好 lleaveService。 2.LeaveDomainService为什么要加一个Domain。

    作者回复: 1、DDD在实际战术落地的时候其实没有具体的规范,你可以根据团队的习惯来定义这些名称,只要大原则不出问题即可。 2、因为在DDD分层架构中有两种类型的服务:应用服务和领域服务,加domain是为了有所区分。

    1
  • 北天魔狼
    2019-11-27
    感谢老师!依赖倒置,代码复用,这俩是最浅显的好处。按照严格模式分层,天然支持微服务,支持多开发语言。🙏🙏🙏

    作者回复: 谢谢你的陪伴。

    1
  • 兔子
    2022-12-10 来自北京
    请问10-12人,是维护一个微服务的团队规模吗
  • Steven
    2022-04-19
    不知道老师是否还关注这里,有问题不太明白: “拆分后的微服务项目团队规模保持在 10~12 人左右为宜。”,这个是不是有点多啊? 虽然说是“两个披萨”原则,但看国外一般是说不超过10个人,还有说不超过9个的。 microservices.io,说是5-9个人。 国内还有一种说法“三个火枪手”。 其它同学对此怎么看?
    展开
  • 徐李
    2021-12-22
    充血模型,是在解除老师的专栏之后才了解到的。目前我们开发用的mvc三层架构,基本都是贫血模型,业务处理都在service层。对于DDD. 充血模型更适合,在实体中,值对象中完成一定的业务处理,就在这个实体里面闭环掉了,就更像是实际的生产情况。这里比以前的面向对象编程更面向对象。
  • trier
    2021-10-28
    过了很久又回来看这个专栏,这次是带着问题来的,不知道老师还有没有空回来解答。 我们在落地时,会发现在应用层会很多不同领域间实体的转换,而且导致代码看起来很繁琐,不知道这些问题该如何解决。是不是应该在应用层再定义一层防腐层去处理领域间实体的转换? 如果是,目录结构如何比较好?
  • paulmin
    2021-10-04
    拆分后的微服务项目团队规模保持在 10~12 人左右为宜。 -- 这个是特指后端研发的人员规模吗? 但若是建设企业大中台,按商业能力中心来划分模型和落地微服务,这个人数是偏少了。
  • 百威
    2021-03-29
    我理解ddd的战略设计应该更加也是更优先被推广出来吧,战术的实现可以多种,但战略上的领域设计应该是明确不变的
  • 厚德载物
    2021-02-21
    太美了,我有信心了,我在没看视频前就是计划用绞杀模式,修缮无法根治,炉罩压力太大,赞赞赞
  • 金龟
    2020-11-13
    老师,我们在做语音助手。其中有个服务是做对话,也就是一句query的回答。 然后对于一句话,我们其实会配置多个回复,每个回复上我们会有些判断条件,然后这些判断条件会去读取对象存储上的文件,用来判断条件是否满足。满足就进行回复。我想问下这个服务应该怎么设计会比较好

    作者回复: 你们这些回复是不是要在很多问题上要复用?这些判断条件是不是一些规则,也需要复用? 如果是,问题可以是一个聚合,它会引用回复ID以及名称等信息,形成一对多的引用关系。判断规则可以作为配置信息独立为一个聚合,主要提供规则查询服务,根据规则找出回复记录。 由于不太清楚更具体的场景,不知道分析的对不对。

  • 2020-11-10
    深刻理解 DDD 的设计思想和内涵,把握好边界和分层这个大原则,结合企业文化和技术特点,灵活运用战术设计方法,选择最适合的技术和方法解决实际问题,切勿为了 DDD 而做 DDD!