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(二)聚合、聚合根、领域服务、应用服务、仓储”和“工作单元”、领域事件、集成事件的更多相关文章

  1. ABP领域层知识回顾之---工作单元

    1. 前言   在上一篇博文中(http://www.cnblogs.com/xiyin/p/6842958.html) 我们讲到了ABP领域层的仓储.这边博文我们来讲 工作单元.个人觉得比较重要.文 ...

  2. 初识ABP vNext(11):聚合根、仓储、领域服务、应用服务、Blob存储

    Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章. 目录 前言 开始 聚合根 仓储 领域服务 BLOB存储 应用服务 单元测试 模块引用 最后 前言 在前两节中介绍了ABP模块开发的基本步 ...

  3. DDD理论学习系列(8)-- 应用服务&领域服务

    DDD理论学习系列--案例及目录 1. 引言 单从字面理解,不管是领域服务还是应用服务,都是服务.而什么是服务?从SOA到微服务,它们所描述的服务都是一个宽泛的概念,我们可以理解为服务是行为的抽象.从 ...

  4. 如何运用DDD - 领域服务

    目录 如何运用DDD - 领域服务 概述 什么是领域服务 从实际场景下手 更贴近现实 领域服务VS应用服务 扩展上面的需求 最常见的认证授权是领域服务吗 使用领域服务 不要过多的使用领域服务 不要将过 ...

  5. 基于ABP落地领域驱动设计-04.领域服务和应用服务的最佳实践和原则

    目录 系列文章 领域服务 应用服务 学习帮助 系列文章 基于ABP落地领域驱动设计-00.目录和前言 基于ABP落地领域驱动设计-01.全景图 基于ABP落地领域驱动设计-02.聚合和聚合根的最佳实践 ...

  6. 应用服务&领域服务

    应用服务&领域服务 DDD理论学习系列——案例及目录 1. 引言 单从字面理解,不管是领域服务还是应用服务,都是服务.而什么是服务?从SOA到微服务,它们所描述的服务都是一个宽泛的概念,我们可 ...

  7. DDD领域驱动设计仓储Repository

    DDD领域驱动设计初探(二):仓储Repository(上) 前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repositor ...

  8. ABP入门系列(18)—— 使用领域服务

    ABP入门系列目录--学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 1.引言 自上次更新有一个多月了,发现越往下写,越不知如何去写.特别是当遇到DDD中一些概念术语的 ...

  9. 关于ABP——领域服务的思考

    我在刚接触ABP的时候一直有一个疑问--有了应用服务,为什么还需要领域服务呢? 领域服务和应用服务对比 领域服务 应用服务 返回值 Entity DTO 被表现层调用 不可以(非强制) 可以 在ABP ...

  10. ABP(现代ASP.NET样板开发框架)系列之12、ABP领域层——工作单元(Unit Of work)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ABP是“ASP.NET Boilerplate Pr ...

随机推荐

  1. Docker上安装MSSQL(SQL Server)

    ​ Mac OS X ,想安装微软的mssql-server数据库有三种方式: 第一种是在本机上安装MSSQL for Linux 版本. 第二种是安装Windows虚拟机,然后在虚拟机里面使用ISO ...

  2. 苹果App 上架 app store 提示 “构建版本错误”使用Application Loader发布App

    步骤1 打开Application Loader(有2种方法) 或 步骤2 使用开发者帐号登录 步骤3 选择需要上传发布的ipa包 选择成功后,会显示ipa包的相关信息 步骤4 上传验证 上传成功 转 ...

  3. Typora的一些基础用法

    Typora的简单实用技巧 标题 标题分为h1~h6六个等级,数字越小标题越大. 定义标题有一下几种方式. 方式一:这个标题手敲就在文本前边敲#号,#和文本中间需又空格隔开. 方式二:快捷键,Ctrl ...

  4. 谷歌云|机密 GKE 节点可在计算优化的 C2D 虚拟机上使用

    机密 GKE 节点可用于计算优化的 C2D 虚拟机. 许多公司已采用 Google Kubernetes Engine (GKE) 作为其应用程序基础架构中的关键组件.在某些情况下,使用容器和 Kub ...

  5. 对比学习InfoNCE loss之“搬砖学习”

    以下链接讲解清晰,供参考 对比学习损失(InfoNCE loss)与交叉熵损失的联系,以及温度系数的作用 - 知乎 (zhihu.com)

  6. 【离线数仓】数据仓库DW图解

    整体框架 技术选型 数据流程图

  7. Node.js 源码解读 EventLoop

    之前断断续续开发过一些 Node.js 的项目,但只仅限于使用它实现一些功能,没有过多对底层深入的研究.现在因为公司大前端组内的服务端渲染直出.BFF(Backend For Frontend) 等需 ...

  8. MyBatis_09(逆向工程)

    MyBatis的逆向工程 正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表.Hibernate是支持正向工程的 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源: J ...

  9. input放入焦点,选中全部文本

    async mounted(){ let inputList = document.querySelectorAll('input'); for (let index = 0; index < ...

  10. Beaver解析代码反向生成语法文件

    背景 Beaver是一款LALR的语法生成工具,现在有一个反编译项目的需求,需要将Beaver语法文件编译后的代码反向生成语法文件的需求,不去评论需求多么傻逼,直接干 设计 flowchart 语法文 ...