《实现领域驱动设计》读书笔记

大家好,我是烤鸭:
    《实现领域驱动设计》,读书笔记,贴个封面,要不不知道是哪本。
在这里插入图片描述

了解概念

刚开始接触DDD,肯定懵逼,很多名词,一点点看下。

领域:带有业务属性的范围,比如搞直播业务,那直播就是一个领域。领域里又分核心域和子域。

可以看个电商系统的领域图。

在这里插入图片描述

限界上下文:用于承接不同子域。

上下文映射图:限界上下文关系图。

六边形架构:又称端口适配器,水平的分层架构,牺牲一部分性能,更好的防止逻辑层外漏。

CQRS:Command Query Responsibility Segregation,读写分离。这个读写分离跟我们想的还有点不一样,

领域事件内的读库和写库的数据同步是基于领域事件的,比如写mysql,读的时候读es,这时候就需要领域事件触发mysql到es的同步。

章节回顾

当我们开始一个业务,需要考虑领域边界,不只是业务层面的,还有技术层面的。核心域的创建,子域的分类,实体和值对象的建立等等。

限界上下文的建模需要考虑不同子域,比如不同流程,需要的对象属性不同,上下文建模的对象就可能意义不同。(比如上面的电商系统,订单子域和发票子域、库存子域的限界上下文对象肯定是不同的)

几种架构设计:

  • 分层架构:用户接口层—>应用层—>领域层—>基础设施层。依赖倒置原则,高、低层模块都依赖于抽象。

  • 六边形架构:外界通过适配器和内部交互。

    在这里插入图片描述

  • SOA:Service-Oriented Architecture,面向服务架构。这个服务可能是根据业务或者技术来拆分的,并不是服务越多越好,需要考虑限界上下文。技术架构不能影响业务领域模型。

在这里插入图片描述

  • REST: URI决定访问的资源,是无状态通信。利于解耦,需要单独涉及上下文。

  • CQRS:读写分离。查询模型和DTO的建立。写模型执行业务行为发布领域事件,事件订阅器更新查询模型。
    (比如查询在es或者redis,在收到写事件时,同时更新es或者redis)

  • 事件驱动:类似linux中的管道和过滤器,采用分治的方式解决大型问题。定义实体和领域事件(业务维度的操作),跟踪器监听长时处理过程(分布式的并行处理模式),考虑幂等,监听超时可以主动定时查询。事件源(记录领域模型的操作)和快照(对一系列事件后生成的,类似redis的AOF恢复时对命令整合)

    在这里插入图片描述

  • 数据网织,缓存的建立和层级维护,通过客户端监听保证缓存中的查询模型更新,长时处理过程不一定是事件驱动。

实体

实体的建立,实体具有唯一标识和可变性,值对象没有。普通的数据模型(CURD模型)是不会创建出好的业务模型,数据模型需要转换为实体模型。

值对象可以用于存放实体的唯一标识。唯一标识可以由用户传递、应用生成、持久化生成或者上下文传递。

委派标识,可以做唯一索引。层超类型(通过继承的方式,隐藏主键)在聚合层可以做乐观锁。

在这里插入图片描述

值对象

值对象的常见例子,比如 数字、文本、日期,或者复杂的对象。

在值对象中引用实体,应该考虑不变性、表达性和方便性。否则,一旦实体改变属性,会破坏值对象的不变性。

使用值对象可以缩小集成化,简化职责假设。协作上下文中的防腐层(身份校验和参数转化)、开放主机服务(三方调用)使用相同的值对象会简单很多。

测试用例也可以使用值对象,使用断言。

值对象的备份和相等性验证。(通过copy的方式,生成新的值对象,并且验证两个相等性)。

ORM和值对象,数据库直接保存值对象,值对象的每一个属性对应一列。多个值对象ORM的情况不考虑。

领域服务

领域服务主要是处理业务逻辑的,将领域对象转换成值对象。只有在必要时建模领域服务。(遵循单一职责原则)

创建独立接口,如果使用依赖倒置或者六边形架构,有些实现类可能在领域模型之外。

领域事件

无论是外部还是内部系统,通过领域事件维护事件的一致性,可以消除两段式事务,还可以支持聚合。(最常见的就是mq)

在这里插入图片描述

通过领域服务创建事件,添加到资源库,通过消息设施进行发布,需要有唯一标识,可以做幂等。

基于 发布-订阅,可以解耦,允许短暂的数据不一致,最终一致。同时作为消费方,在单个事务中只修改单个聚合实例。

事件可以用队列存储,利于检查历史记录,聚合操作。(需要关注事件顺序)

以rest方式发布事件(多个消费方拉取同一个URI时),需要考虑顺序和对发布事件的跟踪(存档日志)。

消息的重复发送需要做幂等处理。

模块

maven项目中的module,设计的要和领域概念保持一致,松耦合,杜绝循环依赖。

模块名的命名规范,比如 com.saasovation.agilepm.domain.service/model

优先考虑组织结构,而不是松耦合性。先考虑模块,后考虑限界上下文。

聚合

实体和值对象在边界内组成聚合。CQS查询将大聚合分为几个小聚合。

在这里插入图片描述

聚合边界内一套不变的业务规则维持一致性,类似持久化机制中的事务。

小聚合在性能和可伸缩性上有好处,还能减少事务的提交冲突。

通过唯一标识引用其他聚合。不能在同一事务中修改多个聚合,需要考虑事务失败的情况。

如果聚合特别多,在web层处理复杂,需要考虑聚合中的标识引用或者实体引用。

发布-订阅或者观察者模式,事务非原子性,通过聚合达到一致性。(分布式场景需要考虑并发)

创建具有唯一标识的根实体,优先使用值对象。

使用"迪米特法则"(强调"最小知识"原则)和"告诉而非询问法则(一个对象不应该被告知如何执行操作)。

数据库层面加版本号的乐观锁方式限制并发。

避免将资源库和领域服务注入到聚合层。

工厂

工厂相关的设计模式有抽象工厂、工厂方法、建造者。

领域模型中的工厂只负责创建对象,没有其他权责。

聚合根中的工厂方法用于创建聚合对象,减轻客户端创建聚合实例时的负担,确保所有实例都在正确的状态。

领域服务中的工厂主要集中在防腐层、发布语言和开放主机服务的集成上。限界上下文的交互中,将领域内的对象转换为上下文中的对象。这里的领域服务其实扮演的是工厂的角色。

资源库

资源库是对聚合实例的一种持久化,代替DTO。

以集合资源库为例,需要考虑顺序和幂等。资源库和数据库层面数据一致性,采用双写保险。

资源库的正确用法,要么只读,要么读取是为了修改。

使用Nosql对资源库进行持久化,需要考虑数据结构。

有时候需要返回大聚合对象中的子聚合,可以考虑直接从资源库中获取,返回值对象。如果用例优化查询,还是需要多个查询方法,可能是边界划分错误,考虑使用CQRS。

领域层不考虑事务,事务加在业务层,不要过多的在领域模型上使用事务。测试环境测试没问题,生产环境有可能会有并发问题。

使用单个资源库保存和获取层级中不同的聚合类型,客户端无需知道他们使用的实际类型,体现了里氏替换原则。

资源库和DAO不同。DAO是从数据库表角度来看待问题,提供CURD操作。可以将资源库当做DAO来看,不过设计资源库时,应该面向集合,而不是面向数据。

集成限界上下文

多种方式,一种是:soap、http(xml)、rpc,另一种是消息队列(发布—订阅),第三种是 restful。

分布式系统本身是有风险的,两个系统间的数据交互还需要考虑版本更新,数据结构改变等等。比如消息消费方的端口适配器应该将自己内部的领域模型和外部的领域模型隔离开,同时传入的数据必须遵循本地限界上下文的类型定义。

无论那种方式(http、rpc) 部署接口/类,还是定义媒体类型契约(mq),看业务场景和项目阶段。但从产品角度,使用低耦合的媒体类型更好。

开放主机服务:当一个限界上下文以URI的方式提供了大量REST资源时,可称其为开放主机服务。

比如下面这个Rest的URI,判断某个用户角色,返回200就是有该角色,其他的就没有。

/tenants/{tenantId}/users/{username}/inRole/{role}

上面这个内部实现是六边形架构的适配器,再调用应用服务和领域服务。

防腐层是上下文协作交互的方式,数据转换的作用,防腐层通常有一个特定的适配器。

在这里插入图片描述

利用消息队列传递上下文需要考虑顺序和幂等,最小化或者消除不同限界上下文之间的信息复制。

长时处理过程指的是完全走完一个流程,比如发布两个对象,经过了哪些过滤器。其中有跟踪器,目的是监听哪些过期,哪些可以重试。

跟踪器放到本地上下文,而不是协作上下文。

针对MQ超时重试,使用否定应答和消息重发结合的方式。

消息集群宕机恢复后,服务监听器可以自动重连。

应用程序

应用程序通过用户界面向外界展示领域模型的概念,并且允许用户在模型上执行各种操作。

DTO(Data Transfer Object) 数据传输对象,将包含需要的所有属性值,从资源库获取再映射。缺点是DTO 可能暴露领域内部数据结构,应该考虑解耦。

调停者模式,即双分派和回调。客户端实现调停者接口,把对象引用传给聚合,聚合通过调停者发布自身状态。

DPO(Domain Payload Object) 领域负载对象,包含了整个聚合实例的引用,而不是单独属性。也使用调停者解耦,避免延迟加载的问题,强制手动访问所有延迟加载的属性。

VO(View Object) 客户端展示层,通过适配器将DTO转换。

优化资源库查询,可以直接返回值对象。

应用服务不等于领域服务,所有的业务领域逻辑放到领域模型,不管是聚合、值对象或者领域服务,应用服务是很薄的一层,只使用他们协调对模型的任务操作。

一种方式是多应用层,每个用户界面组件都提供所有的应用层,此时用户界面组件将向领域模型靠近。结果有点像贫血领域对象。

在这里插入图片描述

领域模型需要考虑模块包的命名,比如:

com.consumerhive.productreviews.domain.model.product

基础设施的职责是为应用程序的其他部分提供技术支持。(比如自己初始化bean,注入spring容器)

在这里插入图片描述

总结

尽管尽量减少章节回顾的内容,还是有很多不好理解的点。

DDD的概念推出很久了,不过感觉用的人不多,会用的人就更少了。

大家一直强调微服务,模块、实体、nosql、mq,这些跟DDD都有交集,不过建模真的考虑过DDD么,是贫血领域对象还是应用服务当做领域服务。

领域模型的建立必须是懂得业务的人参与,纯技术是没办法建模的。

下一篇想结合实战,写一下DDD在实际业务中的使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烤鸭的世界我们不懂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值