??本文主要介绍了通过构造函数和领域服务创建实体2种方式,后者多用于在创建实体时需要其它业务规则检测的场景。最后介绍了在应用服务层中如何进行实体的更新操作。
一.通过构造函数创建实体
假如Issue的聚合根类为:
public class Issue : AggregateRoot
{
public Guid RepositoryId { get;
private set;
} //不能修改RepositoryId,原因是不支持把一个Issue移动到另外一个Repository下面
public string Title { get;
private set;
} //不能直接修改Title,可以通过SetTitle()修改,主要是在该方法中要加入对Title不能重复的校验
public string Text { get;
set;
} //可以直接修改
public Guid? AssignedUserId { get;
internal set;
} //同一个程序集中是可以修改AssignedUserId的// 公有构造函数
public Issue(Guid id, Guid repositoryId, string title, string text=null) : base(id)
{
RepositoryId = repositoryId;
Title = Check.NotNullOrWhiteSpace(title, nameof(title));
Text = text;
//可为空或者null
}// 私有构造函数
private Issue() {}// 修改Title的方法
public void SetTitle(string title)
{
// Title不能重复
Title = Check.NotNullOrWhiteSpace(title, nameof(title));
}
// ...
}
在应用服务层创建一个Issue的过程如下:
public class IssueAppService : ApplicationService.IIssueAppService
{
private readonly IssueManager _issueManager;
//Issue领域服务
private readonly IRepository _issueRepository;
//Issue仓储
private readonly IRepository _userRepository;
//User仓储// 公有构造函数
public IssueAppService(IssueManager issueManager, IRepository issueRepository, IRepository userRepository)
{
_issueManager = issueManager;
_issueRepository = issueRepository;
_userRepository = userRepository;
}// 通过构造函数创建Issue
public async Task CreateAssync(IssueCreationDto input)
{
var issue = new Issue(GuidGenerator.Create(), input.RepositoryId, input.Title, input.Text);
}if(input.AssigneeId.HasValue)
{
// 获取分配给Issue的User
var user = await _userRepository.GetAsync(input.AssigneeId.Value);
// 通过Issue的领域服务,将Issue分配给User
await _issueManager.AssignAsync(issue, user);
}// 插入和更新Issue
await _issueRepository.InsertAsync(issue);
// 返回IssueDto
return ObjectMapper.Map(issue);
}
二.通过领域服务创建实体 ??什么样的情况下会用领域服务创建实体,而不是通过实体构造函数来创建实体呢?主要用在创建实体时需要其它业务规则检测的场景。比如,在创建Issue的时候,不能创建Title相同的Issue。通过Issue实体构造函数来创建Issue实体,这个是控制不住的。所以才会有通过领域服务创建实体的情况。
阻止从Issue的构造函数来创建Issue实体,需要将其构造函数的访问权限由public修改为internal:
public class Issue : AggregateRoot
{
// ...internal Issue(Guid id, Guid repositoryId, string title, string text = null) : base(id)
{
RepositoryId = repositoryId;
Title = Check.NotNullOrEmpty(title, nameof(title));
Text = text;
//允许为空或者null
}// ...
}
通过领域服务IssueManager中的CreateAsync()方法来判断创建的Issue的Title是否重复:
public class IssueManager:DomainService
{
private readonly IRepository _issueRepository;
// Issue的仓储// 公有构造函数,注入仓储
public IssueManager(IRepository issueRepository)
{
_issueRepository=issueRepository;
}public async Task CreateAsync(Guid repositoryId, string title, string text=null)
{
// 判断Issue的Title是否重复
if(await _issueRepository.AnyAsync(i=>i.Title==title))
{
throw new BusinessException("IssueTracking:IssueWithSameTitleExists");
}
// 返回创建的Issue实体
return new Issue(GuidGenerator.Create(), repositoryId, title, text);
}
}
在应用服务层IssueAppService中通过IssueManager.CreateAsync()创建实体如下:
public class IssueAppService :ApplicationService.IIssueAppService
{
private readonly IssueManager _issueManager;
//Issue的领域服务
private readonly IRepository _issueRepository;
//Issue的仓储
private readonly IRepository _userRepository;
//User的仓储// 公共的构造函数,注入所需的依赖
public IssueAppService(IssueManager issueManager, IRepository issueRepository, IRepository userRepository){
_issueManager=issueManager;
_issueRepository=issueRepository;
_userRepository=userRepository;
} // 创建一个Issue
public async Task CreateAsync(IssueCreationDto input)
{
// 通过领域服务的_issueManager.CreateAsync()创建实体,主要是保证Title不重复
var issue=await _issueManager.CreateAsync(input.RepositoryId, input.Title, input.Text);
// 获取User,并将Issue分配给User
if(input.AssignedUserId.HasValue)
{
var user =await _userRepository.GetAsync(input.AssignedUserId.Value);
await _issueManager.AssignToAsynce(issue,user);
}
// 插入和更新数据库
await _issueRepository.InsertAsync(issue);
// 返回IssueDto
return ObjectMapper.Map(issue);
}
}// 定义Issue的创建DTO为IssueCreationDto
public class IssueCreationDto
{
public Guid RepositoryId{get;
set;
}
[Required]
public string Title {get;
set;
}
public Guid? AssignedUserId{get;
set;
}
public string Text {get;
set;
}
}
现在有个疑问是为什么不把Title的重复检测放在领域服务层中来做呢,这就涉及一个区分核心领域逻辑还是应用逻辑的问题了。显然这里Title不能重复属于核心领域逻辑,所以放在了领域服务中来处理。为什么标题重复检测不在应?服务中实现?详细的解释参考[1]。
三.实体的更新操作 接下来介绍在应用层IssueAppService中来update实体。定义UpdateIssueDto如下:
public class UpdateIssueDto
{
[Required]
public string Title {get;
set;
}
public string Text{get;
set;
}
public Guid? AssignedUserId{get;
set;
}
}
实体更新操作的UpdateAsync()方法如下所示:
public class IssueAppService :ApplicationService.IIssueAppService
{
private readonly IssueManager _issueManager;
//Issue领域服务
private readonly IRepository _issueRepository;
//Issue仓储
private readonly IRepository _userRepository;
//User仓储// 公有构造函数,注入依赖
public IssueAppService(IssueManager issueManager, IRepository issueRepository, IRepository userRepository){
_issueManager=issueManager;
_issueRepository=issueRepository;
_userRepository=userRepository;
}// 更新Issue
public async Task UpdateAsync(Guid id, UpdateIssueDto input)
{
// 从Issue仓储中获取Issue实体
var issue = await _issueRepository.GetAsync(id);
// 通过领域服务的issueManager.ChangeTitleAsync()方法更新Issue的标题
await _issueManager.ChangeTitleAsync(issue,input.Title);
// 获取User,并将Issue分配给User
if(input.AssignedUserId.HasValue)
{
var user = await _userRepository.GetAsync(input.AssignedUserId.Value);
await _issueManager.AssignToAsync(issue, user);
}
issue.Text=input.Text;
// 更新和保存Issue
// 保存实体更改是应用服务方法的职责
await _issueRepository.UpdateAsync(issue);
// 返回IssueDto
return ObjectMapper.Map(issue);
}
}
需要在IssueManager中添加ChangeTitle():
public async Task ChangeTitleAsync(Issue issue,string title)
{
// Title不变就返回
if(issue.Title==title)
{
return;
}
// Title重复就抛出异常
if(await _issueRepository.AnyAsync(i=>i.Title==title))
{
throw new BusinessException("IssueTracking:IssueWithSameTitleExists");
}
// 请它情况更新Title
issue.SetTitle(title);
}
修改Issue类中SetTitle()方法的访问权限为internal:
internal void SetTitle(string title)
{
Title=Check.NotNullOrWhiteSpace(title,nameof(title));
}
参考文献:
[1]基于ABP Framework实现领域驱动设计:https://url39.ctfile.com/f/25... (访问密码: 2096)
【基于ABP实现DDD--实体创建和更新】本文由mdnice多平台发布
推荐阅读
- WinDbg实践--入门篇
- Java 入门练习(36 - 40)
- 考阿里云ACA难吗(要做什么准备?)
- 【附下载】漏洞扫描工具AppScan安装及功能简单使用
- IT技术|2019 年软件开发人员必学的编程语言 Top 3
- Java|介绍了Java中的异常
- 技术干货系列|【详细教程】一文参透MongoDB聚合查询
- 技术干货系列|【超详细】手把手教你搭建MongoDB集群搭建
- 技术干货系列|Seata聚合 AT、TCC、SAGA 、 XA事务模式打造一站式的分布式事务解决方案