AbpVnext中的DDD指南之聚合根

我们把领域中的相关的业务写在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的

其他的以此类推

很简单吧

相关推荐

  1. AbpVnextDDD指南聚合

    2024-06-15 14:14:01       10 阅读
  2. html元素以及元素字体含义

    2024-06-15 14:14:01       32 阅读
  3. DDD如何识别子域、实体、值对象和聚合

    2024-06-15 14:14:01       20 阅读
  4. mysql数据聚合

    2024-06-15 14:14:01       5 阅读
  5. MongoDB 聚合查询在数据统计应用

    2024-06-15 14:14:01       31 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-15 14:14:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-15 14:14:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-15 14:14:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-15 14:14:01       20 阅读

热门阅读

  1. grep命令知多少

    2024-06-15 14:14:01       6 阅读
  2. prometheus relabel_configs 标签重写

    2024-06-15 14:14:01       7 阅读
  3. openresty/openresty离线镜像安装包

    2024-06-15 14:14:01       6 阅读
  4. 二、OSI七层模型和TCP,IP模型基本概念

    2024-06-15 14:14:01       7 阅读
  5. 消费全返如何盈利?新零售分销营销模式解析

    2024-06-15 14:14:01       8 阅读