〇、一些阅读的前提注意
本文仅仅是个人的理解,会随着个人理解的逐步加深而修改一些先前的错误理解。这里非常推荐阅读《实现领域驱动设计 (美)弗农著》,《领域驱动设计》,《大数据之路》以及阿里技术专家的DDD文档,这些都对于理解DDD大有裨益。
其次,切莫随波逐流,适合自己的未必是所谓的潮流。
一、回归业务:再谈SOA和微服务
1、再聊聊SOA
在常见的开发场景中,我们基于SOA理论所构建的服务是精确定义、封装完善、独立于其他服务所处环境和状态的函数。
SOA 本质上是服务的集合,服务之间彼此通信,这种通信可能是简单的数据传送,也可能是两个或更多的服务协调进行某些活动。服务之间需要某些方法进行连接(Restful或者RPC协议等)。
每一个服务并不一定是一个完整的业务,一个完整的业务包含角色、参与对象以及动作等信息。一个完整的业务链可能会涉及到多个服务,但是每一个业务团都能构建可复用的服务。
SOA的构建思路,在于充分的解耦,其要求包括了:明确接口、闭包封装、模块化构建、粗粒度、松耦合、互操作性、兼容和策略声明。
2、再聊下微服务
微服务其实是SOA思想的延伸,旨在能将服务进行有效的编排,如活字印刷排版一样构建整个项目,每一个服务犹如每一个字块,是与其他字块独立的。想要构建出整体项目,需要像排版印刷板上的文字一样,合理的排列组合服务。
微服务的核心特点为:服务足够小,且专注,服务间可以构建轻量级的通信机制,保持松耦合、独立部署。
微服务的思路可以说非常高效,它一定程度的降低了服务的复杂性,同时维持了服务的水平扩展和弹性扩容,降低了从开发到部署到维护的难度。每一个服务仅仅需要专注于自身的业务逻辑,对开发周期的可并行化提供了理论基础。
当然,当服务逐渐的细化时,会出现一个业务需要调用好几个服务,这就是服务爆炸。因此如何划分服务,有效的将业务拆解但是又不能分到像把一碗米饭分成一个个米粒儿一样细致,再次降低服务间调用的复杂度,就是需要设计者投入足够的思考。
3、聊聊业务
将业务团构建成可服务的出发点在于复用,再理解下该场景下的业务:即流程的流转。例如下单业务,整个流程为一个业务,包含用户角色,客户端角色以及订单、订单发送、订单接受并通知。
在常见的构建过程中,我们会构建角色表、构建订单表,完成MVC层。常见的场景中构建的角色如下:
@Lombok
public User( ){
private String userId;
private String userName;
private String address;
private String tel;
...
}
在这种常见的构建中,完成一个订单服务,要执行Mapper,然后构建一个Impl完成业务逻辑。当然,User的作用仅仅存在于接受一个用户的信息或者提供getter、setter。
这是常见的贫血模式,当服务的对象或者需要调用的方法发生改变时,对旧代码的重构量是极大的,导致很多原有正确的逻辑无法有效的复用。或者说在基于现有的功能进行扩展时,如果稍有变动,就需要重新构建一个User2,用于接收变更后的信息。
很明显,这样的过程,并不是十分合理,尽管在当前的场景下,这种书写方式,更符合大多数开发者的逻辑。
二、为什么选用DDD
1、绪论
软件发展至今,微服务是大型稳定系统的首选架构方式。在常见的环境中,我们所谓之业务,大体分为两类:ToC、ToB。而无论是哪类业务,都存在着转向微服务架构方式的可能。
而在面向于捞钱的项目开发中,软件在步入生产环境后,会面临着种类繁多、花样齐全的产品迭代,包括但不限于:功能升级、后端优化、功能添加、功能整合等等。那么一旦某项需求涉及到了多项服务,那么该如何进行维护就成为了公司发展的重中之重。因此有些问题我们不得不丑话说在前头,我们这里涉及的产品,需要有一定体量。简单的单机系统不必如此麻烦,在选用技术时,切忌“杀鸡焉用牛刀”,以避免没有必要的效率浪费,小型的项目用DDD实为一种损失精力的做法,这个问题在之后的建模时我们会看得到。而此中的体量大致是:已经步入微服务架构且至少有数个深度纵向的业务,有数个切面业务贯穿系统,系统复杂度很高,业务迭代速度适中且在业务中存在着部分耦合。例如传统的电商系统、咨询平台。
微服务和DDD似乎有着天然的相性,Martin Fowler的《重构》一书中提及到:每次对原有系统进行修改调整的时候是一个非常好的重构契机,这也就是说,DDD可以在微服务实施后,逐步的在迭代中,通过一次次重构逐步实现,不必大费周折的在初期就确立DDD影响基础功能的开发速度,但是这里也要提一句的是,如果能有效的预测出软件复杂度很高,如果一开始就有能力选取DDD时选用DDD是十分明智的。
作为微服务的提出人,似乎对服务到底分多大没有给出过于明确的建议。因此我们在DDD中的界限上下文,更适合用于划分微服务。在一般的分摊思维中,我们常常会陷入一种误区:一个业务即一个服务,但这是一种以偏概全的想法。实际上的服务划分,要考虑诸多问题:如何部署,怎么进行具体实施,数据来源是否封装,在当前云原生的环境下,如何切合云原生的思想等等问题,因此用业务划分属实让人觉得“吃相难看”。
而根据实际场景中的问题,利用界限上下文的思想规划出的微服务,能有效的解决至少两个问题:服务到底多大,服务间如何解耦。在单个服务的内部,利用DDD也能有效的构建出极为优质的代码,便于实施各种迭代和融合。至此,我们其实以及意识到了,DDD是一种贯穿产品到开发的思想,并非单独面相开发的架构思想。在完成DDD时,需要一定的管理能力和人员能力,这些都是实打实的实施基础,切不可为了DDD而DDD的冒进。
在本套文集中,我们主要面相的开发人员的一些技术和思想,因此如何操作DDD这个问题在本文中会更加的偏向开发实施过程中的“注意事项”,因此我们不会涉及过多产品如何应用DDD去构建出业务域。
2、开发如何理解DDD
个人认为,DDD在开发中所应用的概念,如同利用各类细胞组成生物个体并生成个体间交互场景的过程。这个理解似乎听起来很麻烦,但是我的理解是:DDD就是森林状的开发系统,每一个基础实体都可以看做森林的叶子。
我们以“斧子砍树”来举例,在该场景中,设计到两个交互实体:斧头、树。我们暂且吃相难看的用简单的方式划分这个场景,那么就是分别构建出斧子、树这两个实体,然后进行实体间进行“友好交流”。那么为了实施DDD,我该如何构建出这两个实体呢?
对于一棵树而言:从各类细胞开始,逐渐会聚合成各种组织,然后聚合成树。那么大致的结构图应该如下
对于该图像,可以产出对于树(一个服务)而言,我们没有粗暴的通过根、茎、叶、花的业务方式进行划分,而是通过不同特性的组织进行划分,而对应于实际的开发就是,数据服务、各类交互服务、认证服务等等。而对应的根部表皮也好,原生分生组织也好,都可以是作为一个个领域的聚合根(概念会在后续提及)。在各个领域的划分后,我们可以将领域进行聚合,由此生成有效的界限上下文,也同时规定了:树到底需要用那部分跟斧子进行“友好交流”。
而对于斧子而言,是构建出两个零件:手柄和斧头。其结构图如下
同样的,这里规定了用斧头部跟树进行交互。
为了方便理解,我们需要构建一下树内部的界限上下文,如图:
这里,我们额外画出了三角形,即躯干,该处为树的领域和斧头的领域的交互地点,而在实际开发中,即对应防腐层。在实际的实施中,为了防止软件耦合带来的服务间相互入侵的情况,我们必须构建出一定的防腐层来有效的减缓代码腐败的速度,留有足够的时间方便代码在不断的重构中一次次新生。因此躯干这个防腐层,有效的保护了斧头与树交互时,对树的过度侵害,这一点在实施DDD开发时需要十分重视。
至此我们简单的叙述了一下DDD以及DDD在开发实施中的一些简单概念,当然,到此依旧会云里雾里,所以我们需要带着一些疑惑与简单理解再次从头深入DDD。