本文次要介绍了通过构造函数和畛域服务创立实体2种形式,后者多用于在创立实体时须要其它业务规定检测的场景。最初介绍了在应用服务层中如何进行实体的更新操作。

一.通过构造函数创立实体

如果Issue的聚合根类为:

public class Issue : AggregateRoot<Guid>{    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<Issue, Guid> _issueRepository; //Issue仓储    private readonly IRepository<AppUser, Guid> _userRepository; //User仓储        // 私有构造函数    public IssueAppService(IssueManager issueManager, IRepository<Issue, Guid> issueRepository, IRepository<AppUser, Guid> userRepository)    {        _issueManager = issueManager;        _issueRepository = issueRepository;        _userRepository = userRepository;    }    // 通过构造函数创立Issue    public async Task<IssueDto> 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, IssueDto>(issue);}

二.通过畛域服务创立实体

  什么样的状况下会用畛域服务创立实体,而不是通过实体构造函数来创立实体呢?次要用在创立实体时须要其它业务规定检测的场景。比方,在创立Issue的时候,不能创立Title雷同的Issue。通过Issue实体构造函数来创立Issue实体,这个是管制不住的。所以才会有通过畛域服务创立实体的状况。
阻止从Issue的构造函数来创立Issue实体,须要将其构造函数的拜访权限由public批改为internal:

public class Issue : AggregateRoot<Guid>{    // ...    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<Issue,Guid> _issueRepository; // Issue的仓储        // 私有构造函数,注入仓储    public IssueManager(IRepository<Issue,Guid> issueRepository)    {        _issueRepository=issueRepository;    }        public async Task<Issue> 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<Issue,Guid> _issueRepository; //Issue的仓储    private readonly IRepository<AppUser,Guid> _userRepository; //User的仓储        // 公共的构造函数,注入所需的依赖    public IssueAppService(IssueManager issueManager, IRepository<Issue,Guid> issueRepository, IRepository<AppUser,Guid> userRepository){        _issueManager=issueManager;        _issueRepository=issueRepository;        _userRepository=userRepository;    }         // 创立一个Issue    public async Task<IssueDto> 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,IssueDto>(issue);    }}// 定义Issue的创立DTO为IssueCreationDtopublic 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<Issue,Guid> _issueRepository; //Issue仓储    private readonly IRepository<AppUser,Guid> _userRepository; //User仓储        // 私有构造函数,注入依赖    public IssueAppService(IssueManager issueManager, IRepository<Issue,Guid> issueRepository, IRepository<AppUser,Guid> userRepository){        _issueManager=issueManager;        _issueRepository=issueRepository;        _userRepository=userRepository;    }        // 更新Issue    public async Task<IssueDto> 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,IssueDto>(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)

本文由mdnice多平台公布