DDD学习
2019-11-13 13:30:55 5 举报
DDD学习,相关概念以及在盒马的实践基础
作者其他创作
大纲/内容
战术部分
战术部分用于落地到代码上,用代码来清晰地表示业务,代码如何分层、如何设计都有一套成熟的指导方案
fix 有向环
Object Model
假设你的机器内存无限大,永远不宕机,在这个前提假设下,我们是不需要持久化数据的,也就是我们可以不需要数据库 Persistence Ignorance:持久化无关设计
manager的逻辑思维
在service层通过我们非常喜欢的manager去manage大部分的逻辑,POJO(稍后章节里的失血模型)作为数据在manager手(上帝之手)里不停地变换和组合这时候儿子犯了点什么错,老爸非常不爽的扇了儿子一个耳光,老爸手疼,儿子脸疼。Manager通常这么做这里,manager充当了上帝的角色,扇个耳光都得他老人家帮忙。
为什么需要DDD?
软件设计更加合理,但不止于此开发者和熟悉业务的人一起工作,加强团队间不同角色的合作能够帮助业务人员和开发人员梳理清楚复杂的业务规则开发出来的软件是能够准确表达业务规则的,设计就是代码,代码就是设计
接口和类
数据库设计是根本,一切开发围绕着这本数据字典展开,形成类似于如下的架构图:
什么是DDD-Lite
DDD战术模式的一个子集,它更多的是关于技术实现层面,往往忽略了DDD的战略部分。但它并不能称为DDD,因为战略模式在DDD是非常重要的
为什么可以服务微服务(战略)
战略部分和战术部分,两者相辅相成。战略部分用于理解、梳理业务,找到核心业务,更好地划分系统
贫血模型
贫血模型:【盒马流程中心】儿子不知道自己的父亲是谁是不对的,不能每次都通过中间机构(Manager)验DNA(son.fatherId)来找爸爸,领域模型可以更丰富一点,给son这个类修改一下:son这个类变得丰富起来了,但还有一个小小的不方便,就是通过father无法获得son(爸爸怎么可以不知道儿子是谁),这样我们再给Father添加这个属性:
最终架构 盒马
领域模型设计中manager地位
领域模型并不完成业务,每个domain object都是完成属于自己应有的行为(single responsibility)就如同人跑这个动作,person.run是一个与业务无关的行为,但这个时候manger或者service在调用 some person.run的时候可能完成的100米比赛这个业务,也可能是完成跑去送外卖这个业务
什么情况需要
DD适用于“业务复杂”的且“需要维护和扩展”的系统。
data model
充血模式 --必须性
领域模型:盒马模式下repository的实现方式
object model 领域模型存在于内存对象里,这些对象最终都要落到数据库,由于摆脱了领域模型的束缚,数据库设计是灵活多变的。在盒马,domain object是怎么进入到数据库的呢?
数据库回归persistence的本质
【存】将对象数据持久化到存储介质中【取】高效地把数据查询返回到内存中总之数据库设计要做的事情就是尽可能的高效存取,而不是完美表达领域模型
失血模型
失血模型:基于数据库的领域设计方式其实就是典型的失血模型,以java为例,POJO只有简单的基于field的setter,getter方法,POJO之间的关系隐藏在对象的某些ID里,由外面的manager解释,比如son.fatherId,Son并不知道他跟Father有关系,但manager会通过son.fatherId得到一个Father。
例子说明
再看看老子生气扇儿子的例子:根据这个思路,慢慢地,我们在面向对象的世界里设计了栩栩如生的领域模型,service层就是基于这些模型做的业务操作(它变薄了,很多动作交给了domain objects去处理)
角色转变
职责清晰下的架构
领域模型
“领域模型”指的是业务的载体对象,比如“商品”、“订单”等等这些对象具有一些有业务含义的方法,比如“商品.加入购物车”方法
观念转变
不少人认为表和类就是对应的,行row和对象object就是对应的),我个人强烈地不认同这种等同关系,这种认知直接导致了软件设计变得没有意义这些区别对领域建模的表达丰富度有显著的差别,有了封装、继承、多态,我们对领域模型的表达要生动得多,对SOLID原则的遵守也会严谨很多。
一个软件产品的内在质量好坏可能被领域模型清晰与否所决定,好的领域模型可以让产品结构清楚,修改更方便,演进成本更低。架构师很重要,他决定了软件结构,这个结构决定了软件未来的可读性,可扩展性和可演进性领域模型就是数据库设计
领域是什么
“领域”指的就是一块业务范围比如在一个电商系统中,可能会有“商品领域”、“订单领域”、“物流领域”、“仓储领域”等等DDD通过战略部分指导我们根据业务划分出业务领域,并区别出哪些是核心领域,哪些是支撑领域和通用领域
领域模型:失血、贫血、充血模型
object model
是什么
从需求分析到落地的工具。DDD将重心放在业务上,从业务出发来设计代码,业务是DDD的中心。
领域设计
领域模型设计:基于对象vs基于数据库大部分架构师都是从data modeling开始设计软件系统,少部分人通过object modeling方式开始设计软件系统这两种建模方式并不互相冲突,都很重要,但从哪个方向开始设计,对系统最终形态有很大的区别。
设计过程
一个细胞(一个表),多个细胞(多个表),长出尾巴(设计有问题),又把尾巴缩掉(更新设计),最后哇哇落地(上线)传统项目中,架构师交给开发的一般是一本厚厚的概要设计文档,里面除了密密麻麻的文字就是分好了域的数据库表设计
DDD介绍
father factory是单例,由factory依赖注入,然后产给father注入特定sonrepo
说明
我们独特的设计了Tunnel这个接口,通过这个接口我们可以实现对domain对象在不同类型数据库的存取。Repository并没有直接进行持久化工作,而是将domain对象转换成POJO交给Tunnel去做持久化工作Tunnel具体实现可以在任何包实现,这样,部署上,domain领域模型(domain objects+repositories)和持久化(Tunnels)完全的分开,domain包成为了单纯的内存对象集。
依赖注入
新建一个Father的时候需要赋值一个SonRepository,那么我们是否希望可以通过依赖注入的方式把SonRepository注入进去呢?Father在这里不可能是一个singleton对象,它可能在两个场景下被new出来:新建、查询,从Father的构造过程,SonRepository是无法注入的。这时工厂模式就显示出其意义了由于FatheFactory是系统生成的singleton对象,SonRepository自然可以注入到Factory里,newFather方法隐藏了这个注入的sonRepo,这样new一个Father对象就变干净了。
有向环的产生和问题解决
sonRepo里需要一个fatherRepo来构建一个father去赋值son.father。而fatherRepo在构建一个完整father的时候又需要sonRepo去构建一个son来赋值father.son。这形成了一个无向有环圈,这个循环调用问题是可以解决的,但为了解决这个问题,领域模型会变得有些恶心和将就。有向无环才是我们的设计目标,为了防止这个循环调用,我们是否可以在father和son这两个类里省略掉一个引用,加入持久化对象
区别数据表和面向对象
【封装】类可以设计方法,数据并不能完整地表达领域模型,数据表可以知道一个人三维,并不知道“一个人是可以跑的”。【继承、多态】类可以多态,数据上无法识别人与猪除了三维数据还有行为的区别,数据表不知道“一个人跑起来和一头猪跑起来是不一样的”
友好性
repo放到构造函数增加测试友好性通过mock/stub这个Repository,单元测试就可以顺利完成。
测试友好
失血模型和贫血模型是天然测试友好的(其实失血模型也没啥好测试的),因为他们都是纯内存对象。那么在充血模型下,对象里带上了persisitence特性,这就对数据库有了依赖,mock/stub掉这些依赖是高效单元化测试的基本要求,我们再看Father这个例子:
收藏
收藏
0 条评论
下一页