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

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

了解概念

刚开始接触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在实际业务中的使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/412483.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring junit单元测试

项目是有很多个功能块组成的,我们开发的时候,当我们开发出来一个功能,想要测试这个功能是否正确,不可能等到前端和后端全部写好了再进行测试,这样太浪费时间,有没有什么方法能直接测试后台的功能写的是否正…

windows docker redis

大家好,我是烤鸭: docker真的太方便了,尤其是对windows系统,友好的不得了。以前还只能是正版的专业版才能用,现在已经没有限制了,虽然加了收费,个人用免费就够了。redis 新版也不支持windows系统…

[css] CSS3新增伪类有哪些并简要描述

[css] CSS3新增伪类有哪些并简要描述 个人简介 我是歌谣,欢迎和大家一起交流前后端知识。放弃很容易, 但坚持一定很酷。欢迎大家一起讨论 主目录 与歌谣一起通关前端面试题

模拟微信自动化发送(微信公众号文章自动点击)

大家好,我是烤鸭: 分享个微信自动化发送的新方式,仅技术分享。 本来是公众号文章抓取相关的,审核一直不过,将就看吧。 需要的工具 Java(jdk1.8) Fiddler Python(3.8)…

Entity FrameWork 操作使用详情

Entity FrameWork 是以ADO.net为基础发展的ORM解决方案。 一、安装Entity FrameWork框架 二、添加ADO.Net实体数据模型 三、EF插入数据 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace EFDem…

网络通信中TCP出现的黏包以及解决方法 socket 模拟黏包

粘包问题概述 1.1 描述背景 采用TCP协议进行网络数据传送的软件设计中,普遍存在粘包问题。这主要是由于现代操作系统的网络传输机制所产生的。我们知道,网络通信采用的套接字(socket)技术,其实现实际是由系统内核提供一片连续缓存(流缓冲)来…

windows docker redis 集群部署

大家好,我是烤鸭: 上次分享了windows docker redis,这么快就不够用了,单机的不行,整个集群的,看了网上的教程都好麻烦,简单点。 单机的:https://blog.csdn.net/Angry_Mills/article…

Codeforces Round #530 Div. 1 自闭记

A&#xff1a;显然应该让未确定的大小尽量大。不知道写了啥就wa了一发。 #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; #define ll long long #…

自研redis sdk支持自动dns切换(附源码)

大家好&#xff0c;我是烤鸭&#xff1a; 标题起的有点大了&#xff0c;说是自研&#xff0c;其实就是个封装&#xff0c;不过倒是解决了dns切换的问题&#xff08;虽然不太优雅&#xff09;。 背景 之前做活动的时候&#xff0c;用域名链接的redis&#xff0c;当时做了主备集…

使用Canal实现redis和mysql的同步

使用Canal实现redis和mysql的同步 ### canal 工作思路 Canal 会将自己伪装成 MySQL 从节点&#xff08;Slave&#xff09;&#xff0c;并从主节点&#xff08;Master&#xff09;获取 Binlog&#xff0c;解析和贮存后供下游消费端使用。Canal 包含两个组成部分&#xff1a;服务…

上线到凌晨4点半 pagehelper的bug?

大家好&#xff0c;我是烤鸭&#xff1a; 上上周末上线到凌晨4点半&#xff0c;哭了&#xff0c;没想到问题竟然如此简单。最近又懒惰了&#xff0c;写了开头就一直放着了&#xff0c;今天终于补上。 ​ 问题日志 Error querying database. Cause: com.github.pagehelper.P…

skywalking 引起 spring-cloud-gateway 的内存溢出 skywalking的bug

大家好&#xff0c;我是烤鸭&#xff1a; 又是个线上问题记录&#xff0c;这次坑惨了&#xff0c;开源软件也不是万能的&#xff0c;还是要做好压测和灰度。 问题 上游反馈大量超时&#xff0c;不止某一个服务&#xff0c;查看服务没有问题&#xff0c;猜测是网络或者环境问题…

长连接检测 监控的一点思考 java实现

大家好&#xff0c;我是烤鸭&#xff1a; 怎么监控长链接服务器的稳定&#xff0c;除了探活服务之外&#xff0c;怎么保证长链接的收发正常&#xff0c;这篇文章考虑下这个。 问题来源 运营反馈部分直播间无法收到弹幕、点赞消息&#xff0c;第一时间进行复现&#xff0c;发现…

rabbitmq 启动失败 dump日志分析

大家好&#xff0c;我是烤鸭&#xff1a; rabiitmq 突然宕机&#xff0c;并且无法启动。同事反馈测试环境 rabbitmq 有一个节点突然掉了&#xff0c;并且无法启动。 现象 集群有一个节点宕机。 去对应的机器上执行启动命令 ./rabbitmq-server -detached发现进程不在&#x…

《深入理解Java虚拟机》-读书笔记(第一、第二部分)

大家好&#xff0c;我是烤鸭&#xff1a; 《深入理解Java虚拟机》-读书笔记&#xff08;第一、第二部分&#xff09;。 第一部分&#xff1a;走进Java 第1章 走进Java 1.1 概述 摆脱了硬件平台的束缚&#xff0c;实现了“一次编写&#xff0c;到处运行”的理想&#xff1b;…