基于ABP落地领域驱动设计-06.正确区分领域逻辑和应用逻辑
系列文章
基于ABP落地领域驱动设计-01.全景图
基于ABP落地领域驱动设计-02.聚合和聚合根的最佳实践和原则
基于ABP落地领域驱动设计-03.仓储和规约最佳实践和原则
基于ABP落地领域驱动设计-04.领域服务和应用服务的最佳实践和原则
基于ABP落地领域驱动设计-05.实体创建和更新最佳实践
围绕DDD和ABP Framework两个核心技术,后面还会陆续发布核心构件实现、综合案例实现系列文章,敬请关注! ABP Framework 研习社(QQ群:726299208) ABP Framework 学习及实施DDD经验分享;示例源码、电子书共享,欢迎加入!
领域逻辑和应用逻辑
正如前面提到的,领域驱动设计中的业务逻辑拆分为两部分:领域逻辑和应用逻辑。
领域逻辑由系统的核心领域规则组成,而应用程序逻辑实现特定于应用程序的用例。
虽然定义很清楚,但实现可能并不容易,常常无法决定哪些代码应该放在应用层,哪些代码应该放在领域层。本节试图解释这些差异。
多应用层
当你的系统很大时,DDD有助于处理复杂性问题。特别是,在一个领域中开发多个应用,那么领域逻辑与应用逻辑的分离就变得更加重要。
假设你正在构建一个有多个应用程序的系统:
•一个Web应用程序,使用 ASP .NET Core MVC,展示产品给用户。浏览产品,不需要进行身份验证;只有当用户执行某些操作时,比如向购物车中添加产品,才会要求登陆。•一个后台管理应用程序,使用Angular UI+ REST APIs构建。此应用由公司办公人员做系统管理,比如:编辑产品描述。•一个移动应用程序,和 Web应用程序 相比UI更加简单,通过REST APIs或其他技术(如:TCP/Socket)与服务器通信。
每一个应用都需要解决不同的需求,实现不同用例(应用服务方法),不同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
示例:正确区分应用逻辑和领域逻辑
本节包含一些应用服务和领域服务示例,讨论如何决定在这些服务中放置业务逻辑。
示例:在领域服务中创建 Organization (组织)
public class OrganizationManager:DomainService
{private readonly IRepository<Organization> _organizationRepository;private readonly ICurrentUser _currentUser;private readonly IAuthorizationService _authorizationService;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;}
}
让我们一步一步来分析 CreateAsync
方法中的代码是否都应该放在领域服务中:
•正确:组织名重复检测,存在重复名称则抛出异常。该检测与核心领域规则相关,不允许重名。•错误:领域服务不应该进行权限验证。权限验证应该放在应用层。•错误:记录日志包含当前用户的用户名。领域服务不应该依赖当前用户,当前用户(Session)应该是展示层或应用层中的相关概念。•错误:创建新组织发送邮件,我们仍然认为这是业务逻辑。可以能会根据用例来创建不同类型邮件。
示例:在应用层创建新组织
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...}
}
让我们看看 CreateAsync 方法,一步一步讨论其中的代码是否应该放在应用服务:
•正确:应用服务方法应该是工作单元,ABP框架工作单元系统自动实现,可以不用添加[UnitOfWork]
特性。•正确:权限验证应该放在应用层,可以使用 [Authorize]
特性。•正确:在我们的业务逻辑中创建组织是付费服务,当前操作调用基础设施服务进行支付操作。•正确:应用服务方法负责保存变更到数据库。•正确:给系统管理员发送邮件通知。•错误:不能返回实体,应该返回DTO。
讨论:为什么我们不应该将支付逻辑放在领域服务中?
你可能想知道为什么付款代码不在 OrganizationManager
里面。这是一件很重要的事情,我们绝不希望付款出错。
然而,业务的重要性并不意味着要将其视为核心业务逻辑。我们可能有其他的支付用例,在这些用例中,我们不收取费用来创建一个新的组织。
例如:
•后台办公系统中的管理员用户可以创建新组织,不用考虑支付。•系统数据导入、整合、同步,也可能需要在没有任何支付操作的情况下,创建组织。
如您所见,支付不是创建一个有效组织的必要操作。它是一个特定于用例的应用逻辑。
示例:CRUD操作
public class IssueAppService
{private readonly IssueManager _issueManager;public IssueAppService(IssueManager issueManager){_issueManager=issueManager;}public async Task<IssueDto> GetAsync(Guid id){return await _issueManager.GetAsync(id);}public async Task CreateAsync(IssueCreationDto input){await _issueManager.CreateAsync(input);}public async Task UpdateAsync(UpdateIssueDto input){await _issueManager.UpdateAsync(input);}public async Task DeleteAsync(Guid id){await _issueManager.DeleteAsync(id);}
}
应用服务并没有做任何事情,而是委托给领域服务来处理。只接收DTO参数,并传递给 IssueManger
。
•不要创建只实现简单 CRUD 操作的领域服务方法,而不带任何领域逻辑。•不要传递 DTO 给领域服务,或领域服务方法返回 DTO。
应用服务可以直接使用仓储,实现查询、创建、更新或删除数据,除非执行这些操作时需要处理领域逻辑,这种情况下,创建领域服务方法,但只针对那些真正需要的方法。
不要因为将来可能会需要这些CRUD领域服务方法,就去提前创建这些方法! 当需要时再去创建,并重构现有的代码。由于抽象了应用层,重构领域层不会影响到UI层和其他客户端。
学习帮助
围绕DDD和ABP Framework两个核心技术,后面还会陆续发布核心构件实现、综合案例实现系列文章,敬请关注!
ABP Framework 研习社(QQ群:726299208) 专注 ABP Framework 学习及DDD实施经验分享;示例源码、电子书共享,欢迎加入!
基于ABP落地领域驱动设计-06.正确区分领域逻辑和应用逻辑相关推荐
- 领域驱动设计战术模式:领域服务
领域驱动设计战术部分,是一组面向业务的设计模式,是基于技术的一种思维方式,相对开发人员来说更接地气,是提升个人格局比较好的切入点. 该文章为战术模式的第四篇,重心讲解领域服务模式. 在建模时,有时会遇 ...
- 领域驱动设计战术模式:领域事件
领域驱动设计战术部分,是一组面向业务的设计模式,是基于技术的一种思维方式,相对开发人员来说更接地气,是提升个人格局比较好的切入点. 该文章为战术模式的第五篇,重心讲解领域事件模式. 在建模时,有时会遇 ...
- 领域驱动设计之领域模型_领域驱动的设计思维
领域驱动设计之领域模型 Size matters, when it comes to your companies' digital presence. The bigger you become, ...
- 《领域驱动设计》:从领域视角深入仓储的设计和实现
一 前言 " DDD设计的目标是关注领域模型而并非技术来创建更好的软件,假设开发人员构建了一个SQL,并将它传递给基础设施层中的某个查询服务然后根据表数据的结构集取出所需信息,最后将这些信息 ...
- 领域驱动设计 pdf_什么是领域驱动设计?
什么是领域驱动设计? 你可能使用领域驱动设计(DDD)开发了一些项目.你可能很满意, 使用领域模型来开发领域业务.并且得意地展示给你的同事看,他们会说"666". 但有的时候你使用 ...
- 阿里资深架构师推荐 -- 如果正确的开启领域驱动设计
简介: 什么是领域驱动设计?传统分层架构在实际开发中存在哪些问题?业务开发人员如何设计并搭建自己的领域模型?阿里文娱技术专家战獒将为大家一一解答,并分享文娱在领域驱动设计上的实践. 一 什么是领域驱动 ...
- 谈谈微服务架构中的领域驱动设计
谈谈微服务架构中的领域驱动设计 https://mp.weixin.qq.com/s/43HSud6ijdVzPA_wdLrxzQ 谈谈微服务架构中的领域驱动设计 本文是关于领域驱动设计与微服务架构结 ...
- 领域驱动设计在讲什么
概述 概念可以简单描述某类事物,这类事物可以是实体也可以是问题.领域驱动设计是为了管理系统复杂性问题而生的一套方法论. 随着业务系统的复杂性不断提高,系统的性能和灵活性要求也会越来越高,如何构建一个扩 ...
- 深入理解领域驱动设计中的聚合
简介:聚合模式是 DDD 的模式结构中较为难于理解的一个,也是 DDD 学习曲线中的一个关键障碍.合理地设计聚合,能清晰地表述业务一致性,也更容易带来清晰的实现,设计不合理的聚合,甚至在设计中没有聚合 ...
最新文章
- php 调用系统命令 执行外部程序
- 数据对象类代码的生成小工具
- wxWidgets:VScroll示例
- Debian 7.1.0 安装教程图解(——Debian系统轻量级快速安装法)
- SpringMVC的XML配置解析
- Android+clipse导入工程提示:invalid project description
- 《 .NET软件设计新思维》一书作者MSDN课程日程
- RDS数据订阅服务使用说明
- 数组及引用类型内存分配
- raid卡组不同raid_Linux 软件阵列与低端硬件阵列卡性能对比
- Java岗史上最全八股文面试真题汇总,堪称2022年面试天花板
- 未来的计算机100字的小短文,关于电脑作文100字
- 2020Android不死我不倒,作为一个程序员,你觉得最大的悲哀是什么
- 计算机组成与体系结构——计算机系统概述、数据表示方法
- 吴洪声十问易名CEO金小刚:域名还有没有投资价值?
- 发声 | 再访《Scratch少儿趣味编程》系列图书作者阿部和广访谈问题有奖征集...
- 使用HM NIS Edit制作软件安装包
- 分享一个网站,英文名称随机生成,随机生成英文名称
- 非线性微分方程的平均法
- 云原生之docker详细知识技能介绍与实战
热门文章
- [导入]【翻译】WF从入门到精通(第八章):调用外部方法及工作流
- 浅谈ASP中Web页面间的数据传递
- 空间数据索引RTree完全解析及Java实现
- python拓展7(Celery消息队列配置定时任务)
- MyEclipse小问题与汉字处理
- iOS UIViewContentMode 使用详解
- (转)程序员的十层楼11层(上帝)
- 使用PowerShell配置Microsoft Teams
- 棉花糖多少钱_如何在6.0棉花糖及更高版本中访问Android的正在运行的应用程序列表...
- java 中的chartdata_获取Helm Charts中的文件夹列表