绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
小团队也能做DDD-上篇
2022-10-12 15:53:20

作者简介:胡正军,御家汇架构团队负责人,坐标湖南长沙。开发,运维,测试,大数据均有负责,杂而不精。乐于复杂系统重构,拥抱DDD



上一篇文章说了小团队也能做中台,里面提到我们用了领域驱动设计(DDD)这个方法论来开发中台,本文对我们为什么使用DDD做一个分享,下一篇再讲我们怎么实践的DDD

 


DDD出现得很早,2004Eric Evans写了《领域驱动设计:软件核心复杂性应对之道》,但使用并不广泛,拿长沙这边来说,使用的企业可以说是寥寥无几。



近几年DDD随着微服务反而火了一把,各种技术群里一言不合就DDD,也有一些在线培训和书籍出版,比如人保的架构师欧创新就在极客时间开了专栏,预计本月还会出版一本书籍。然而大家虽然谈论得多,真正落地的少。


我认为原因有:

< 1 >系统不复杂,没必要用DDD; 

< 2 > DDD概念多,理解难,懒得去学

< 3 > 就算一个人学了也没用,需要团队都用起来,由于人性本懒惰,DDD对业务和开发都要求有点高,所以不想做,还是CRUD爽点。


 

DDD是为了解决复杂业务系统构建的一套方法论,有战略设计和战*术设计两大块。
战略设计的核心是统一语言和限界上下文,战*设计的核心是独立领域逻辑层。

复杂业务系统为什么要用DDD,可以从3个维度来说明。
3个维度分别是软件开发过程复杂系统设计模块编码

可以看到这3个维度是一级一级降低维度的,软件开发过程涉及多个岗位(产品经理,开发,测试,业务运维),属于开发流程这个维度,复杂系统设计涉及的岗位是架构师或者开发,属于架构设计这个维度,模块编码涉及的岗位主要是开发了,属于代码编写这个维度。我们先分析这3个维度上有什么问题,再看DDD都能提供什么指导。


一、软件开发过程 


一个简单系统的开发是不需要分这么多岗位的,大学期间做的一些兼职小项目就是一个人把需求,开发,测试,运维全做了,效率很高,如果说搞多个岗位多个人来做,反而效率很低。

不过这种情况随着项目复杂度上升就不一样了,一个人是没法搞定所有事情的,于是我们设立了各种岗位,通过团队分工让专业的人做专业的事情。

拿开发这个岗位来说还分了前端和后台开发,也是因为技术复杂性的要求,必须分工。一个理论是建立知识壁垒并能有效交换能提高效率,这个方面详细的论述可以看下八叉说的视频。通过专业分工来提升效率在生活中有很多地方都有体现,比如流水线作业什么的,这在国富论和科学管理中也有提到,这个方面详细的论述可以看下八叉说的视频。

 

这里得到个结论:

软件开发过程中的技术知识通过分工建立知识壁垒来提升效率


但是软件开发还有一类知识是没法通过分工解决了,上图箭头表示的就是领域知识必须随岗位传递,需求分析师把业务转换成需求文档,然后这个需求文档里面的业务知识必须传递给开发,开发必须理解清楚了才能干活,现实中确实出现了很多这样的情况,开发需求都没理解透,就开始设计编码,后做出的东西跟实际业务需求不一样,返工,开发和产品经理矛盾重重。如何让业务知识快速传递,是迫切需要解决的问题。

 

这里得到第二个结论:

软件开发过程中的业务知识需要消除知识壁垒才能提升效率

 


从两个结论看好像有冲突,从个结论看,分工是有意义的,从第二结论看,不分工反而是好的。随着各个岗位专业度的提高,分工是必然的,那么怎么解决业务知识传递问题, DDD的一个核心模式是统一语言,用于解决解决业务知识传递的问题。



统一语言有两个含义。
1、统一交流语言我们把业务名词的含义事先确定好,在业务和技术的各个岗位都统一认知,这样在交流时,由于对业务名词的理解已经同频了,会节省很多沟通成本。

这里举个没统一语言的例子,我们公司有个有个渠道的概念,每次讨论这个渠道就容易出现每个人理解不一样,有的把电商平台一个店铺当渠道,有的把电商平台当渠道,还有人理解成其他含义。这是因为每个公司都有些历史遗留原因对名词的定义变味道了。


2、统一领域模型和代码模型我们知道一个系统重要的知识沉淀就是代码和文档了,当然很多公司都没文档,程序员嘛,都一个尿性,恨别人不写文档,恨自己写文档。想一想当一个新人来接手一个项目,这个项目有一份设计文档和代码,设计文档里面描述的业务模型跟代码模型不一样时,我想这个新人是蒙圈的,到底哪个是对的,心里会骂娘。当业务模型和代码模型一样,会大大降低我们系统的认知复杂度。

 

二、 复杂系统设计

为了降低业务系统复杂性,一般是通过分而治之的方式,现在流行的微服务就是基于业务维度进行拆分,那么到底怎么拆分。

如下图,每个点代表一个领域对象,当很多个对象混合在一起时,设计者或者开发者需要了解所有领域对象,无疑增加了复杂度。如果能找到一种方式合理划分边界,让联系紧密的对象聚合在一起(比如下图中形成ABC三个聚合),聚合之间通过接口*交互,那么接口数量会是少的。有点类似K聚类算法,总能找到一种优的划分方法,当然很多情况下依赖架构设计者的经验。DDD的限界上下文提供了一种指导思路,限界上下文划分好后,一个限界上下文可以对应一个微服务。

这里同样运用了建立知识壁垒可以提升效率这个理论,开发A服务的人实际上不需要了解B服务的内部实行,只需要了解B提供的接口即可,A服务的领域对象数量已经大大下降了,无疑降低了复杂度。


拿一个大学里面的一个学生管理系统来说,涉及到如下领域对象:学生,教师,角色,权限,菜单,课程,课程排班,签到等。

现在来划分限界上下文,因为学生和教师都需要登录和权限,我们一种上下文分解的方法是鉴权上下文<角色,权限,菜单,学生,教师> 授课上下文<课程,课程排班,签到>

从关联度来看,这两个上下文需要4个接口来做交互。

由于DDD里面规定限界上下文一个领域对象有确定的含义,学生和教师这个两个在鉴权上下文里面是系统的一个用户,放到授课上下文里面代表了两种不同的身份,也就是当我们说到学生时,到底是需要登录的一个用户还是授课里面的学生,含义是不确定的,所以这里缺了一个领域对象用户。

增加用户这个领域对象后,限界上下文重新划分,只需要2个接口做交互。

   



三、模块编码

传统的三层结构是web,service,dao,这个大伙都很熟悉,一个业务流程稍微复杂点会导致service里面的代码变成大泥球,我在上一份工作中,见过一个service类达到上万行,维护及其痛苦,怎么痛苦法呢,就是每次转测试,都会产生20多个bug,改完又会出现新的,一轮一轮无法收敛。我实在受不了,就跟项目经理申请重构,项目经理是不同意的,没办法只能立军令状,就开干了,终一万多行代码变成4000行左右,bug终于收敛。

解决service层大泥球的一个方法是分离复杂性,把service分为应用层和领域层,这块内容可以参考王林在infoq的文章
https://www.infoq.cn/article/A3cgSUWuRulXHl2c_dUr,文章中提到了一个架构原则是业务和技术分离。领域层放业务逻辑,应用层放技术逻辑,复杂性降低,并且领域层更便于测试。

 

DDD战*术设计就是把业务逻辑放到核心位置,整洁架构和六边形架构也是如此,跟业务相关的代码写到实体,值对象和领域服务里面,一些事务,缓存等技术相关代码写到应用服务里面。

例说明:购物车有个操作是添加商品到购物车,应用层ShoppingCartApplicationService把事务,缓存处理掉,核心业务逻辑在ShoppingCart这个领域对象里面。

领域层代码如下:


应用层代码如下:

 

 


四、总结

DDD的统一语言在软件开发过程中能帮助业务知识更好的传递,限界上下文能指导复杂系统的拆分,战*术设计能帮助代码更好的分层。

有的公司虽然没有用DDD这么一套组合拳,可能已经在利用这些好的方法来开发软件,其实DDD提供一套方法论,也不是必须全部用上,完全可以只借鉴其中一部分。比如DDD的统一语言是通过降低业务知识的传递提升效率,可以应用到实际开发中。

举一个实际的例子,很多企业内部系统也采用了前后端分离,然后用前端开发和后台开发两拨人分别开发,再接口联调,联调成本很大,完全可以换一种方式,让后台开发来做前端,节省联调成本。

那这里可能有人有疑问,后台开发做前端会不会效率很低?其实只要让前端提供了架子和前端组件,再辅助一些组件使用文档和代码示例,后台开发完全可以很快上手,而节省的联调成本会很可观。


后想说一句,方法论能提供指导,但也不要生搬硬套,需要根据自身情况进行裁剪,灵活运用。


——————————  END  ——————————


分享好友

分享这个小栈给你的朋友们,一起进步吧。

嵌入式到底应该选择驱动开发,还是应用开发
创建时间:2021-12-23 14:22:12
很多初入职场的小伙伴都会纠结于是选择驱动开发,还是应用开发。今天我们就来聊聊,希望能给初入职场的你带来一点点的帮助。这里所说的主要是带操作系统(Linux或Android)的开发,因为像单片机,我们比较少区分驱动开发和应用开发。
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

栈主、嘉宾

查看更多
  • yctbibrz
    栈主

小栈成员

查看更多
  • LCR_
戳我,来吐槽~