围绕DDDABP Framework两个核心技术,后面还会陆续发布核心构件实现综合案例实现系列文章,敬请关注! ABP Framework 研习社(QQ群:726299208) ABP Framework 学习及实施DDD经验分享;示例源码、电子书共享,欢迎加入!

领域服务

领域服务实现领域逻辑,它:

•依赖于服务仓储。•需要多个聚合,以实现单个聚合无法处理的逻辑。

领域服务与领域对象一起使用,其方法可以获取和返回实体值对象、原始类型等。然而,它并不获取/返回DTOs,DTOs属于应用层。

示例:将问题分配给用户

回想一下,我们之前是如何实现将问题分配给用户的

public class Issue:AggregateRoot<Guid>
{//..//问题关联的用户IDpublic Guid? AssignedUserId{get;private set;}//分配方法public async Task AssignToAsync(AppUser user,IUserIssueService userIssueService){var openIssueCount = await userIssueService.GetOpenIssueCountAsync(user.Id);if(openIssueCount >=3 ){throw new BusinessException("IssueTracking:CanNotOpenLockedIssue");}AssignedUserId=user.Id;}public void CleanAssignment(){AssignedUserId=null;}
}

现在,我们将逻辑迁移到领域服务中。首先,修改 Issue 类:

public class Issue:AggregateRoot<Guid>
{//...public Guid? AssignedUserId{get;internal set;}
}

•在聚合中移除 AssignToAsync 方法(因为需要在对应的领域服务中实现该方法。)•将 AssignedUserId 属性设置器从私有改为内部internal,以允许从领域服务中设置它。

接下来,创建一个领域服务 IssueManager 定义方法 AssignToAsync 将指定 Issue 分配给指定用户。

public class IssueManager:DomainService
{private readonly IRepository<Issue,Guid> _issueRepository;public IssueManager(IRepository<Issue,Guid> issueRepository){_issueRepository=issueRepository;}public async Task AssignToAsync(Issue issue,AppUser user){//获取关联用户处于打开状态问题的数量var openIssueCount=await _issueRepository.CountAsync(i=>i.AssingedUserId==user.Id && !i.IsClosed);//超过3个,则抛出异常if(openIssueCount>=3){throw new BusinessException("IssueTracking:ConcurrentOpenIssueLimit");}issue.AssignedUserId=user.Id;}
}

IssueManager在构造函数中注入需要的仓储,用于查询分配给用户处于打开状态的Issue。

建议使用Manager后缀命名来命名领域服务。

这种设计的唯一问题是:Issue.AssignedUserId现在是 public ,可以在任何外部类中设置。然而,它不应该是公共的,访问范围应该是程序集内部internal,只有在同一个程序集(IssueTracking.Domain)项目中才可以调用。

这个例子的解决方案就是如此,我们认为这很合理:

•领域层开发者在使用 IssueManager 时,已经熟知领域规则。•应用层开发者强制使用 IssueManager,因此无法直接修改实体。

以上我们展示了将问题分配给用户的两种实现方式,两种方式权衡之下,我们更加推荐当业务逻辑需要与外部服务协同工作时,创建领域服务

如果没有一个充分的理由,我们认为没有必要去为领域服务创建接口,比如:为 IssueManager 创建 IIssueManger 接口。

应用服务

应用服务是无状态服务,实现应用程序用例。一个应用服务通常使用领域对象实现用例,获取或返回数据传输对象DTOs,被展示层调用。

应用服务通用原则:

•实现特定用例的应用逻辑,不能在应用服务中实现领域逻辑(需要理清应用逻辑和领域逻辑二者的区别)。•应用服务方法不能返回实体,因为这样会打破领域层的封装性,始终只返回DTO。

示例:分配问题给用户

using System;
using System.Threading.Tasks;
using IssueTracking.Users;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;namespace IssueTracking.Issues
{public class IssueAppService :ApplicationService.IIssueAppService{private readonly IssueManager _issueManager;private readonly IRepository<Issue,Guid> _issueRepository;private readonly IRepository<AppUser,Guid> _userRepository;public IssueAppService(IssueManager issueManager,IRepository<Issue,Guid> issueRepository,IRepository<AppUser,Guid> userRepository){_issueManager=issueManager;_issueRepository=issueRepository;_userRepository=userRepository;}[Authorize]public async Task AssignAsync(IssueAssignDto input){var issue=await _issueRepository.GetAsync(input.IssueId);var user=await _userRepository.GetAsync(inpu.UserId);await _issueManager.AssignToAsync(issue,user);await _issueRepository.UpdateAsync(issue);//没有对issue做任何修改,为什么要更新?在IssueManager中进行了状态修改。}}
}

一个应用服务方法通常有三个步骤:

•从数据库获取关联的领域对象•使用领域对象(领域服务、实体等)执行业务逻辑•在数据库中更新实体(如果已修改)

当时使用EF Core时,最后的 Update 更新操作并不是必须的,应为有 状态变更跟踪。但是建议显式调用,适配其他数据库提供程序。

示例中 IssueAssignDto 是一个简单的 DTO 类:

using System;
namespace IssueTracking.Issues
{public class IssueAssignDto{public Guid IssueId{get;set;}public Guid UserId{get;set;}}
}

学习帮助

围绕DDDABP Framework两个核心技术,后面还会陆续发布核心构件实现综合案例实现系列文章,敬请关注!

ABP Framework 研习社(QQ群:726299208) 专注 ABP Framework 学习及DDD实施经验分享;示例源码、电子书共享,欢迎加入!

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

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

    前言 上一篇 基于ABP落地领域驱动设计-01.全景图 概述了DDD理论和对应的解决方案.项目组成.项目引用关系,以及基于ABP落地DDD的通用原则.从这本篇开始,会更加深入地介绍在基于 ABP Fr ...

  2. 基于花季A传媒ABP落地领域驱动设计-02.聚合和聚合根的最佳实践和原则

    ​ DDD和ABP Framework两个中心技术,后边还会陆续发布中心构件完成.归纳事例完成系列文章,敬请关注! ABP Framework 学习及实施DDD经历分享:示例源码.电子书同享,欢迎加入 ...

  3. ddd领域驱动设计_领域驱动设计(DDD)理论启示

    过去几年通天塔一直处于快速的业务能力建设和架构完善的阶段,以应对不断增长的业务需求和容量.高可用等技术需求,现在通天塔平台已经能满足集团主站的大部分活动.频道搭建和运营能力,主流程的新需求越来越少,个 ...

  4. [.NET领域驱动设计实战系列]专题二:结合领域驱动设计的面向服务架构来搭建网上书店...

    原文:[.NET领域驱动设计实战系列]专题二:结合领域驱动设计的面向服务架构来搭建网上书店 一.前言 在前面专题一中,我已经介绍了我写这系列文章的初衷了.由于dax.net中的DDD框架和Bytear ...

  5. 领域驱动设计在马蜂窝优惠中心重构中的实践

    前言 正如领域驱动设计之父 Eric Evans 所著一书的书名所述,领域驱动设计(Domain Driven Design)是一种软件核心复杂性应对之道. 在我们解决现实业务问题时,会面对非常复杂的 ...

  6. 领域驱动设计在爱奇艺打赏业务的实践

    领域驱动设计(Domain-Driven Design,以下简称DDD)思潮的形成要追述到30几年前,17年前,Eirc Evans定义了领域驱动设计的概念.DDD一直为传统行业的软件工程师提供软件设 ...

  7. Eric Evans谈领域驱动设计、微服务与边界

    在今年于伦敦举办的DDD Exchange大会的主题演讲中,Eric Evans表达了他对微服务的看法.尽管微服务这个词现在已经有点炒作的味道,但Evans相信微服务确实蕴含着巨大的价值,它为我们带来 ...

  8. DDD领域驱动设计之领域基础设施层

    1.DDD领域驱动设计实践篇之如何提取模型 2.DDD领域驱动设计之聚合.实体.值对象 其实这里说的基础设施层只是领域层的一些接口和基类而已,没有其他的如日子工具等代码,仅仅是为了说明领域层的一些基础 ...

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

    系列文章 基于ABP落地领域驱动设计-01.全景图 基于ABP落地领域驱动设计-02.聚合和聚合根的最佳实践和原则 基于ABP落地领域驱动设计-03.仓储和规约最佳实践和原则 基于ABP落地领域驱动设 ...

最新文章

  1. c# 对文件的各种操作
  2. Excel:Excel使用技巧经验总结之(利用Excel自带功能统计各个字段不同类别及其个数并进行图表可视化+非编程实现)图文教程之详细攻略
  3. 前端学习(159):meta
  4. python怎么处理数据标注_在python中将数据标记为敏感
  5. 《专家速成手册》 做专家只需记住6点
  6. Python绘制三次贝塞尔曲线
  7. html在p中加空格,HTML基础 p 多个空格和换行都算一个空格
  8. Python----chardet模块的使用方法
  9. 如何查看网页字体大小
  10. CoordinatorLayout布局和自定义Behavior
  11. 2017年PHP培训机构排名
  12. android手机只有三星采用实体键,三星新配件:你终于可以体面地使用实体全键盘了...
  13. 如何在PPT中嵌入交互式图表?LightningChart助力炫酷展示
  14. DOS环境进入及基本命令DOS
  15. Android 安卓动画 属性动画 - 缩放动画
  16. 留学生日常英语46~50
  17. Shell知识点(一)基本语法
  18. 懒惰的苏珊 UVa1620
  19. Missing Tag Identification in COTS RFID Systems: Bridging the Gap between Theory and Practice 翻译
  20. unknown target connected的解决方法

热门文章

  1. 敏捷开发组织【北京及其他地区QQ群】【长三角QQ群】【珠三角QQ群】
  2. windows2003——IIS
  3. RHCE课程-初级部分6、编辑工具VIM,网络配置,进程优先,日志文件简介。
  4. 突发奇想:flash+.Net+数据库的一种构思
  5. SiteMapCreator 发布 (Open Source)
  6. 记录一次webpack3升级到webpack4过程
  7. FastCGI中文规范
  8. Hbase笔记4 java操作Hbase
  9. python3-day4(装饰器)
  10. 构造不可变类及其优点