[Abp vNext 源码分析] - 6. DDD 的应用层支持 (应用服务)
一、简要介绍
ABP vNext 针对于应用服务层,为我们单独设计了一个模块进行实现,即 Volo.Abp.Ddd.Application 模块。
PS:最近博主也是在恶补 DDD 相关的知识,这里推荐大家看一下 ThoughtWorks 的 DDD 相关文章。
关于 DDD 相关的著作,我这儿还是推荐经典的那三本《领域驱动设计:软件核心复杂性应对之道》、《实现领域驱动设计》、《领域驱动设计精粹》。
DDD 的学习整体来说是比较枯燥的,而且偏理论化的知识。所以需要结合大量实例来看,反复对照书中的概念加深理解。不仅要看别人的实例,自己也要尝试运用 DDD 的战略方法和战术方法进行设计。
应用服务层在 DDD 分层架构里面是最顶层的,一般与前端(展示层)打交道的都是应用服务层。常规的开发人员,如果没有遵循 DDD 理论来进行开发的话,应用服务层是十分臃肿的,里面全是业务逻辑。而领域层里面则是空无一物,全是贫血的领域模型对象。这种模式被称之为 贫血领域模型模式,这是一个 反模式。
这里我就不再赘述应用服务层与 DDD 之间的关系了,在这里你可以看作它是一个 API 接口实现类,你所有对外开放的接口都是通过应用服务层暴露的,接口的方法应该与用例相对应。
二、源码分析
应用服务层模块里面比较简单,只有两个文件夹,分别存放了数据传输模型(Dtos)和应用服务基类定义(Services)。
2.1 启动模块
首先我们还是按照之前的顺序,看一个模块先看他的模块类。这里我们先看一下 AbpDddApplicationModule 的代码。
[DependsOn(
typeof(AbpDddDomainModule),
typeof(AbpSecurityModule),
typeof(AbpObjectMappingModule),
typeof(AbpValidationModule),
typeof(AbpAuthorizationModule),
typeof(AbpHttpAbstractionsModule),
typeof(AbpSettingsModule),
typeof(AbpFeaturesModule)
)]
// 不要看上面依赖这么多模块,主要是因为基类会用到很多基础组件。
public class AbpDddApplicationModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
// 配置接口类型。
Configure<ApiDescriptionModelOptions>(options =>
{
options.IgnoredInterfaces.AddIfNotContains(typeof(IRemoteService));
options.IgnoredInterfaces.AddIfNotContains(typeof(IApplicationService));
options.IgnoredInterfaces.AddIfNotContains(typeof(IUnitOfWorkEnabled));
});
}
}
可以看到,在上述代码里面,只做了一件事情,就是调用 ApiDescriptionModelOptions ,往里面添加了 IRemoteService、IApplicationService、IUnitOfWOrkEnabled 三种接口类型。添加了三种类型之后,ABP vNext 根据应用服务类创建控制器时,就会从这个 IgnoredInterfaces 判断哪些类型不被忽略 (即只会自动注册实现了三种接口的类型成为控制器)。
2.2 应用服务基类
ABP vNext 提供了标准基类 ApplicationService 和简单 Crud 基类 CrudAppService 给我们使用,前者只是继承了 IApplicationService 接口,并提供了基本组件的简单基类。而后者则是定义了 Crud 操作所需要的所有 API 方法,你只需要继承这个基类对象,填充相应的泛型参数,就可以快速实现一个 Crud 接口。
2.2.1 简单基类
简单基类里面我们首先需要注意的是它实现的接口,你可以发现 ApplicationService 实现了诸多接口,不过这些接口更多的是类似于标识接口。
public abstract class ApplicationService :
IApplicationService,
IAvoidDuplicateCrossCuttingConcerns,
IValidationEnabled,
IUnitOfWorkEnabled,
IAuditingEnabled,
ITransientDependency
{
// ... 其他代码
}
所有应用服务都必须继承 IApplicationService,这个是肯定的,不然 ABP vNext 不会为我们生成需要的控制器。
其次是 IAvoidDuplicateCrossCuttingConcerns 接口,这个接口最早可以追溯到老版本 ABP 框架里面。它的主要作用是防止拦截器进行重复执行。
public interface IAvoidDuplicateCrossCuttingConcerns
{
List<string> AppliedCrossCuttingConcerns { get; }
}
例如调用购买这个 API 接口,首先会进入 ASP.NET Core 的审计日志 Filter,在 Filter 里面会将这个 API 接口归属的类型的 List 容器(接口里面定义的 List )里面写入一条记录,说明已经通过审计日志过滤器记录了。
写了审计日志之后,又会进入审计日志拦截器,这个时候拦截器就会对指定的类型进行判断,看是否已经被执行过了,因为这个类型的 List 容器有了之前过滤器的记录,所以不会重复执行。
public override void Intercept(IAbpMethodInvocation invocation)
{
if (!ShouldIntercept(invocation, out var auditLog, out var auditLogAction))
{
invocation.Proceed();
return;
}
// ... 审计日志记录。
}
protected virtual bool ShouldIntercept(
IAbpMethodInvocation invocation,
out AuditLogInfo auditLog,
out AuditLogActionInfo auditLogAction)
{
// 判断实例的 List 容器里面,是否写入了 AbpCrossCuttingConcerns.Auditing。
if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Auditing))
{
return false;
}
// ... 其他代码
return true;
}
剩余的 IValidationEnabled、IUnitOfWorkEnabled、IAuditingEnabled、ITransientDependency 接口类似于一个启用标识,只要类型继承了该接口,就会执行一些特殊的操作。
回到之前的简单基类里面,ABP vNext 为我们注入了大量基础设施,例如获取当前用户的 ICurrentUser 组件,获取当前租户的 ICurrentTenant 组件,还有日志组件等。
除了基础组件,ABP vNext 在简单基类里面还提供了一个权限检测方法,用户检测当前用户是否具备某些权限。
protected virtual async Task CheckPolicyAsync([CanBeNull] string policyName)
{
if (string.IsNullOrEmpty(policyName))
{
return;
}
await AuthorizationService.CheckAsync(policyName);
}
在不具备权限的时候,ABP vNext 会抛出 AbpAuthorizationException 异常。
2.2.2 Crud 基类
Crud 基类可以极大减少对于某些简单对象的代码编写,例如我有个客户管理接口,只需要简单地增删改查操作。那么我就可以直接继承自 Crud 基类,给它填写和是的泛型参数之后,ABP vNext 就会为我们生成带有增删改查操作的应用服务对象。
这个 Crud 基类拥有多个泛型定义与实现,除了真正的实现以外,其他的都是简单的调用基类方法而已。我们直接进入主题,看一下类型签名为 public abstract class CrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput> 的基类。
public abstract class CrudAppService<TEntity, TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
: ApplicationService,
ICrudAppService<TGetOutputDto, TGetListOutputDto, TKey, TGetListInput, TCreateInput, TUpdateInput>
where TEntity : class, IEntity<TKey>
where TGetOutputDto : IEntityDto<TKey>
where TGetListOutputDto : IEntityDto<TKey>
{
public virtual async Task<TGetOutputDto> GetAsync(TKey id)
{
// 具体代码。
}
public virtual async Task<PagedResultDto<TGetListOutputDto>> GetListAsync(TGetListInput input)
{
// 具体代码。
}
public virtual async Task<TGetOutputDto> CreateAsync(TCreateInput input)
{
// 具体代码。
}
public virtual async Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input)
{
// 具体代码。
}
public virtual async Task DeleteAsync(TKey id)
{
// 具体代码。
}
}
从上述代码可以看到基类根据传入的泛型参数,将会为我们实现常规的增删改查逻辑。我们也可以随时重写这些方法,来达到一些个性化的操作。
ABP vNext 抽象了公用接口以外,在内部还编写了诸如 MapToEntity() 和 MapToEntity() 等内部共用方法,这里就不再详细赘述,这些方法都是 protected 修饰的,你也可以随时重写来达到自己的目的。
2.3 数据传输对象
一般来说,应用服务层返回给展示层的数据肯定是某个实体对象的部分属性,或者是多个聚合的整体,这个时候就需要 DTO 来帮我们处理应用服务层与外部的数据交换了。
ABP vNext 在应用服务模块定义了常用的一些 DTO 对象,例如实体 DTO 和分页查询 DTO,关于这些 DTO 你只需将其看作一个数据容器即可,不需要太多关注,这里也没有太多要讲的。
三、总结
ABP vNext 提供的应用服务层模块还是比较简单的,里面主要是针对应用服务基类进行了预定义。方便我们开发人员进行业务开发,而不需要自己实现这些繁杂的基类。
在 DDD 当中,应用服务是表达 用户用例 和 用户故事 的主要手段,应用服务只是通过领域对象/领域服务来表达需求用例的一个组件。不要将业务逻辑泄漏到应用服务当中,这种设计最终会导致贫血领域模型。
四、点击我跳转到文章目录
[Abp vNext 源码分析] - 6. DDD 的应用层支持 (应用服务)的更多相关文章
- [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)
一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...
- [Abp vNext 源码分析] - 文章目录
一.简要介绍 ABP vNext 是 ABP 框架作者所发起的新项目,截止目前 (2019 年 2 月 18 日) 已经拥有 1400 多个 Star,最新版本号为 v 0.16.0 ,但还属于预览版 ...
- [Abp vNext 源码分析] - 4. 工作单元
一.简要说明 统一工作单元是一个比较重要的基础设施组件,它负责管理整个业务流程当中涉及到的数据库事务,一旦某个环节出现异常自动进行回滚处理. 在 ABP vNext 框架当中,工作单元被独立出来作为一 ...
- [Abp vNext 源码分析] - 7. 权限与验证
一.简要说明 在上篇文章里面,我们在 ApplicationService 当中看到了权限检测代码,通过注入 IAuthorizationService 就可以实现权限检测.不过跳转到源码才发现,这个 ...
- [Abp vNext 源码分析] - 9. 接口参数的验证
一.简要说明 ABP vNext 当中的审计模块早在 依赖注入与拦截器一文中有所提及,但没有详细的对其进行分析. 审计模块是 ABP vNext 框架的一个基本组件,它能够提供一些实用日志记录.不过这 ...
- [Abp vNext 源码分析] - 14. EntityFramework Core 的集成
一.简要介绍 在以前的文章里面,我们介绍了 ABP vNext 在 DDD 模块定义了仓储的接口定义和基本实现.本章将会介绍,ABP vNext 是如何将 EntityFramework Core 框 ...
- [Abp vNext 源码分析] - 3. 依赖注入与拦截器
一.简要说明 ABP vNext 框架在使用依赖注入服务的时候,是直接使用的微软提供的 Microsoft.Extensions.DependencyInjection 包.这里与原来的 ABP 框架 ...
- [Abp vNext 源码分析] - 2. 模块系统的变化
一.简要说明 本篇文章主要分析 Abp vNext 当中的模块系统,从类型构造层面上来看,Abp vNext 当中不再只是单纯的通过 AbpModuleManager 来管理其他的模块,它现在则是 I ...
- [Abp vNext 源码分析] - 1. 框架启动流程分析
一.简要说明 本篇文章主要剖析与讲解 Abp vNext 在 Web API 项目下的启动流程,让大家了解整个 Abp vNext 框架是如何运作的.总的来说 ,Abp vNext 比起 ABP 框架 ...
随机推荐
- 基于SAP HANA平台的多团队产品研发
工欲善其事必先利其器.要提高多团队的开发效率,而且还是在SAP HANA平台上,建议大家还是本着“慢就是快”的原则,不要急功近利,在没有准备好团队开发的架构时就匆忙开始功能的开发.匆忙功能开发就算了, ...
- Md2All:好用的markdown文件转换工具,文章迁移微信公众号的利器
目录 简介 使用体验 极速上手 更多功能 总结 简介 markdown以简单的语法和强大的功能,征服了无数技术创作者,几乎主流的技术博客网站都开始支持markdown语言撰写博客.但是微信公众号的文章 ...
- 在.NET Core 3.0中的WPF中使用IOC图文教程
我们都知道.NET Core 3.0已经发布了第六个预览版,我们也知道.NET Core 3.0现在已经支持创建WPF项目了,刚好今天在写一个代码生成器的客户端的时候用到了WPF,所以就把WPF创建以 ...
- [Java] 父类和子类拥有同名的成员变量(fields)的情况
首先,需要明确的是,无论是通过casting,还是通过将子类对象的reference赋值给父类变量,都无法改变该reference所指对象的真实类型.但当该reference的类型是父类时,将无法调用 ...
- Spring如何解决循环引用
概念 什么是循环引用? 故名思义,多个对象形成环路. 有哪几种循环引用? 在Spring中存在如下几种循环引用,一一举例分析一下 注入循环引用(Set注入 注解注入) package c.q.m; i ...
- Zookeeper详解-伪分布式和集群搭建(八)
说到分布式开发Zookeeper是必须了解和掌握的,分布式消息服务kafka .hbase 到hadoop等分布式大数据处理都会用到Zookeeper,所以在此将Zookeeper作为基础来讲解. Z ...
- spring boot 2.x 系列 —— actuator 服务监控与管理
文章目录 一.概念综述 1.1 端点 1.2 启用端点 1.3 暴露端点 1.4 健康检查信息 二.项目说明 1.1 项目结构说明 1.2 主要依赖 1.3 项目配置 1.4 查看监控状态 三.自定义 ...
- spring 5.x 系列第3篇 —— spring AOP (xml配置方式)
文章目录 一.说明 1.1 项目结构说明 1.2 依赖说明 二.spring aop 2.1 创建待切入接口及其实现类 2.2 创建自定义切面类 2.3 配置切面 2.4 测试切面 附: 关于切面表达 ...
- AbstractQueuedSynchronizer(AQS)源码解析
关于AQS的源码解析,本来是没有打算特意写一篇文章来介绍的.不过在写本学期课程作业中,有一门写了关于AQS的,而且也画了一些相关的图,所以直接拿过来分享一下,如有错误欢迎指正. ...
- kali Linux渗透测试技术详解
kali Linux渗透测试技术详解 下载:https://pan.baidu.com/s/1g7dTFfzFRtPDmMiEsrZDkQ 提取码:p23d <Kali Linux渗透测试技术详 ...