再探DDD以及美团的“野路子”

语言: CN / TW / HK

theme: smartblue

前言


之前我写过一篇关于DDD文章,大白话之辩论DDD,阿里面试中台化理解,这次心血来潮是因为最近在看掘金的一个关于领域设计的课程,然后下面谈谈我读后感,以及在我现实项目里面究竟用到哪些内容,最后还有今天看的美团的一篇 广告平台化的探索与实践,可以发现路子挺野的,因为我还没到那个层次能驾驭他这种设计方式,后面也会讲到,感兴趣的同学赶紧搬个椅子听听~

image.png

掘金课程相关感受


深入浅出 DDD就是这个课程,接下来我会大概讲下里面的内容,然后说下我的感受,最后谈谈我现在项目所做的领域改造,以及还有哪些缺陷需要改进。

课程内容

课程内容就是按照概念、然后分点举例子介绍,然后最后也有自己的demo,可以说适合小白入门的课程。但是属于入门级别的,明白我意思吗,深入一层在实际项目里面应用是如何的效果,究竟我项目哪些地方要做这样的改造,还是说全盘采用DDD理念,文章比较少钱探讨的。

我目前项目关于领域改造

我们项目从一开始就决定采用领域模式设计方式,当然也是第一次接触,有些概念是不太清晰的,下面大致讲下。首先我们采用经典四层的DDD架构,为什么呢?因为很多中间件基本是固定的,没有必要去依赖倒置,这种架构跟传统的mvc比较相似,接口层、应用层、领域层、基础设施层

应用层一开始不理解,所以很薄的一层,后面的话明白是服务编排的作用,会做领域之间的编排。如果是那种直接一个领域搞定的,就通过领域服务处理即可。传统设计最复杂最多代码的就是服务实现类,虽然这个项目是按照领域来划分,但是没有去改变这个现状,所以导致实现类逻辑会杂

顺下来,到dto跟中间转换,我们也是直接beanutils去复制参数,并没有采用防腐层那种去隔开,然后像数据库entity,ddd里面概念叫实体po,一般跟业务转换需要仓储层来转换,我们也是直接拿entity用,下面我再详细说这样做的原因。

对于外部http请求、rpc请求,采用防腐层acl去隔开,当然这里采用策略模式,不管是适配器模式还是什么模式,这里本质就是为了将系统跟外部隔开,就是系统需要什么东西,外部提供什么,而不是外部有什么我拿什么,减少强依赖。

类里面字段逻辑,还是采用贫血模型,受之前开发思路的影响。

缺陷

  1. 领域层逻辑很多,比较杂。

解决方案:按照业务包再分包,比如说商品领域,mybatis下面会有spu、sku、他们之间绑定关系,还有一些附加的东西,这些都是有自己servicImpl对吧,再加一个商品领域模型,这个抽出来一个模块,这就很清晰,哪个模块对应什么servicImpl,不然可能需要看代码注释半天才知道这是之前做过什么需求。

  1. 没有仓储层转换

A: 这个不算缺陷,我们采用mybatis-plus自带封装service,本身自带来很多基本crud,为啥你非要自己重写呢?其次没有做po跟业务类转换,原因是不可能一步到位,可能这个业务需要这个参数,另一个业务需要另一个参数,所以就是都给,按需自己在领域层处理

  1. 没有去实现值对象、实体、聚合根这些区分

A: 原因也很简单,目前阶段没有必要上到这个程度。有些扩展性要求很高的需求可以这么搞,比如我们公司基础服务里面有个商品服务,里面会经常变动字段,这时候就需要上这玩意了。值对象是不变的东西,实体是包含整个对象生命周期的字段还有字段逻辑。对于变动很大的需求就好办了,不变了可能就是商品code,对于spu、sku信息是会改变的,可以放在实体里头,有些新加一些额外的字段,比如优化价格,我们可以通过聚合类方式加上去,当我们不需要的时候,不会影响这些不变的内容,这是好处。

DDD思想(进阶)

核心思想

高内聚、低耦合,这个就是领域设计的核心思想。我们从架构上,比如说六边形架构,外层都是适配器,低耦合;比如应用层服务编排,也是为了隔开服务之间依赖,还是低耦合;防腐层是为了跟外部接口隔开,防止接口改动影响内部实现方式,也是低耦合;再到各层的转换,dto到业务bo,bo到po,层层转换,都是为了将他们隔开,字段不用全部输出,也是低耦合

高内聚体现:采用充血模型,将字段的逻辑放在类里面,而不是在功能实现逻辑里头。这样的好处是什么呢?就是代码复用率,比如说a字段在逻辑b、c中都有,如果哪天需求变了,需要改动的地方一多就容易错,其次是提高代码复用率,也就是高内聚。

因需取材

我见过很多上来就是全套搬DDD的东西,有点为了技术而技术,作为研发人,我觉得应该理解为啥会有这种思想的出现,什么时候我们才需要去应用它,那我项目没有全盘采用算不算DDD呢?也算,就是我们下面要讲的什么情况下需要做这层改造。

讲讲架构东西,从一开始是单体服务,当业务上来之后,机器扛不住,需要横向还有纵向去提升整个系统服务能力,业务逻辑也会开始复杂起来,混乱不堪,这时开始有soa面向服务的治理,将这些功能按照业务模块来划分,分割成单独的机器,这样可以提高单独模块的服务能力;再到后来有了ddd领域设计思想,我觉得它是针对saas去做设计的,如果你按照paas去拆分了,其实没有所谓的再去定义领域了,这时会出现聚合层那些。对于saas里面会夹杂很多领域,如果整在一块会跟炒面一样混乱,ddd就是为了解决这个场景的。

什么时候要用到?

当系统业务已经复杂到影响开发,或者说一开始做项目的时候这个业务就是比较复杂,经常变动需求那种,就要这样设计。这让我想起之前面阿里的时候,面试官问我,mysql跟redis去区别是什么,什么时候要用redis?最后我去百度一下,mysql在200以上并发的时候性能越来越差,这时可以上redis。

同样的道理,我们需要观察系统里面的东西是否已经达到需要拆分的级别,而不是强行为了技术而技术

举个栗子:

1、对于外部http请求,我们有个报单的功能,需要对n多家供应商,如果你在一个方法里面写,那就翘翘了,一直叠加上去。这时采用设计模块策略模式,根据供应商编号进行不同报单请求,这就是防腐层思路。

2、当领域层很多service,这时新人接手的时候上手很慢,你需要跟它讲什么表,什么类,这时我们再根据业务再分包,那就很清楚了。这个是不是高内聚体现,对于新人上手也是提效的,这才是有意义的。

3、对于各层转换,我们项目是没有做一层特殊处理的,直接逻辑里面转,原因是没有必要去隔开。因为我们很多逻辑都是直接复制两个类的字段,没有做复杂的逻辑处理,他们之间强耦合就强呗。但是对于一些公共方法,我们确实是可以这么做,因为避免各个方法都去重写逻辑,增加bug风险。

4、如果全套按照ddd去设计,可以去统计下代码量有多少,直接翻上去,对于打包cicd是有影响的,对开发也没有所谓的提效。

这就是我为什么说掘金那课程是初级,因需取材这方面没有展开太多。

美团的“野路子”


今天看到美团年度最佳文章展示,我看到里面一篇# 广告平台化的探索与实践,然后仔细阅读之后,觉得路子太野了,因为我驾驭不了。

它这一篇文章跟ddd很多思想是一致的,所以大家要用心的体会,而不是被浅层的皮毛所限制了思路。

大家想起什么了嘛,值对象、实体的思想,就是把不变、扩展的拆开。

这个是什么思想呢?模块化思想,流程化思想,这是对业务熟悉基础归纳出来的,也是工程师很重要的能力。模块化有多重要,比如说当你要改变东西的时候,我只需要加或者减少、或者变更模块即可,而不是像印度电线杆一样找半天都不知道需要改哪根电线。

路子很野的地方

首先这是什么?就是应用层的服务编排嘛,但是什么区别呢?它是在外部的,有点像聚合层,但是聚合层一般是一个服务去调其他服务整合数据,这个还能通过界面编排,老六了。我说个场景你就明白了,低代码平台,这是跟我做过一个项目很相似的。

里面也是接口编排,然后对结果进行处理,然后返回。有个我吐槽最多的,就是数据处理上比传统会麻烦,比如说我写段逻辑,可能java我很快就写完了,对于低代码平台我还要用js代码,golang代码处理,对于java开发同学是降效的,所以这块我不清楚美团是怎么去实现逻辑处理的,理论上也可以写java代码去整合不同模块数据。

这样做的意义

上面我们也提到了不要为了技术而去应用技术,美团这么做为了让pm、测试、研发都能直观知道广告业务里面有哪些策略,分别实现什么功能,特别是新人入手的时候,所以这里有个业务采集,对于已经实现的东西进行上报,qa同学也知道我需要测试的内容范围。

对于一般公司来设计: 就是新增一层聚合层,还无法实现调度引擎比较前沿的设计,当然对于一般公司已经够用了,所以说美团这样设计比较“野”,褒义词。

总结


希望大家在学习完ddd基础知识基础上,在对业务一定熟悉程度上,因虚取材,这样才能真正发挥它的作用。然后我们通过美团的技术方案,知道思想是更加重要的,而不是局限于形式上,它是我见过不一样的聚合层,或者说服务编排,当然也是我见过的只是没想到会以这样形式呈现(低代码平台)。

image.png