大话领域驱动设计——领域层

概述

在DDD中,业务逻辑主要分布在领域层和应用层两层,他们包含不同的业务逻辑。这一篇,我们先对领域层做详细的讲解分析。

f6864ef0a47e96f3326bfafe676d51f3.png

领域层实现了领域或系统的,与用户界面上的用户交互(用例)无关的核心业务逻辑。

总览

a1f6bc9eb20d813a9ff095f5964beadd.png

领域层主要包含以下组件:

实体(Entity)实体是一个具有自己的属性(状态、数据)和实现在这些属性上执行的业务逻辑的方法的对象。实体由其唯一标识符(Id)表示。具有不同id的两个实体对象被视为不同的实体。

聚合和聚合根Aggregate&Aggregate Root聚合是通过集合根对象绑定在一起的对象集(实体和值对象)。聚合根是具有一些附加职责的实体的特定类型。

值对象Value Object值对象是另一种由其属性而不是唯一的Id来标识的域对象。这意味着两个具有相同属性的值对象将被视为相同的对象。值对象通常被实现为不可变对象,而且大多数值对象都比实体要简单得多。

域服务Domain Service领域服务是一个实现域的核心业务规则的无状态服务。它依赖于多个聚合(实体)类型或某些外部服务,用于领域逻辑的实现。

仓储Repository接口:仓储是一个类似于集合的接口,它被领域层和应用层用于访问数据持久化系统(数据库)。它对业务代码隐藏了DBMS的复杂性。领域层包含仓储的接口声明。

规约(Specification)规约用于为实体和其他业务对象定义已命名的、可重用的且可组合的过滤器。

域事件Domain Event领域事件是在领域特定事件发生时以松散耦合的形式通知其他服务的一种方法。

6989b5c66bdf94194dfce671608df44c.png

在ABP框架下,领域层包含以下两个项目:

Domain是包含所有组件(实体、聚合根、值对象、领域服务、规约、存储库接口等)的基本领域层。‍

Domain.Shared是一个轻量的项目,它包含了属于领域层的一些类型,但与所有其他层共享。例如,它可能包含一些与域对象相关的常量和枚举,但需要被其他层重用。

实现细节

1

实体

d99bf0154481c8f78eeb25695586bb10.gif

在ABP中,所有的实体类均需实现ABP框架中定义的接口 IEntity 。ABP中将实体分为两种用法:有默认主键(Id)的实体和没有默认主键的实体。

dcdd060a5667d54cc176a6a617091172.gif

有默认主键的实体在 IEntity 接口基础上封装了 IEntity<TKey> 接口,其中,定义了TKey类型的主键,名称为Id。其默认实现类继承自 Entity<TKey> 。我们使用时,需以泛型方式定义主键的类型。

ecfe3141412cdaa5461b99f19d87432d.gif

无默认主键的实体类直接实现IEntity接口,其默认实现类继承自 Entity ,在 Entity 中定义了虚方法 object[] GetKeys() 。该方法用于设定一个或多个属性为主键。使用无默认主键的实体时,我们必须实现GetKeys方法指定该实体类对应数据库表的主键。

0f5d30de4891cbc2dfc16c17e370159d.gif

在ABP中,对于实体定义了默认增删改操作的审计属性,通过 ICreationAuditedObject , IModificationAuditedObject 和 IDeletionAuditedObject 三个接口进行约束,分别包含对添加(添加人,添加时间),修改(修改人、修改时间),删除(删除人、删除时间、是否被软删除)操作记录的审计属性。ABP为ICreationAuditedObject接口创建了默认实现基类 CreationAuditedEntity ,同时还创建了包含以上三个接口所有实现的基类 FullAuditedEntity , CreationAuditedEntity 和 FullAuditedEntity 均包含有默认主键和无默认主键两种用法,以是否定义泛型来区分。

db11c5c0a0283e6c1265fa1fe4f9c364.gif

ABP中定义了 ISoftDelete 接口,其中包含 IsDeleted 属性,表示该条数据是否被软删除。当实体实现ISoftDelete接口时,ABP的仓储会自动将删除方法转为软删除,即只将IsDeleted属性设置为true,使其部不被查询到,但并不在数据库中对数据进行物理删除。

2

聚合根

b75a868ee3b7b17082a949e48dba0b60.gif

在ABP中聚合根需实现I AggregateRoot ,其默认基类为 AggregateRoot ,同时 AggregateRoot 也实现了 IEntity 接口。 IAggregateRoot 本身未包含任何属性或者方法的声明,其主键声明继承自IEntity接口。

3317f3645d7b31d145de429e82a5f413.gif

和实体类相同,聚合根也定义了有默认主键和无默认主键两种用法,通过是否传入泛型来进行区分。

22d1d1076000390e2e2a27d07d41efa9.gif

聚合根也可以实现 ICreationAuditedObject ,  IModificationAuditedObject 和 IDeletionAuditedObject 对增删改操作进行审计记录,其中实现接口 ICreationAuditedObject 的基类为 CreationAuditedAggregateRoot ,实现所有审计接口的基类为 FullAuditedEntity 。

3

值对象

501c04870e9f83d37a48fd8014e69584.gif

值对象的定义相对来说非常简单,在ABP中,定义了值对象的基类  ValueObject  ,定义值对象必须继承ValueObject  ValueObject 并实现ValueObject中定义的抽象方法 GetAtomicValues() 。

4e05c32f8fa526dd4c20e813f31d6bbb.gif

GetAtomicValues 方法用于检查两个值对象是否相等,其用法为通过 yield return 返回一组属性,表示如果两个值对象这些属性值都相等,则这两个为相等的值对象。示例代码如下:

protected override IEnumerable<object> GetAtomicValues() {    yield return Street;    yield return CityId;    yield return Number; }

4

领域服务

c7d26fd7796c8b664a6797caf1a19e81.gif

领域服务的接口和实现均包含在领域层Domain项目中,其中领域服务接口声明继承自ABP提供的 IDomainService 接口,领域服务实现类继承自ABP提供的 DomainService 类。

705f7cbbdd6e000118383cc61718b416.gif

在 IDomainService 接口中未声明任何属性和方法,我们如果需要用到领域服务,可完全依据自己的需求定义其中的内容。

2ae9b1ec76f42bffa1678baae0e09202.gif

在ABP框架中,领域服务通过自动依赖注入的方式以瞬态生命周期被注册到IOC容器中,即每次使用都会生成一个新的实例。我们只需要保证其所在类库中含有继承自 AbpModule 的模块定义类,即可实现自动依赖注入。

5

仓储接口

bfc18922adedee800ab47b2e81ff5d27.gif

在领域层Domain项目中,只会声明仓储的接口,其实现需要在基础设施层的EntityFrameworkCore项目中编写。

adccfb30240008174b9760243dab2780.gif

ABP框架已经为我们提供了默认的仓储接口和实现,在大多数情况下,我们不需要编写任何仓储接口或实现类的代码,只需要在使用时注入 IRepository<TEntity, TKey> ,其中的两个泛型分别为实体/聚合根的类型和主键数据类型。

b703a67b9f718b113d156c8550142d98.gif

如果我们需要自定义仓储,则在Domain层定义其接口声明,仓储接口声明需要继承自 IRepository 接口。

6

规约

5c60c9487a3eadfa28d60e0eddc1d23d.gif

ABP提供了规约的接口 ISpecification<T> 和实现类基类 Specification<T> ,其中泛型为需要约束的实体或聚合根的类型。

c1e533cec5d579169754b45a2c76760e.gif

编写规约时必须实现 ISpecification 接口中定义的 ToExpression 方法,该方法用来声明规约中数据约束的表达式。

04df26045f39a23d0a52f8dd82700635.gif

同时, ISpecification 接口中还定义了 IsSatisfiedBy 方法,用于判断一个对象是否满足此规约,在 Specification 类中已提供了此方法的默认实现。

7

领域事件

9c69ff32d4e7d3d1ce38169c4f438be2.gif

ABP提供了本地事件总线和分布式事件总线两种方案,其中本地事件总线常用于我们DDD中传统意义的领域事件实现,而分布式事件总线常用于分布式或微服务系统的事件处理。

299ae0b53aede7759bd17afda4e49d4a.gif

ABP本地领域事件使用主要分为订阅和发布两个部分:

ef875f76f79d2234b817762668382a4b.gif

发布和订阅的事件或对应关系以其传输的数据类型为区分。对于每种事件,我们都需要单独定义一个事件类型的简单类(ABP未提供统一基类,直接继承自Object即可)用于存放传输的数据,即使不需要传输数据,我们也要定义一个空类来区分事件类型。该类型命名通常以Event结尾。

84be11402024ce1957cc5855a78f940b.gif

在DomainService、ApplicationService、Controller等可以使用依赖注入的组件中,我们可以注入 ILocalEventBus 接口并调用 PublishAsync 方法来发布事件,在Entity和Aggregate Root中无法使用依赖注入,ABP官方给他们提供了一个基础方法 AddLocalEvent 用于发布事件。

561425a67114551a8e5f7d44fdbfeb46.gif

在订阅事件时我们需要定义一个类,实现 ILocalEventHandler<T>  其中的泛型为前面所说的事件类型定义,并实现接口中的 HandleEventAsync 方法用于处理事件。

END

a7c4beee43dd6df1e2230a10f670fa11.png

625dc2c371b6ba58539c75465dd25fd2.png

关注我获得

更多精彩

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

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

相关文章

【北斗】北斗卫星导航系统(BDS)介绍

一、概述 北斗卫星导航系统(以下简称北斗系统)是中国着眼于国家安全和经济社会发展需要,自主建设运行的全球卫星导航系统,是为全球用户提供全天候、全天时、高精度的定位、导航和授时服务的国家重要时空基础设施。 北斗系统提供服务以来,已在交通运输、农林渔业、水文监…

正则验证金额大于等于0,并且只到小数点后2位

2019独角兽企业重金招聘Python工程师标准>>> ^(([0-9]|([1-9][0-9]{0,9}))((\.[0-9]{1,2})?))$ 转载于:https://my.oschina.net/u/934148/blog/528688

我结婚了,我要用什么做个邀请函呢?【iVX无代码YYDS 06】

作者简介 作者名&#xff1a;1_bit 简介&#xff1a;CSDN博客专家&#xff0c;2020年博客之星TOP5&#xff0c;InfoQ签约作者、CSDN新星导师&#xff0c;华为云享专家。15-16年曾在网上直播&#xff0c;带领一批程序小白走上程序员之路。欢迎各位小白加我咨询我相关信息&#…

《微软云计算Microsoft Azure部署与管理指南》即将上市!!!

大家好&#xff0c;本人新作《微软云计算Microsoft Azure部署与管理指南》即将与广大读者见面&#xff0c;由电子工业出版社出版。希望大家能关注此书&#xff0c;并推荐给身边的好友和技术人员。 众所周知&#xff0c;Microsoft Azure是专业的国际化公有云平台, 是微软研发的公…

如何解决分布式日志exceptionless的写入瓶颈

我们都知道在分布式日志当中&#xff0c;exceptionless客户端是把日志写到Elasticsearch数据库&#xff0c;就像我们把数据写入到关系数据库一样&#xff1b;既然是写入&#xff0c;那么在短时间大数据量的情况下&#xff0c;写入就会涉及到效率的问题&#xff1b;首先我们看下…

iVX 基础

1.1 iVX 线上集成环境进入 点击 连接 或通过浏览器输入网址 https://editor.ivx.cn/ 进入线上集成开发环境。 进入 在线集成开发环境 后&#xff0c;可点击右上角 登录/注册 进行帐号登录或者注册。登录账户 后在进行项目开发时会自动保存项目开发进度。 [外链图片转存失败…

Android之靠谱的把图片和视频插入手机系统相册

1 需求 把图片和视频插入手机系统相册,网上查了下基本上很乱,没几个靠谱的。 2 结果爆照 3 思路 图片插入系统相册(可以直接插入系统相册,但是我这里多做了一步就是先把图片拷贝到了一个目录再插入系统相册) 视频插入系统相册(先把视频拷贝到MIUI目录,然后再…

C# WPF 实现Tab页动态增减

概述Tab页面是一个很常用的控件&#xff0c;针对页面固定的场景&#xff0c;直接给Item进行数据绑定就行&#xff0c;如下所示&#xff1a;<dx:DXTabControl cal:Message.Attach"[Event Loaded][TabControl_Loaded($source,$eventArgs)]"><dx:DXTabItem Hea…

2014 网选 上海赛区 hdu 5047 Sawtooth

题意&#xff1a;求n个M型的折线将一个平面分成的最多的面数&#xff01; 思路&#xff1a;我们都知道n条直线将一个平面分成的最多平面数是 An An-1 n1 也就是f(n) (n*n n 2)/2 对于一个M型的折线呢&#xff1f;它有四条线&#xff0c;但是由于三个顶点的关系导致划分的平…

二、基础(IVX快速开发手册)

二、基础 通过本节你将了解 iVX 所支持应用的创建方法。 文章目录二、基础2.1 iVX 线上集成环境进入2.2 创建项目2.3 选择项目类型2.3.1 WebApp/小程序/原生应用2.3.2 微信小游戏2.3.3 微信小程序&#xff08;原生组件&#xff09;2.1 iVX 线上集成环境进入 点击 连接 或通过…

Android之TabLayout+ViewPager2+FragmentStateAdapter实现带数字变化的TAB选项

1 问题 TabLayout+ViewPager2实现带数字变化的TAB选项,然后左边滑动或者点击上面的Tab切换fragment不能刷新 2 结果爆照 3 代码实现 layer_tab_indicator.xml <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="h…

slq2000数据库升级到sql2012

看到标题&#xff0c;估计有同行笑了&#xff0c;这年代还有用sql2000的&#xff1f;真的有&#xff0c;最近单位服务器数据迁移升级&#xff0c;将数据库迁移到新服务器后&#xff0c;发现数据全是2000的&#xff0c;无法直接导入到sql2012。没办法&#xff0c;只能先将数据库…

电脑网页打不开但qq能上解决方法

2019独角兽企业重金招聘Python工程师标准>>> 问题描述&#xff1a; 电脑网页打不开但qq能上。 问题原因&#xff1a; 是由于电脑系统的DNS解析出了问题。 解决方法&#xff1a; 首先在键盘上同时按下 winR 然后在弹窗中输入cmd &#xff0c; 再按enter键&#xf…

基于Linux命令行KVM虚拟机的安装配置与基本使用

背景由于生产环境的服务器并不会安装桌面环境&#xff0c;简单操作的图形化安装也不适合批量部署安装。因此&#xff0c;我还是更倾向于在命令下安装配置KVM虚拟机。结合了一些资料和个人使用的状况&#xff0c;我大致列出了一些基本和常用的使用方法。 安装配置一、环境介绍操…

四、WebApp 基础可视组件(IVX 快速开发教程)

四、基础可视组件 通过本节你将了解 iVX 开发中的核心—— iVX 组件的使用方法。iVX 的组件是开发应用时所必要的对象&#xff0c;通过这些对象你将快速的完成应用的开发。 在 iVX 应用开发中&#xff0c;所有交互、动画、数据都需要以组件为基础&#xff0c;通过组件之间的编…

Springboot项目搭建(三)整合thymeleaf模板

springboot整合thymeleaf模板 一、POM文件添加依赖 <!--thymeleaf--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency><!--nekohtml 解决thymea…

React-引领未来的用户界面开发框架-读书笔记(一)

这本书的主要内容都是以react v0.12为主&#xff0c;ES5语法&#xff0c;笔记中将会使用react v0.14和RS6。 第1章 react简介 1.本质上是一个状态机&#xff0c;它以精简的模型管理复杂的随着时间而变化的状态。 2.它不是model-view-controller&#xff0c;它是mvc中的v(view)&…

Android之提示This version of Android Studio cannot open this project, please retry with Android Studio

1 问题 编译项目&#xff0c;错误提示如下 This version of Android Studio cannot open this project, please retry with Android Studio 2 解决办法 很明显&#xff0c;看英语翻译也知道&#xff0c;是由于AS版本太低导致&#xff0c;升级AS就可以了。

Netflix 的 API 架构演变历程

Netflix 以其松耦合和高度可扩展的微服务架构而闻名&#xff0c;Netflix API 的后端架构经历了 4 个主要阶段。&#x1d40c;&#x1d428;&#x1d427;&#x1d428;&#x1d425;&#x1d422;&#x1d42d;&#x1d421; &#x1d40c;&#x1d428;&#x1d427;&#x1d…

五、Web App 基础可视组件属性(IVX 快速开发教程)

五、基础可视组件属性 在 iVX 中各个组件存在不同的属性&#xff0c;这些属性用于设置显示的样式或者是自身具备的特征等&#xff0c;通过更改这些属性可以极大的方便我们进行项目的创作。 大多数组件都拥有相同的属性&#xff0c;相同属性在以下内容中不会赘述介绍&#xff…