记得在规划设计微服务架构的时候,张队长给我的一个提醒我至今记忆犹新:为什么你的设计蓝图中没有看到DDD的影子?随着对充血模型领域认知的深入,越来越感受到DDD的重要性。于是在网上到处找,做学习笔记。DDD的内容很多。在我看来,它与传统的贫血不同。核心点是将原有传统贫血模型中的业务逻辑层抽离出来,整合到领域层中。这样,面对复杂业务的大规模变化,我们只需要关注Domain即可。回到正题,我们需要了解的是微服务和DDD是什么关系?因为在互联网时代,软件面临的问题域比以前复杂得多。这种复杂性来自于不断扩大的问题域本身,也来自于这种规模增长带来的创新变革和挑战。但是,一个人和一个团队,对复杂事物的认知是有限度的。面对如此复杂的问题,唯一的办法就是分而治之。划分主要考虑的是如何划分;治理是指事业部各部分要能够独立运作,相互配合,完成总体目标,能够应对外部变化的影响。微服务的缺陷微服务架构在分而治之方面提供了很好的理论指导和最佳实践。微服务是解决复杂问题的灵丹妙药吗?事实上,很多团队在应用微服务架构构建系统后,发现并没有完全解决这个复杂性问题,甚至还带来了一些其他问题。例如:服务并没有解决复杂系统如何响应不断变化的需求的问题,甚至加剧了这个问题。当需求发生变化时,需要花费大量精力来确定哪些微服务受到此变化的影响。这些服务的多个团队需要通过无休止的争论来决定哪个服务多,哪个服务少来改变。那么测试组还需要做这种昂贵的联调测试。即便如此,开发组还是不放心,不得不小心翼翼地切流,通过一系列的开关控制来做灰度发布。从业务的角度来看,微服务架构并没有避免这种鸟枪式的修改。甚至激怒了他,这是为什么呢?一个重要的原因是微服务架构在这个维度上没有得到全面的考虑。DDD功能我们在做这类工作的时候,需要考虑哪些维度?我认为我们至少需要考虑三个维度:功能纬度,质量纬度,比如性能,以及可用性工程纬度。微服务对第二个给出了很好的指导,对第三个也给出了一些建议。然而,对于第一个功能维度只给出了非常有限的指导,这就是为什么领域驱动设计(DDD)随着微服务的流行而被重新强调的原因。DDD弥补了微服务在功能划分方面没有很好指导的缺陷。因此,在面对复杂问题和构建系统时,它们是互补关系,在系统拆分时,它们可以很好地合作。只是他们看待系统分裂的角度不同。微服务中服务的重点是DDD所提倡的六边形架构中的领域层。拆分案例接下来,结合DDD和微服务来拆分一个复杂的系统。关于领域,我们把企业的业务范围和在该范围内开展的活动称为领域,与软件系统无关。该字段将被划分为多个子域。比如我们的电子商务系统会有:商品子域订单子域库存子域等等。在不同的子域中,不同的概念有不同的含义。因此,我们在进行领域建模时,必须要有一个明确的领域边界,在DDD中称为boundedcontext。它是系统内部的一个架构边界,决定了系统的架构。本书中对系统内部架构边界划分的简洁方式是这样说的:系统架构是由系统内部架构边界和边界之间的依赖关系决定的,以及系统中各个组件之间的通信和调用方式。系统是无关紧要的。我们常说微服务的服务调用本身只是一种比函数调用稍微贵一点的拆分应用行为的形式,与系统架构无关。因此,复杂系统的划分,首先重要的是划分内部的架构边界,即明确划分上下文以及它们之间的关系,这对应于我们前面提到的功能维度。这就是DDD派上用场的地方。其次,我们考虑如何基于非功能维度来划分,这也是微服务可以发挥优势的地方。比如我们把系统分为三个上下文,ABC和ABC。三个上下文的代码可以运行在一个部署单元中,通过进程内调用完成操作。这是一个典型的单体架构;它们也可以运行在一个独立的部署单元中运行,通过远程调用完成操作。这就是现在流行的微服务架构。界限清晰的好处是我们更多的是两种架构模型的混合体。比如A和B一起是一个部署单元,C是另外一个独立的部署单元。这往往是因为C非常重要,它的并发访问量非常大,或者它的需求变化频繁。拆分C有以下优点:资源倾斜使用弹性设计模式:如重试、断路器、降级使用特殊技术:例如Go语言有独立的代码库:有独立的团队和运维人员,而A和B运行期间的隔离和不干扰这四点正是服务架构所关心的。它是从非功能纬度的角度来看拆分的问题。他关注的不是系统架构的逻辑边界,更关注的是应用行为的分离。那为什么不把A和B都拆分成一个单独的部署单元呢?这会带来更多的收益,但也会带来额外的成本。该体系结构应该能够发展。在业务开发初期,要注意系统架构的逻辑边界,保持逻辑边界清晰,关系正确。业务量的增长逐渐分裂,这是DDD和微服务架构结合应用带来的最大收益。在单体架构中,很难保持架构的逻辑边界不被打破。如果逻辑边界不明确,可能需要拆分服务器时无法拆分。此外,没有人能一下子就正确定义逻辑边界。即使上下文定义不正确,DDD聚合根的概念也可以保证我们可以演化出更合适的上下文。DDD限界上下文通过实体和值对象对领域概念进行建模,一组实体和值子对象属于一个聚合根。根据DDD的要求,聚合根用于保证内部实体规则的正确性和数据的一致性。外部对象只能通过ID引用聚合根,不能引用聚合根内部的实体。聚合根不能共享数据库事务。之间的数据一致性需要通过最终一致性来保证。基于这些约束,未来可以根据需要将聚合根升级为上下文,甚至拆分成微服务,相对容易。
