本文是小马的学习笔记备用,比较杂乱。不过小马自己已实现了一份PHP DDD demo,目前在整理完善中且后期会整理出一份比较清晰的文章,有需要的同学可以共同进步。
领域驱动设计是思想是方法论
微服务架构是架构风格
DDD与微服务和单体没关系,只不过微服务更贴合。
DDD是为解决软件复杂性而诞生,与OOP最大的区别就是划分边界的方式不一样
原视频地址
领域(电商)
子域(订单、仓储、商品)
核心子域 (某宝:卖家订单,卖家商户系统;某东:仓储,物流,供应链)、 通用子域 、支撑子域
精炼业务域 业务领域专家
所以业务核心不同,领域模型不能复制直接用。
uml建模适合小范围的建模,不适用 领域建模
贫血模型目前用的比较多。充血模型对程序员水平要求高,但比如保险行业会用。
以下来自这里。
参考这里。好文。
上图原文
假设studentService 是微服务(图书馆借书业务,选课业务,实习业务.....),不好改,很多服务都调用这个微服务。
studentService.xuanke()
xuankeService.xuanke(sId,cId)
业务不动,代码就不用动(不含具体技术实现,只有业务抽象代码,产品也能看得懂业务)
封装 业务变化 抽象出来变化(接口/api)
对于域的封装不能受到其他域的影响。每个域可以单独发展,比如老师域存MySQL,学生域存mongodb。但域之间是有互相调用和联系的。
DDD要落地,要统一团队思想,先做培训。
domain 内域实体内 可以实现得很烂,但是主控人 控制好域之间的接口api变化,也可以控制好。
中台不适合快速变化的业务,所以阿里去中台化,切薄。回到各个小服务之间的调用。中台适合业务变化不变的,比如 银行。
不同的事业部不同的领域。公司的组织架构每个部门是一个领域。
一个领域好。聚合和聚合根,比如家长,对外指暴露 爸爸,只能通过爸爸访问其他人,其他人员 爷爷,哥哥,妈妈都不能直接访问。
一个团队负责一个领域。
相关视频地址
非业务的修改会影响业务逻辑代码,改mq或mysql
每个实体的业务都是清晰的,帐户可以多个实体。按业务分 业务领域。
面向业务建模,查询另外建模。有业务造成数据改变才建模。
业务稳定的话,业务代码就可以不用动。业务块代码,业务块代码没有任何实现细节。
针对业务设计,比如转账业务。
测试可以对db单独单元测试,不必回归业务代码。
领域服务
熟悉业务
不是先建表转对象。
领域模型,领域边界
微服务不一定要ddd,只是相匹配。ddd比微服务出生早。
Post 失血模型,get,set。
PostRepository 仓库对模型进行实际 连接数据库,并实现add函数封装。由PostService 来调用仓库里面的函数。仓库 放于基础设置层,数据持久化,类似MVC 的model,可结合ORM ;
PostService 类即咱们所说的应用服务,它的目的是编排和组织领域行为。换句话说,应用服务是领域模型的直接客户端,是那些使业务发生的服务。没有其余类型的对象能够直接与模型层内部直接对话。 = 由Service 调领域模型
由接口层的controller来 调用 各个 Service服务,代表接口逻辑和视图控制。
-----------------
依赖倒置:六边形架构
命令查询分离
CQS (命令查询分离)设计模式建议将对象的方法映射到两类:方法要么改变对象的内部状态,但不返回任何内容,要么只返回元数据。这种方法称为Command。或者一个方法返回信息但不改变内部状态。这种方法称为Query。
根据 CQS,一个方法永远不应该同时存在。比如看栈的典型数据结构,push函数是一个命令,而top是一个查询。最后,pop 函数违反了 CQS 模式,因为它修改了堆栈的内部状态并同时返回信息。
因此,CQS 的核心是在单个对象上分离写入和读取。这尤其重要,例如,当代码要并行执行时:由于没有副作用,查询可以并行化而没有任何问题,但命令不能。
从技术上讲,这可以在 HTTP 中实现,因此 Command API 仅使用 POST 路由实现,而 Query API 仅使用 GET 路由实现。实际语义被转移到 URL。例如,下订单需要向/submit-order路由发送 POST 请求。这与 REST 形成对比,后者仅允许 HTTP 动词作为动词,因此技术语义已经在 API 级别丢失。
如果进一步把 CQRS 的思路拿来,把 API 后面的数据库分成两个数据库也是有道理的。一个应该针对写入进行优化,另一个针对读取进行优化——例如,通过对一个进行大量规范化,同时对另一个进行非规范化。一路走来,在写入时可以确保良好的完整性和一致性,同时在读取时实现高效率和高性能。
提出一个问题不该该改变对应的答案 - Bertrand Meyer
这种设计原则提出每一个方法应该要么是执行动做的命令,要么是返回数据给调用者的查询,而不是二者都是 - 维基百科
CQRS谋求一种更为激进的关注点分离,即将模型分为两部分:
写模型: 同时也称为命令模型,它执行写入和负责真实的领域行为。
读模型: 它在应用内负责读取,并将这部分视为领域模型以外的内容。
每次只要触发一个命令给写模型,它就会执行渴求数据的存储写入。除此以外,它还会触发读模型的更新,保证在读模型上显示最后一次的更改。
领域服务之间用领域事件(CQRS)通信,子域之间也是。则领域可以自定义DB。
这种严格的分离致使了另外一个问题,最终一致性。读模型的一致性如今受写模型执行的命令的影响。换句话说,读模型是最终一致性的。也就是说,每次当写模型执行一个命令,它就会负责挂起一个进程,依照写模型上最后一次更改,来更新读模型。因此这里存在一个时间窗口,UI可能会向用户展现旧的信息。在 web 场景中,这种状况常常发生,由于咱们受当前技术因素限制。
考虑一个 web 应用的缓存系统,每次用新信息数更新数据库时,缓存层的数据有多是陈旧的,因此每当模型有更新时,也应该同时更新缓存系统。因此 缓存系统是最终一致性的。
这些处理过程,在 CQRS 术语中被称为写模型投影,或者就称做投影。即投影一个写模型到读模型上。这个过程能够是同步或者异步,取决于你的须要,同时它能够用另外一种颇有用的战术设计模式 - 领域事件(本书后面的章节会讲到)来实现。写模型投影的基本过程就是收集全部发布的领域事件,而后用事件中的信息来更新读模型。
----------------------------------
这种方法即基于一个设计原则,命令查询分离(CQS)。这个原则由 Bertrand Meyer 提出,而后,相应地,成长为一个全新的架构模式,叫做命令查询职责分离(CQRS),CQRS 由 Greg Young 定义。
原文
1. 概述
CQRS(Command Query Responsibility Segration:命令查询职责隔离)
其中:
Command包括增、删、改; Query只包含查
它是一种读写分离思想的架构
常用解决方案就是对数据库进行读写分离。
让主数据库处理事务性的增、删、改操作,让从数据库处理查询操作,然后主从数据库之间进行同步。
这只是从DB角度处理了读写分离,从业务或者系统层面上来说,读和写的逻辑仍然是存放在一起的
CQRS架构:
2. 实现方式
CQRS可以有两种实现方式。
2.1 CQ两端数据库共享,只是在上层代码上分离。
这样做的好处是可以让我们的代码读写分离,更容易维护,而且不存在CQ两端的数据一致性问题,
因为是共享一个数据库的。这种架构是非常实用的(也就是我上面画的那种)
2.2 CQ两端不仅代码分离,数据库也分离,然后Q端数据由C端同步过来。
同步方式有两种:同步或异步,如果需要CQ两端的强一致性,则需要用同步;如果能接受CQ两端数据的最终一致性,则可以使用异步。
C端可以采用EventSourcing(简称ES)模式,所有C端的最新数据全部用DomainEvent表达即可
而要查询显示用的数据,则从Q端的ReadDB(关系型数据库)查询即可。
《聚合根等和数据库表设计实例》
相关资料:
文章1
文章2
文章3 (好文)
文章4
git样例 这个PHP样例看起来似乎不是很理想,接口层直接和领域层的仓库交互而不是与应用层交互
文章5
易于理解的好文
《领域驱动设计之PHP实现》