我们把领域中的相关的业务写在Domain里,这样可以跟我们产品(领域专家)进行沟通的时候,可以确定我们的领域建模是否符合、是否缺少领域专家的某些业务逻辑,领域专家无需全部看懂我们的聚合根的方法,这就是和领域专家沟通的方式,我们有哪些业务一看就知道, 跟领域专家方便沟通,这就是领域驱动设计的重点
/// <summary>
/// 问题的领域
/// </summary>
public class Issue : BasicAggregateRoot<Guid>, IHasCreationTime
{
private Issue()
{
}
public Issue(Guid id, Guid repositoryId, string title, string? text = null, Guid? assignedUserId = null) : base(id)
{
RepositoryId = repositoryId;
SetTitle(title);
Text = text;
AssignedUserId = assignedUserId;
Comments = new List<Comments.Comment>();
IssueLabels = new List<IssueLabels.IssueLabel>();
}
public string Title { get; private set; }//需要验证,不允许直接修改
public string? Text { get; set; }//No validation
public bool IsLocked { get; private set; }//有业务逻辑的
public bool IsClosed { get; private set; }//Should change with CloseReason
public EnumCloseReason? CloseReason { get; private set; }//Should change with IsClosed
public DateTime? CloseDate { get; private set; }
/// <summary>
/// 仓库(领域)的Id
/// </summary>
public Guid RepositoryId { get; private set; }//不允许被修改
/// <summary>
/// 用户(领域)的Id
/// </summary>
public Guid? AssignedUserId { get; internal set; }//No validation
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreationTime { get; private set; }
/// <summary>
/// 最后评论的时间
/// </summary>
public DateTime? LastCommentTime { get; private set; }
/// <summary>
/// 问题的所有评论
/// </summary>
public ICollection<Comments.Comment> Comments { get; set; }
/// <summary>
/// 问题的所有标签id
/// </summary>
public ICollection<IssueLabels.IssueLabel> IssueLabels { get; set; }
/// <summary>
/// 添加评论
/// </summary>
public void AddComment(Guid id, Guid userId, string text)
{
Comments.Add(new Domain.Comments.Comment(id, text, userId));
LastCommentTime = DateTime.Now;
}
public void SetTitle(string title)
{
Title = Check.NotNullOrWhiteSpace(title, nameof(title));
}
/// <summary>
/// 关闭问题
/// </summary>
public void Close(EnumCloseReason reason)
{
IsClosed = true;
CloseReason = reason;
CloseDate = DateTime.UtcNow;
AddLocalEvent(new IssueCloseEvent { IssueId = Id });
}
/// <summary>
/// 重新打开
/// </summary>
public void ReOpen()
{
if (!IsClosed)
{
return;
}
if (IsLocked)
{
throw new UserFriendlyException("问题已经被锁定,无法重新打开");
}
IsClosed = false;
CloseReason = null;
CloseDate = null;
AddDistributedEvent(new IssueReOpenEto { IssueId = Id });
}
/// <summary>
/// 锁住问题
/// </summary>
public void Lock()
{
if (!IsClosed)
{
throw new UserFriendlyException("无法锁定已打开的问题");
}
IsLocked = true;
}
/// <summary>
/// 解锁问题
/// </summary>
public void UnLock()
{
IsLocked = false;
}
/// <summary>
/// 是否非活动
/// </summary>
/// <returns></returns>
public bool IsInActive()
{
//var daysAgo30 = DateTime.Now.Subtract(TimeSpan.FromDays(30));
//return
// //Open
// !IsClosed &&
// //Assigned to nobody
// AssignedUserId == null &&
// //Created 30+ days ago
// CreationTime < daysAgo30 &&
// //No comment or the last comment was 30+ days ago
// (LastCommentTime == null || LastCommentTime < daysAgo30);
return new InActiveIssueSpecification().IsSatisfiedBy(this);
}
}
public enum EnumCloseReason : byte
{
Normal = 1,
}
首先,先贴代码,Issue是一个聚合根,也就是一个领域,包括了Comment 评论子域和IssueLabel 标签关联的子域
我们有一些业务,我们定义为方法,Close关闭问题、ReOpen重新打开问题,Lock锁定问题、UnLock解锁问题,这些方法的命名都表示了某个业务
/// <summary>
/// 评论实体
/// </summary>
public class Comment : Entity<Guid>, IHasCreationTime
{
private Comment() { }
public Comment(Guid id, string text,Guid userId) : base(id)
{
Text = text;
UserId = userId;
}
public string Text { get; set; }
public DateTime CreationTime { get; set; }
/// <summary>
/// 问题的Id
/// </summary>
public Guid IssueId { get; set; }
/// <summary>
/// 用户(领域)的Id
/// </summary>
public Guid UserId { get; set; }
}
public class IssueLabel : IEntity
{
/// <summary>
/// 问题的Id
/// </summary>
public Guid IssueId { get; set; }
/// <summary>
/// 标签(领域)的Id
/// </summary>
public Guid LabelId { get; set; }
public object[] GetKeys()
{
return new object[] { IssueId, LabelId };
}
}
Close关闭问题业务,我们需要把IsClosed改为true,给关闭原因赋值、给关闭时间赋值,所以不允许外部直接单独修改,只需要调用我们的方法即可,所以他们都是private的
ReOpen 重新打开业务,如果不是关闭的问题return,如果是锁住的问题,我们抛出异常
AddComment 添加评论,我们直接使用Issue问题的实例调用AddComment即可添加我们的评论,也就是子域是通过我们Issue聚合根操作,不直接对外进行操作
CreationTime 由框架管理
Title我们需要验证是否为空,长度等,不能直接修改
IsLocked是跟业务逻辑有关的,不能直接修改 private的
其他的以此类推
很简单吧