一 概念
领域:需求背景、业务描述;
子领域:对领域划分后的子系统;
设计:划分子领域、分层的过程
驱动:由领域驱动,而非数据库、面向过程之类的驱动;说人话就是先划分子系统,再分析可能有的业务、可能有的流程,分析出哪些地方可以提取公共方法,哪些地方必须单独写方法,再进行开发。【相对的,传统的开发可能图尽快开工,没有分析完备就开始开发,需要加什么功能时就加个新接口,新service方法,新dao持久方法一整套,功能加多了才可能发现一些能抽出来的公共方法,但项目已经臃肿,不好重构。
CQRS:中文翻译为“命令与查询分离”,就是读写分离,具体可表现为分别设置读写数据库,发生CUD操作时通知读库同步。体量小的项目没必要分离。分库后会面临同步延迟,同步失败数据不一致等问题。
实体:有唯一标识,持久化、可变的对象,比如持久化框架中的entity。
值对象:没唯一标识的对象,不同场景下也可能是实体,我理解的比如do,与entity字段相同的dto之类。
领域服务:封装的方法,我理解的就是不作为接口暴露在控制层,由业务层方法调用的那些方法,可以对实体、值对象做一些操作。
聚合:bean?但不是entity,比如user用户聚合里可以包含他发布的帖子集合titleList,我理解就是dto之类。
资源:类似dao,但不止是表实体才有对应的dao,聚合也可以有,提供此聚合的原子操作,比如user用户聚合资源可提供发帖方法,有点乱,这不就和领域服务一样了么。
二 举例
需求:开发一个在线考试系统,包含用户(注册、登录),试题(录入,修改),考试,判卷,结果统计展示等功能。
a) 传统(前后端分离,springboot框架,mvc模式,控制/业务/持久分层):
按照需求划分的子系统,分别设计用户表、试题表,选型框架,开发实体类、两个表的dao持久方法,开发“用户注册、用户登录、试题录入、加载题目、回答选项、加载回答、标注回答是否正确、成绩查询”等前端页面及controller控制层接口,然后每个接口调用各自的service业务层对应开发的处理方法,业务方法中看需要调dao持久方法。
如图,可分层、按功能划分为这些调用关系,进行开发。
若后续继续增加功能,如更多的统计方式、自动判题、更多的题目类型、用户权限管理等,可能由于系统臃肿,开发时继续为新接口增加新的service方法(图中增加更多纵列)。
例如上图,新增自动判题功能,按顺序开发完后,会发现两个流程有一部分相同逻辑(粗红框),仅判断题目正误部分不同。
一般,在设计时就考虑到这种情况时,会提前将“更新回答,若此为最后一题则统计储存成绩”逻辑提取出公共方法A,这样后续增加自动判卷时判断完直接调用A即可;或者,发现当前系统还很简单,改造原始判卷方法,抽取公共方法也可以。
但系统很大时,尤其有的项目还会有SpringContext.getBean("xxx")这种反射取类,很难判断改造老方法对系统的影响,所以后续重构、抽取公共业务会越来越难。
b)ddd
详尽分析各个子系统、业务,从业务角度理解,将每个步骤、每个潜在的小流程都做成service方法,尽早尽可能准备好图3中的这种公共方法,新增同类功能就能开发特有流程后,直接调现有步骤(
三 PS
阅读 领域驱动设计(DDD)实现之路 后,有了些新理解
领域驱动在具体项目的表现,
1 数据库方面
领域驱动概念可附带CQRS读写分离,因为增删改查指令、查询在概念上确实差别蛮大,有时同一个内容的修改和查询也能划分到不同领域(比如都针对上述考试系统的“题目”实体,编写录入题目、考试加载题目却可划分到不同子领域);并且在数据量大时,读写库分离也是增加效率的一个方法。不过小项目数据库用着和传统差球不多。
2 持久层
除了表、实体类对应的dao,还有聚合的dao,这些都是资源。(见一里的描述。我还是不懂)
3 业务层
领域服务,就是提取的公共方法罢,其它没体会到和传统有啥不同。
4 控制层
感觉没啥不同。
5 子系统划分
由于有领域划分子领域的概念,每个子领域就是一个子系统,每个子系统有自己的微服务,顺理成章就成了分布式。另外还提到“值对象”在不同领域表现不同,举例可以是:考试系统下划分了录题微服务,考试微服务;录题微服务中的题目对象就需要录入齐全的字段,而考试微服务只需要查询题目的题干,所以两个微服务下的“题目”对象/值对象就不一样。