DDD(二)聚合、聚合根、领域服务、应用服务、仓储”和“工作单元”、领域事件、集成事件
DDD(二)聚合、聚合根、领域服务、应用服务、仓储”和“工作单元”、领域事件、集成事件
如果觉得样式不好:跳转即可 http://www.lifengying.site/(md文件复制过来有些样式会不一样)
DDD之聚合、聚合根
聚合(Aggregate)
1、目的:高内聚,低耦合。有关系的实体紧密协作,而关系很弱的实体被隔离。
2、把关系紧密的实体放到一个聚合中,每个聚合中有一个实体作为聚合根(Aggregate Root),所有对于聚合内对象的访问都通过聚合根来进行,外部对象只能持有对聚合根的引用。
3、聚合根不仅仅是实体,还是所在聚合的管理者。
高内聚,低耦合

聚合的意义
1、为什么聚合可以实现“高内聚,低耦合”。
2、聚合体现的是现实世界中整体和部分的关系,比如订单与订单明细。整体封装了对部分的操作,部分与整体有相同的生命周期。部分不会单独与外部系统单独交互,与外部系统的交互都由整体来负责。
好处就是聚合内部的实体可以紧密的工作,聚合之间可以低耦合的工作
聚合的划分很难
1、系统中很多实体都存在着不同程度的关系,这些关系到底是设计为聚合之间的关系还是聚合之内的关系是很难的。
2、聚合的判断标准:实体是否是整体和部分的关系,是否存在着相同的生命周期。
3、订单与订单明细?用户与订单?
聚合的划分没有标准答案
1、不同的业务流程也就决定了不同的划分方式。
2、新闻和新闻的评论?
例子:
传统的新闻网站可以把新闻和对应的新闻评论设计成一个聚合,但是现在大多数新闻网站都有热评榜,这就导致新闻评论是可以单独与外部系统交互的,这就可以设计成2个聚合了。所以得根据自己的系统来合理的划分。
聚合的划分的原则
1、尽量把聚合设计的小一点,一个聚合只包含一个聚合根实体和密不可分的实体,实体中只包含最小数量的属性。
2、小聚合有助于进行微服务的拆分。
聚合宁愿设计的小一点也不要设计的太大
DDD之领域服务、应用服务
1、聚合中的实体中没有业务逻辑代码,只有对象的创建、对象的初始化、状态管理等个体相关的代码。
2、对于聚合内的业务逻辑,我们编写领域服务(Domain Service),而对于跨聚合协作以及聚合与外部系统协作的逻辑,我们编写应用服务(Application Service)。
3、应用服务协调多个领域服务、外部系统来完成一个用例。
在DDD中,一个典型的用例的处理流程如下
第一步,准备业务操作所需要的数据。
第二步,执行由一个或者多个领域模型做出的业务操作,这些操作会修改实体的状态,或者生成一些操作结果。
第三步,把对实体的改变或者操作结果应用于外部系统。
用例方便理解的就是如下:
//第一步,准备业务操作所需要的数据。
MyUser user = new MyUser();
//第二步 我这里是新增用户 如果是修改上面应该准备好数据 这里只需要修改就行
//这里做业务处理
user.UserName = req.UserName;
user.PasswordHash = req.Password;
user.CreationTime = DateTime.UtcNow;
ctx.add(user);
//第三步,把对实体的改变或者操作结果应用于外部系统。
ctx.SaveChanges();
职责的划分
1、领域模型与外部系统不会发生直接交互,即领域服务不会涉及数据库操作。
2、业务逻辑放入领域服务,而与外部系统的交互由应用服务来负责。
3、领域服务不是必须的,在一些简单的业务处理中(比如增删改查)是没有领域知识(也就是业务逻辑)的,这种情况下应用服务可以完成所有操作,不需要引入领域服务。这样可以避免过度设计。
“仓储”(Repository)和“工作单元”(Unit Of Work)
1、仓储负责按照要求从数据库中读取数据以及把领域服务修改的数据保存回数据库。
2、聚合内的数据操作是关系非常紧密的,我们要保证事务的强一致性,而聚合间的协作是关系不紧密的,因此我们只要保证事务的最终一致性即可。
3、聚合内的若干相关联的操作组成一个“工作单元”,这些工作单元要么全部成功,要么全部失败。
继续按照上面的例子可以简单的理解为EF Core是仓储、SaveChanges是工作单元(要么这一波全部成功,要么全部失败)。
总结:
因为领域服务不依赖外部系统、不保存状态,所以领域服务比应用服务更容易进行单元测试,这对于提高系统的质量是非常有帮助的。
DDD之领域事件、集成事件(十分重要)
事务脚本处理“事件”
1、“当发生某事件的时候,执行某个动作”。
2、当有人回复了用户的提问的时候,系统就向提问者的邮箱发送通知邮件。事务脚本的实现:
伪代码如下
void 保存答案(long id,string answer)
{
保存到数据库(id,answer);
string email = 获取提问者邮箱(id);
发送邮件(email,"你的问题被回答了");
}
1、代码会随着需求的增加而持续膨胀。比如增加功能“如果用户回复的答案中有涉嫌违法的内容,则先把答案隐藏,并且通知审核人员进行审核”。怎么做?
2、代码可扩展性低。比如把“发送邮件”改成“发送短信”,怎么办? “开闭原则”:对扩展开放,对修改关闭。
3、容错性差。外部系统并不总是稳定的。
//1、
void 保存答案(long id,string answer)
{
保存到数据库(id,answer);
if(答案违规)//一般是调用第三方鉴黄服务
{
hide();
}
else
{
string email = 获取提问者邮箱(id);
发送邮件(email,"你的问题被回答了");//调用第三方邮箱服务 可能会挂掉
}
}
//2、
void 保存答案(long id,string answer)
{
保存到数据库(id,answer);
if(答案违规)//一般是调用第三方鉴黄服务 可能会挂掉
{
hide();
}
else
{
string phoneNo = 获取提问者手机(id);
发送短信(phoneNo,"你的问题被回答了");//调用第三方邮箱服务 可能会挂掉
}
}
以上代码并不满足“开闭原则”:对扩展开放,对修改关闭。
采用事件机制的伪代码
void 保存答案(long id,string answer)
{
long aId = 保存到数据库(id,answer);
发布事件("答案已保存",aId,answer);
}
[绑定事件("答案已保存")]
void 审核答案(long aId,string answer)
{
if(检查是否疑似违规(answer))
{
隐藏答案(aId);
发布事件("内容待审核",aId);
}
}
[绑定事件("答案已保存")]
void 发邮件给提问者(long aId,string answer)
{
long qId = 获取问题Id(aId);
string email = 获取提问者邮箱(qId);
发送邮件(email,"你的问题被回答了");
}
优点:关注点分离;容易扩展;容错性好;
上诉代码如果需要保存数据,刷新缓存。则加以下代码即可,并不需要修改原有代码
[绑定事件("答案已保存")]
void 审核答案(long aId,string answer)
{
刷新缓存(aId);
}
两种事件
1、DDD中的事件分为两种类型:领域事件(Domain Events)和集成事件(Integration Events)。
2、领域事件:在同一个微服务内的聚合之间的事件传递。使用进程内的通信机制完成。
3、集成事件:跨微服务的事件传递。使用事件总线(EventBus)实现。
总结
领域事件由于是在同一个进程内进行的,我们通过进程内的通信机制就可以完成:
集成事件由于需要跨微服务进行通信,我们就要引入事件总线(eventbus)来实现事件的传递。我们一般使用消息队列服务器中的“发布/订阅”模式来实现事件总线。
本文内容大部分都为杨中科老师《ASP.NET Core技术内幕与项目实战》一书中内容,此文只是做学习记录,如有侵权,联系立马删除。
DDD(二)聚合、聚合根、领域服务、应用服务、仓储”和“工作单元”、领域事件、集成事件的更多相关文章
- ABP领域层知识回顾之---工作单元
1. 前言 在上一篇博文中(http://www.cnblogs.com/xiyin/p/6842958.html) 我们讲到了ABP领域层的仓储.这边博文我们来讲 工作单元.个人觉得比较重要.文 ...
- 初识ABP vNext(11):聚合根、仓储、领域服务、应用服务、Blob存储
Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 聚合根 仓储 领域服务 BLOB存储 应用服务 单元测试 模块引用 最后 前言 在前两节中介绍了ABP模块开发的基本步 ...
- DDD理论学习系列(8)-- 应用服务&领域服务
DDD理论学习系列--案例及目录 1. 引言 单从字面理解,不管是领域服务还是应用服务,都是服务.而什么是服务?从SOA到微服务,它们所描述的服务都是一个宽泛的概念,我们可以理解为服务是行为的抽象.从 ...
- 如何运用DDD - 领域服务
目录 如何运用DDD - 领域服务 概述 什么是领域服务 从实际场景下手 更贴近现实 领域服务VS应用服务 扩展上面的需求 最常见的认证授权是领域服务吗 使用领域服务 不要过多的使用领域服务 不要将过 ...
- 基于ABP落地领域驱动设计-04.领域服务和应用服务的最佳实践和原则
目录 系列文章 领域服务 应用服务 学习帮助 系列文章 基于ABP落地领域驱动设计-00.目录和前言 基于ABP落地领域驱动设计-01.全景图 基于ABP落地领域驱动设计-02.聚合和聚合根的最佳实践 ...
- 应用服务&领域服务
应用服务&领域服务 DDD理论学习系列——案例及目录 1. 引言 单从字面理解,不管是领域服务还是应用服务,都是服务.而什么是服务?从SOA到微服务,它们所描述的服务都是一个宽泛的概念,我们可 ...
- DDD领域驱动设计仓储Repository
DDD领域驱动设计初探(二):仓储Repository(上) 前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repositor ...
- ABP入门系列(18)—— 使用领域服务
ABP入门系列目录--学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 1.引言 自上次更新有一个多月了,发现越往下写,越不知如何去写.特别是当遇到DDD中一些概念术语的 ...
- 关于ABP——领域服务的思考
我在刚接触ABP的时候一直有一个疑问--有了应用服务,为什么还需要领域服务呢? 领域服务和应用服务对比 领域服务 应用服务 返回值 Entity DTO 被表现层调用 不可以(非强制) 可以 在ABP ...
- ABP(现代ASP.NET样板开发框架)系列之12、ABP领域层——工作单元(Unit Of work)
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ABP是“ASP.NET Boilerplate Pr ...
随机推荐
- Warning: PHP Startup: Unable to load dynamic library
使用 phpstudy时,一直提示找不到指定的模块,但是在我对应的文件里面是有该模块的 且 php.ini中 对应的语句注释也已打开 我遇见该问题解决方法是:php版本与这个拓展的版本不对应 ,把版 ...
- windows下gitlab-ci.yml配置进入某一目录找不到,无权限
打开任务管理器查看gitlab-runner所使用的的用户 更改gitlab-runner服务权限, 找到gitlab-runner服务,右键-属性-登陆,选择[此账户]点击[浏览] 选择具有权限的账 ...
- vue-表格拖拽
1. el-table结合sortable 参考:https://blog.csdn.net/weixin_42460570/article/details/125765599?ops_request ...
- Spring Boot--自定义异常类
1.业务异常 /** * @Description: 业务异常 * @Author: Yaoson * @Create: 2022/4/21 0:05 **/ public class Busines ...
- STP理论基础
目的 防止二层环路及防止环路造成的广播风暴以及引起的MAC表震荡 方法 首先,所有配置了STP的交换机互相向相邻交换器(配置了STP的)发送BPDU(协议数据单元),选举根桥(根交换机),根交换机上所 ...
- sentinel监控数据持久化&本地测试
官方文档 https://sentinelguard.io/zh-cn/ wiki: 在生产环境中使用-Sentinel 推荐方案:持久化到 时序数据库InfluxDB : 结合Grafana 可 ...
- vue3 自定义组件中使用 v-model
1.直接绑定 v-model,但是 Props 要固定为 modelValue 组件D: 注意这里的 Props 和 Emits,必须使用 Vue 提供的 defineProps() 和 define ...
- 性能测试-top-实时显示系统中各个进程的资源占用状况, 也可以查看线程
1.top命令介绍 top命令是Linux系统中常用的性能分析工具,可以实时地查看系统的运行情况,比如内存.CPU.负载以及各个进程的资源占用情况 top界面主要分为两个部分,前5行展示的是系统的整体 ...
- john破解linux用户密码
John是kali-linux自带的密码破解工具,支持密码本破解.Linux的系统账户在/etc/passwd中,密码存于/etc/shadow中. 以下操作为例: (1)创建一个账户,
- 转载-分享一个Ubuntu20.04安装以及中文教程
https://blog.csdn.net/weixin_45912291/article/details/108901106 https://www.cnblogs.com/libotaoer/p/ ...