DDD领域驱动设计(笔记)

本文是小马的学习笔记备用,比较杂乱。不过小马自己已实现了一份PHP DDD  demo,目前在整理完善中且后期会整理出一份比较清晰的文章,有需要的同学可以共同进步。

领域驱动设计是思想是方法论

微服务架构是架构风格

DDD与微服务和单体没关系,只不过微服务更贴合。

DDD是解决软件复杂性而诞生,与OOP最大的区别就是划分边界的方式不一样

原视频地址

领域(电商)

子域(订单、仓储、商品)

核心子域 (某宝:卖家订单,卖家商户系统;某东:仓储,物流,供应链)、  通用子域  、支撑子域

精炼业务域      业务领域专家

所以业务核心不同,领域模型不能复制直接用。

uml建模适合小范围的建模,不适用 领域建模



贫血模型
充血模型


充血模型

贫血模型目前用的比较多。充血模型对程序员水平要求高,但比如保险行业会用。

DB驱动  三层架构

以下来自这里。

参考这里。好文。

所有模式的总揽图
这里的依赖关系表明着代码实现时类之间的互相use依赖

上图原文

领域驱动 四层架构


DDD四层架构
四层架构
MVC

假设studentService 是微服务(图书馆借书业务,选课业务,实习业务.....),不好改,很多服务都调用这个微服务。

studentService.xuanke()


DDD
DDD

xuankeService.xuanke(sId,cId)

业务不动,代码就不用动(不含具体技术实现,只有业务抽象代码,产品也能看得懂业务)

封装 业务变化  抽象出来变化(接口/api)


对于域的封装不能受到其他域的影响。每个域可以单独发展,比如老师域存MySQL,学生域存mongodb。但域之间是有互相调用和联系的。

用领域服务(service =接口)处理学生和课程实体(domain)之间的联系关系
仓库(只管存取,具体实现透明,怎么存都行),工厂
提前做防腐层,p7(技术)->p8(业务,管理)

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实现》

你可能感兴趣的:(DDD领域驱动设计(笔记))