本文主要介绍了多应用层的问题,包括原因和实现。通过理解介绍了如何区分领域逻辑和应用逻辑,哪些是正确的实践,哪些是不推荐的或者错误的实践。

一.多应用层的问题

1.多应用层介绍

  不知道你们是否会遇到一种情况,通过ABP构建了一个后端的API项目,刚开始是为Web端项目(比如,Vue)提供后端接口服务的,随着项目的发展和业务的复杂,增加了移动端的App,或者公众号、小程序等,这样不仅要为Web端提供API服务,而且还需要为移动端的App,或者公众号、小程序等提供API服务。这个场景就是多应用层的问题。也就是说你现在需要构建一个有多个应用程序的系统了:

  • Web应用程序:比如使用的ASP.NET Core MVC技术,主要用来给用户展示产品,游客模式是可以查看产品的,并不需要登录和验证。
  • 后端管理应用程序:比如前端是Vue,后端的ASP.NET Core API,主要用来对产品进行增删改等操作。
  • 移动应用程序:比如前端是uni-app,后端是ASP.NET Core API,通过REST接口和后端通信。

2.多应用层例子

  由于业务的复杂性,每个应用系统都有自己的不同应用服务方法,不同的输入和输出DTO,不用的认证和授权规则等,所以如果把所有的业务逻辑都融入到一个应用系统中,就会让系统更难开发、维护和测试,并导致潜在的Bug风险。因此,为每个应用程序创建单独的应用层,并且它们都是用单个领域层来共享核心领域逻辑。为了更加具体的说明,为每种应用程序类型创建不同的.csproj项目:

  • 后端管理应用程序:IssueTracker.Admin.Application和IssueTracker.Admin.Application.Contracts项目
  • Web应用程序:IssueTracker.Public.Application和IssueTracker.Public.Application.Contracts项目
  • 移动应用程序:IssueTracker.Mobile.Application和IssueTracker.Mobile.Application.Contracts项目

二.如何区分领域逻辑和应用逻辑

通常,DDD中的业务逻辑包括领域逻辑和应用逻辑。领域逻辑由系统的核心领域规则组成,而应用程序逻辑实现特定于应用程序的用例。话虽这样说,但是并不容易区分什么是领域逻辑和应用逻辑。

1.在领域服务层中创建组织Organization

下面通过在领域服务中创建Organization这个例子,来尽可能简要说明:

public class OrganizationManager:DomainService
{
private readonly IRepository<Organization> _organizationRepository; //Organization的仓储
private readonly ICurrentUser _currentUser; //当前用户
private readonly IAuthorizationService _authorizationService; //Authorization的服务
private readonly IEmailSender _emailSender; //邮件发送服务 // 公共构造函数,依赖注入
public OrganizationManager(IRepository<Organization> organizationRepository, ICurrentUser currentUser, IAuthorizationService authorizationService, IEmailSender emailSender)
{
_organizationRepository=organizationRepository;
_currentUser=currentUser;
_authorizationService=authorizationService;
_emailSender=emailSender;
} // 创建一个新的组织
public async Task<Organization> CreateAsync(string name)
{
// 如果组织存在同名,那么抛出异常[正确]
if(await _organizationRepository.AnyAsync(x=>x.Name==name))
{
throw new BusinessException("IssueTracking:DuplicateOrganizationName");
} // 检查是否拥有创建的权限[错误]
await _authorizationService.CheckAsync("OrganizationCreationPermission"); // 记录⽇志[错误]
Logger.LogDebug($"Creating organization {name} by {_currentUser.UserName}"); // 创建一个新的组织
var organization = new Organization(); // 发送邮件进行提醒[错误]
await _emailSender.SendAsync("systemadmin@issuetracking.com", "新组织", "新组织名称:"+name); // 返回一个组织实例
return organization;
}
}
  • 领域服务不做权限验证,权限验证放在应用层来做
  • 用户概念是应用层或展示层的相关概念,记录日志不应该包含当前用户的用户名
  • 创建一个新的组织,并发送邮件,这个业务逻辑也应该放在应用层

2.在应用层中使用领域服务创建组织Organization

public class OrganizationAppService:ApplicationService
{
private readonly OrganizationManager _organizationManager; //组织的领域服务
private readonly IPaymentService _paymentService; //支付服务
private readonly IEmailSender _emailSender; //邮件服务 // 公共构造函数,依赖注入
public OrganizaitonAppService(OrganizationManager organizationManager, IPaymentService paymentService, IEmailSender emailSender)
{
_organizationManager=organizationManager;
_paymentService=paymentService;
_emailSender=emailSender;
} // 创建组织
[UnitOfWork][正确] //工作单元,用于提交事务
[Authorize("OrganizationCreationPermission")][正确]
public async Task<Organization> CreateAsync(CreateOrganizationDto input)
{
// ⽀付组织的费⽤[正确]
await _paymentService.ChargeAsync(CurrentUser.Id, GetOrganizationPrice()); // 通过领域服务,创建一个新的组织实例
var organization = await _organizationManager.CreateAsync(input.Name); // 保存和更新组织到数据库中[正确]
await _organizationManager.InsertAsync(organization); // 发送提醒邮件[正确]
await _emailSender.SendAsync("systemadmin@issuetracking.com", "新组织", "新组织名称:"+name); //返回实例[错误]
return organization;
}
private double GetOrganizationPrice()
{
return 42;//Gets form somewhere...
}
}

应用服务层的输入和输出参数都是DTO,不能返回实体。至于为什么不将支付放在领域服务中,只能说业务重要也不一定放在领域服务中,详细原因说明参考[1]。

参考文献:

[1]基于ABP Framework实现领域驱动设计:https://url39.ctfile.com/f/2501739-616007877-f3e258?p=2096 (访问密码: 2096)

基于ABP实现DDD--领域逻辑和应用逻辑的更多相关文章

  1. 基于ABP落地领域驱动设计-06.正确区分领域逻辑和应用逻辑

    目录 系列文章 领域逻辑和应用逻辑 多应用层 示例:正确区分应用逻辑和领域逻辑 学习帮助 系列文章 基于ABP落地领域驱动设计-00.目录和前言 基于ABP落地领域驱动设计-01.全景图 基于ABP落 ...

  2. 基于事件驱动的DDD领域驱动设计框架分享(附源代码)

    原文:基于事件驱动的DDD领域驱动设计框架分享(附源代码) 补充:现在再回过头来看这篇文章,感觉当初自己偏激了,呵呵.不过没有以前的我,怎么会有现在的我和现在的enode框架呢?发现自己进步了真好! ...

  3. 基于ABP落地领域驱动设计-00.目录和小结

    <实现领域驱动设计> -- 基于 ABP Framework 实现领域驱动设计实用指南 翻译缘由 自 ABP vNext 1.0 开始学习和使用该框架,被其优雅的设计和实现吸引,适逢 AB ...

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

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

  5. 基于ABP落地领域驱动设计-05.实体创建和更新最佳实践

    目录 系列文章 数据传输对象 输入DTO最佳实践 不要在输入DTO中定义不使用的属性 不要重用输入DTO 输入DTO中验证逻辑 输出DTO最佳实践 对象映射 学习帮助 系列文章 基于ABP落地领域驱动 ...

  6. 基于ABP落地领域驱动设计-02.聚合和聚合根的最佳实践和原则

    目录 前言 聚合 聚合和聚合根原则 包含业务原则 单个单元原则 事务边界原则 可序列化原则 聚合和聚合根最佳实践 只通过ID引用其他聚合 用于 EF Core 和 关系型数据库 保持聚合根足够小 聚合 ...

  7. 基于ABP实现DDD--领域服务、应用服务和DTO实践

      什么是领域服务呢?领域服务就是领域对象本身的服务,通常是通过多个聚合以实现单个聚合无法处理的逻辑. 一.领域服务实践 接下来将聚合根Issue中的AssignToAsync()方法[将问题分配给用 ...

  8. 基于ABP实现DDD--聚合和聚合根实践

      在下面的例子中涉及Repository.Issue.Label.User这4个聚合根,接下来以Issue聚合为例进行分析,其中Issue聚合是由Issue[聚合根].Comment[实体].Iss ...

  9. 基于ABP实现DDD--DDD相关概念

      什么是DDD呢?领域驱动设计[DDD]是一种针对复杂需求的软件开发方法.将软件实现与不断发展的模型联系起来,专注于核心领域逻辑,而不是基础设施细节.DDD适用于复杂领域和大规模应用,而不是简单的C ...

随机推荐

  1. muduo源码分析之回调模块

    这次我们主要来说说muduo库中大量使用的回调机制.muduo主要使用的是利用Callback的方式来实现回调,首先我们在自己的EchoServer构造函数中有这样几行代码 EchoServer(Ev ...

  2. SpringBoot从0到0.7——第二天

    SpringBoot从0到0.7--第二天 今天来搭建一个登陆界面进行跳转,不带有数据库,看似就很简单的一个界面,却让我学习了好长时间去了解学习他的原理,能看懂的话还是尽量去看源码. 首先创建一个项目 ...

  3. 这 BUG,绝了

    上周只上了三天班,但我也丝毫不敢懈怠,BUG 更是一个也没少写. 看着满屏幕的 ERROR,我陷入沉思.为什么我写的代如此烂,无法像大牛们写的那般优雅? 越想越自卑,越想越抑郁.我觉得这样不行,一定得 ...

  4. Oracle19c单实例数据库配置OGG单用户数据同步测试

    目录 19c单实例配置GoldenGate 并进行用户数据同步测试 一.数据库操作 1.开启数据库附加日志 2.开启数据库归档模式 3.开启goldengate同步 4.创建goldengate管理用 ...

  5. 机器学习实战-k近邻算法

    写在开头,打算耐心啃完机器学习实战这本书,所用版本为2013年6月第1版 在P19页的实施kNN算法时,有很多地方不懂,遂仔细研究,记录如下: 字典按值进行排序 首先仔细读完kNN算法之后,了解其是用 ...

  6. 实用git指令

    实用git指令 clone 操作目的 简要操作说明 详细操作说明 init & add & commit & push 操作目的 简要操作说明 详细操作说明 pull & ...

  7. 【ASP.NET Core】URL重写

    今天老周和大伙伴们聊聊有关 Url Rewrite 的事情,翻译过来就是 URL 重写. 这里不得不提一下,URL重定向与重写的不同. 1.URL重定向是客户端(通常是浏览器)向服务器请求地址A,然后 ...

  8. 992. Sort Array By Parity II - LeetCode

    Question 992. Sort Array By Parity II Solution 题目大意:给一个int数组,一半是奇数一半是偶数,分别对偶数数和奇数数排序并要求这个数本身是偶数要放在偶数 ...

  9. C#语言中的类型转换方法(unfinished)

    一.C#中的数据类型 1.数值类型 2.字符类型 3.字符串类型 4.布尔类型 5.枚举类型 6.Object类型 二.常见的类型转换 从转换方式的角度,类型转换分为隐式转换与显式转换两种. 其中,隐 ...

  10. sqlserver用windows方式验证登录踩过的坑

    坑位一: 之前没用过windows验证方式登录sqlserver,连接串怎么写 坑位二: 链接上了,但是启动报错 八月 19, 2020 9:33:43 上午 com.microsoft.sqlser ...