标题:重温ABP领域层

1. 前言

最近一段时间一直在看《ABP的开发指南》(基于DDD的经典分层架构思想)。因为之前一段时间刚看完《领域驱动设计:软件核心复杂性应对之道》,概念比较多,看着有点空。于是拿起了这本书。应该说是不是书, 只是一个PDF版的开发指南。于是乎,就开始了。好了,废话不多说,首先是ABP领域层的结构介绍,如下图所示:

从图中可以看到,ABP的领域层分为 实体仓储工作单元数据过滤器,以及领域事件五个部分。这五个部分的功能作用,如果看过了解过DDD的应该不会陌生。接下来我将一一介绍这五个部分的详情,每个部分具体作用,以及实现。顺便给自己温习温习。附加一点自己粗浅的理解,如有不正确的地方,欢迎指正,交流。

2. 各个模块

2.1 ABP领域层-实体

很多对象不是通过它们的属性定义的,而是通过一连串的连续性事件和标识定义的。———摘录自《领域驱动设计》第5章 5.2 模式:Entity(又称为Reference Object)

在ABP的实体中,实体继承至Entity。可以细分为下面三个部分。1、实体类,2、接口约定以及3、IEntity接口

2.1.1 首先是第一个实体类

查看源码发现如下:

namespace Abp.Domain.Entities
{
/// <summary>
/// A shortcut of <see cref="T:Abp.Domain.Entities.Entity`1" /> for most used primary key type (<see cref="T:System.Int32" />).
/// </summary>
[Serializable]
public abstract class Entity : Entity<int>, IEntity, IEntity<int>
{
}
}

然后最后我们看下IEntity<TPrimary> 之后就真相大白了。

namespace Abp.Domain.Entities
{
/// <summary>
/// Defines interface for base entity type. All entities in the system must implement this interface.
/// </summary>
/// <typeparam name="TPrimaryKey">Type of the primary key of the entity</typeparam>
public interface IEntity<TPrimaryKey>
{
/// <summary>Unique identifier for this entity.</summary>
TPrimaryKey Id { get; set; } /// <summary>
/// Checks if this entity is transient (not persisted to database and it has not an <see cref="P:Abp.Domain.Entities.IEntity`1.Id" />).
/// </summary>
/// <returns>True, if this entity is transient</returns>
bool IsTransient();
}
}

相当于每个继承于 Entity 的都有一个Id属性。Id属性是该实体的主键,所以,Id是所有继承自Entity类的实体的主键。当然,从源码中,我们可以发现。很显然这个Id数据的类型可以被更改。因为是<TPrimary>类型的,所以,你如果想要改成long,或者Guid等等啦。只需要向如下这样定义:

public class ClassA : Entity<Guid>
{
//...
}

2.1.2 第二个是接口约定

刚开始听到这个名字时,我觉得有点难以理解。但是在文章中,听了一下解释。可以帮助理解,如下:

很多实体会有像CreationTime 用来指示该实体是什么时候被创建的。ABP提供了一些有用的接口来实现类似的功能,只需要实现这些接口的实体,就能够实现指定的功能。

接口预定中,又分为如下三个部分:

首先是审计(Auditing),这个接口主要的是CreationTime 这个属性。只要实体类实现IHasCreationTime 接口,当该实体被插入到数据库时,ABP会自动设置该属性的值为当前时间。

public interface IHasCreationTime{
DateTime CreationTime{get;set;}
}

审计中还有一个接口ICreationAudited ,这个接口是扩展至IHasCreationTime ,并且该接口还有一个属性CreatorUserId ,可以用来记录当前用户的Id,

public interface ICreationAudited : IHasCreationTime{
long ? CreatorUserId{get;set;}
}

CreationAuditedEntity 类已实现了 ICreationAudited 我们可以直接继承来使用。

审计中还有一个实现类似修改功能的接口IModificationAudited

public interface IModificationAudited{
DateTime? LastModificationTime{get;set;}
ling? LastModifierUserId{get;set;}
}

当然,如果你想都实现这些审计属性,ABP也提供给你了。IAudited 接口。

public interface IAudited : ICreationAudited, IModificationAudited{

}

可能你有点乱了,我来给你理理。大概就是如下这样的。

这样简单吧。

接下来是软删除, 表示标记了一个实体已经被删除了。而不是从数据库中删除记录。对应的接口为ISoftDelete

public interface ISoftDelete{
bool IsDeleted{get;set;}
}

来源于文中:当一个实现了软删除的实体正在被删除,ABP会察觉到这个动作,并且阻止其删除,设置 IsDeleted 属性值为 true 并且更新数据库中的实体。

和审计中的ICreationAudited类似, 记录谁删除了这个实体。IDeletionAudited 接口。

public interface IDeletionAudited: ISoftDelete{
long? DeleterUserId{get;set;}
DateTime? DeletionTime{get;set;}
}

当然,如果你想要实现所有(包括,创建,修改,和删除。)那就实现这个终极接口, IFullAudited

public interface IFullAudited: IAudited , IDeletionAudited{
//...
}

实体类 FullAuditedEntity 实现了 IFullAudited 接口。可以直接继承使用。

那么审计的终极总结图就如下:

很清楚了吧!

PS:详情可以查看源代码 Abp.Domain.Entities.Auditing

2.1.3最后一个是IEntity接口

在2.1.1 中已经介绍了一下,这里就不赘述。Entity 实现了 IEntity接口(和Entity<TPrimaryKey> 实现了 IEntity<TPrimaryKey> 接口)。

2.2 ABP领域层-仓储

2.3 ABP领域层-工作单元

2.4 ABP领域层-数据过滤器

2.5 ABP领域层-领域事件

3. 结语

本想一次性把这些都总结整理一下,突然发现内容有点多。还是一步一步来,慢慢整理把。这次先整理实体。好尴尬。

4.附录

参考:Github_ABPChina

参考链接:

ABP领域层知识回顾之---实体的更多相关文章

  1. ABP领域层知识回顾之---仓储

    1. 前言  在上一篇博文中 http://www.cnblogs.com/xiyin/p/6810350.html 我们讲到了ABP领域层的实体,这篇博文继续讲ABP的领域层,这篇博文的主题是ABP ...

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

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

  3. ABP(现代ASP.NET样板开发框架)系列之10、ABP领域层——实体

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之10.ABP领域层——实体 ABP是“ASP.NET Boilerplate Project (ASP.NET样板 ...

  4. ABP领域层——实体

    ABP领域层——实体 基于DDD的现代ASP.NET开发框架--ABP系列之10.ABP领域层——实体 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的 ...

  5. ABP(现代ASP.NET样板开发框架)系列之11、ABP领域层——仓储(Repositories)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之11.ABP领域层——仓储(Repositories) ABP是“ASP.NET Boilerplate Proj ...

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

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

  7. ABP(现代ASP.NET样板开发框架)系列之13、ABP领域层——数据过滤器(Data filters)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之13.ABP领域层——数据过滤器(Data filters) ABP是“ASP.NET Boilerplate P ...

  8. ABP(现代ASP.NET样板开发框架)系列之14、ABP领域层——领域事件(Domain events)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之14.ABP领域层——领域事件(Domain events) ABP是“ASP.NET Boilerplate P ...

  9. ABP领域层——领域事件(Domain events)

    ABP领域层——领域事件(Domain events) 基于DDD的现代ASP.NET开发框架--ABP系列之14.ABP领域层——领域事件(Domain events) ABP是“ASP.NET B ...

随机推荐

  1. Django学习(六)---博客文章页面的超链接设置

    Django中的超链接 超链接的目标地址 href后面是目标地址 template中可以用 {% url  'app_name : url_name'   param %} app_name:应用命名 ...

  2. Leetcode 14——Longest Common Prefix

    题目:Write a function to find the longest common prefix string amongst an array of strings. 很简单的一个描述,最 ...

  3. JavaScript(第二十七天)【错误处理与调试】

    JavaScript在错误处理调试上一直是它的软肋,如果脚本出错,给出的提示经常也让人摸不着头脑.ECMAScript第3版为了解决这个问题引入了try...catch和throw语句以及一些错误类型 ...

  4. C语言程序设计(基础)- 第6周作业

    一.PTA作业 完成PTA第六周作业中4个题目的思路列在博客中. 1.7-1 高速公路超速处罚 2.7-2 计算油费 3.7-3 比较大小 4.7-4 两个数的简单计算器 (必须使用switch结构实 ...

  5. Beta No.5

    今天遇到的困难: 前端大部分代码由我们放逐的组员完成,这影响到了我们解决"Fragment碎片刷新时总产生的固定位置"的进程,很难找到源码对应 新加入的成员对界面代码不熟悉. 我们 ...

  6. Alpha冲刺Day3

    Alpha冲刺Day3 一:站立式会议 今日安排: 我们把项目大体分为四个模块:数据管理员.企业人员.第三方机构.政府人员.数据管理员这一模块,数据管理员又可细分为两个模块:基础数据管理和风险信息管理 ...

  7. appcompat v21: 让 Android 5.0 前的设备支持 Material Design

    1. 十大Material Design开源项目 2. appcompat v21: 让 Android 5.0 前的设备支持 Material Design 主题 AppCompat已经支持最新的调 ...

  8. jav音频格式转换 ffmpeg 微信录音amr转mp3

    项目背景: 之前公司开发了一个微信公众号,要求把js-sdk录音文件在web网页也能播放.众所周知,html的<audio>标签ogg,mp3,wav,也有所说苹果safari支持m4a格 ...

  9. Angular组件——组件生命周期(一)

    组件声明周期以及angular的变化发现机制 红色方法只执行一次. 变更检测执行的绿色方法和和组件初始化阶段执行的绿色方法是一个方法. 总共9个方法. 每个钩子都是@angular/core库里定义的 ...

  10. python+flask 分分钟完美解析阿里云日志

    拿到了自己阿里云服务器的日志,对其需要进行处理. class Read_Rizhi: def __init__(self,filename): self.filename=filename def o ...