贫血模型与充血模型比较 - DDD - The Domain Driven Design

栏目: 后端 · 发布时间: 5年前

内容简介:在这篇文章中使用Vaughn Vernon的书[ IDDD,2013 ] 的例子来描述SCRUM模型的情景,并能够以实际的方式展示贫血模型和富模型的实现之间的区别。让我们说产品负责人:允许将每个积压项分配给Sprint。如果它已经分配给不同的Sprint,则必须先将其解除分配。分配完成后,通知相关方。

在这篇文章中使用Vaughn Vernon的书[ IDDD,2013 ] 的例子来描述SCRUM模型的情景,并能够以实际的方式展示贫血模型和富模型的实现之间的区别。

让我们说产品负责人:

允许将每个积压项分配给Sprint。如果它已经分配给不同的Sprint,则必须先将其解除分配。分配完成后,通知相关方。

一个非常简单的场景,可以在下面分层显示:

贫血模型与充血模型比较 - DDD - The Domain Driven Design

尽管在图中有表示,但我们将在示例中忽略Task。

贫血模型

领域/实体

<b>public</b> <b>class</b> Sprint
{
    <b>public</b> <b>int</b> Id { get; set; }
    <b>public</b> IList<BacklogItem> BacklogItems { get; set; }
    <b>public</b> SprintStatusEnum Status { get; set; }
    <b>public</b> string Description { get; set; }
    <b>public</b> DateTime BeginDate { get; set; }
    <b>public</b> DateTime EndDate { get; set; }
}

<b>public</b> <b>class</b> BacklogItem
{
    <b>public</b> <b>int</b> Id { get; set; }
    <b>public</b> IList<Task> Tasks { get; set; }
    <b>public</b> <b>int</b>? Spr<b>int</b>Id { get; set; }
    <b>public</b> <b>int</b>? UserId { get; set; }
    <b>public</b> BacklogItemStatusEnum Status { get; set; }
    <b>public</b> string Description { get; set; }
    <b>public</b> DateTime? BeginDate { get; set; }
    <b>public</b> DateTime? EndDate { get; set; }
}

领域/服务

<b>public</b> <b>class</b> SprintServices : ISprintServices
{
    <b>private</b> readonly ISprintRepository _sprintRepository;
    <b>private</b> readonly IBacklogItemRepository _backlogItemRepository;
    <b>public</b> SprintServices(ISprintRepository sprintRepository, IBacklogItemRepository backlogItemRepository)
    {
        _sprintRepository = sprintRepository;
        _backlogItemRepository = backlogItemRepository;
    }

    <b>public</b> <b>void</b> InsertBacklogItem(<b>int</b> spr<b>int</b>Id, <b>int</b> backLogItemId)
    {
        <b>var</b> sprint = _sprintRepository.GetById(sprintId);
        <b>var</b> backLogItem = _backlogItemRepository.GetById(backLogItemId);

        backLogItem.SprintId = sprintId;
        backLogItem.Status = BacklogItemStatusEnum.Committed;

        EmailService.SendMail(<font>"destination@email.com"</font><font>,
            $</font><font>"The backlog item '{backLogItem.Description}' was assigned to Sprint '{sprint.Description}'"</font><font>);

        _backlogItemRepository.Update(backLogItem);
    }
}
</font>

请注意,它们的实体没有业务逻辑,每个规则都依赖领域服务,实体属性设置没有任何控制,设置这些属性后没有验证,聚合不生成域事件。

这样的对象只是数据容器。

现在看看富血模型......

域/实体

<b>public</b> <b>class</b> Sprint : Entity
{
    <b>public</b> Sprint(string description, DateTime beginDate, DateTime endDate)
    {
        Status = SprintStatusEnum.New;
        Description = description;
        BeginDate = beginDate;
        EndDate = endDate;

        Validate();
    }

    <b>public</b> <b>int</b> Id { get; <b>private</b> set; }
    <b>public</b> IList<BacklogItem> BacklogItems { get; <b>private</b> set; }
    <b>public</b> SprintStatusEnum Status { get; <b>private</b> set; }
    <b>public</b> string Description { get; <b>private</b> set; }
    <b>public</b> DateTime BeginDate { get; <b>private</b> set; }
    <b>public</b> DateTime EndDate { get; <b>private</b> set; }

    <b>public</b> <b>void</b> SetStatus(SprintStatusEnum status) => Status = status;
    <b>public</b> <b>void</b> SetDescription(string description) => Description = description;
    <b>public</b> <b>void</b> SetBeginDate(DateTime beginDate) => BeginDate = beginDate;
    <b>public</b> <b>void</b> SetEndDate(DateTime endDate) => BeginDate = endDate;

    <b>public</b> <b>void</b> Validate()
    {
        <b>if</b> (string.IsNullOrEmpty(Description))
        {
            <b>throw</b> <b>new</b> Exception(<font>"Description can not be null"</font><font>);
        }

        <b>if</b> (BeginDate > EndDate)
        {
            <b>throw</b> <b>new</b> Exception(</font><font>"EndDate must be greater than BeginDate"</font><font>);
        }

        </font><font><i>//more rules...</i></font><font>
    }
}
<b>public</b> <b>class</b> BacklogItem : Entity
{
    <b>public</b> BacklogItem(string description)
    {
        Status = BacklogItemStatusEnum.New;
        Description = description;

        Validate();
    }

    <b>public</b> <b>int</b> Id { get; <b>private</b> set; }
    <b>public</b> IList<Task> Tasks { get; <b>private</b> set; }
    <b>public</b> <b>int</b>? Spr<b>int</b>Id { get; <b>private</b> set; }
    <b>public</b> <b>int</b>? UserId { get; <b>private</b> set; }
    <b>public</b> BacklogItemStatusEnum Status { get; <b>private</b> set; }
    <b>public</b> string Description { get; <b>private</b> set; }
    <b>public</b> DateTime? BeginDate { get; <b>private</b> set; }
    <b>public</b> DateTime? EndDate { get; <b>private</b> set; }

    <b>public</b> <b>void</b> SetSpr<b>int</b>Id(<b>int</b>? spr<b>int</b>Id) => Spr<b>int</b>Id = spr<b>int</b>Id;
    <b>public</b> <b>void</b> SetUserId(<b>int</b>? userId) => UserId = userId;
    <b>public</b> <b>void</b> SetStatusToNew() => Status = BacklogItemStatusEnum.New;
    <b>public</b> <b>void</b> SetStatusToCommitted() => Status = BacklogItemStatusEnum.Committed;
    <b>public</b> <b>void</b> SetStatusToApproved() => Status = BacklogItemStatusEnum.Approved;
    <b>public</b> <b>void</b> SetStatusToDone() => Status = BacklogItemStatusEnum.Done;
    <b>public</b> <b>void</b> SetDescription(string description) => Description = description;
    <b>public</b> <b>void</b> SetBeginDate(DateTime? beginDate) => BeginDate = beginDate;
    <b>public</b> <b>void</b> SetEndDate(DateTime? endDate) => BeginDate = endDate;

    <b>public</b> <b>void</b> CommitToSprint(Sprint sprint)
    {
        <b>if</b> (IsCommittedToSprint())
        {
            UncommitFromSprint();
        }

        SetStatusToCommitted();
        SetSprintId(sprint.Id);

        <b>this</b>.AddDomainEvent(<b>new</b> BacklogItemCommitted
        {
            Id = Id,
            SprintId = SprintId.Value
        });
    }
    <b>public</b> <b>void</b> UncommitFromSprint()
    {
        SprintId = <b>null</b>;

        <b>this</b>.AddDomainEvent(<b>new</b> BacklogItemUncommitFromSprint
        {
            Id = Id,
            SprintId = SprintId.Value
        });
    }
    <b>public</b> bool IsCommittedToSpr<b>int</b>() => Spr<b>int</b>Id != <b>null</b> && Spr<b>int</b>Id != <b>default</b>(<b>int</b>);

    <b>public</b> <b>void</b> Validate()
    {
        <b>if</b> (string.IsNullOrEmpty(Description))
        {
            <b>throw</b> <b>new</b> Exception(</font><font>"Description can not be null"</font><font>);
        }

        </font><font><i>//more rules...</i></font><font>
    }
}
</font>

应用:

<b>public</b> <b>class</b> BoardApplication : IBoardApplication
{
    <b>private</b> readonly ISprintRepository _sprintRepository;
    <b>private</b> readonly IBacklogItemRepository _backlogItemRepository;
    <b>public</b> BoardApplication(ISprintRepository sprintRepository, IBacklogItemRepository backlogItemRepository)
    {
        _sprintRepository = sprintRepository;
        _backlogItemRepository = backlogItemRepository;
    }

    <b>public</b> <b>void</b> ToAllocateBacklogItemToaSpr<b>int</b>(<b>int</b> spr<b>int</b>Id, <b>int</b> backLogItemId)
    {
        <b>var</b> sprint = _sprintRepository.GetById(sprintId);
        <b>var</b> backLogItem = _backlogItemRepository.GetById(backLogItemId);

        backLogItem.CommitToSprint(sprint);

        _backlogItemRepository.Update(backLogItem);
    }
}

你能看到区别么?

第一个例子使用了一种非常以数据为中心的方法,而不是行为方法。它不是一个真正的领域模型。

在我们的Rich Model示例中,我们使用表达泛在语言的域对象的行为。

它不会向客户端公开数据属性,而是公开一种行为,该行为明确且清楚地表明客户可以将Backlog项目分配给Sprint。

如果不将此丰富的行为插入Backlog项,则客户端必须处理事件,这是非常错误的。

在第二个例子中,好处要大得多。

现在,您能看到使用Rich Model的好处吗?


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

GitHub入门与实践

GitHub入门与实践

[日] 大塚弘记 / 支鹏浩、刘斌 / 人民邮电出版社 / 2015-7 / 39.00元

本书从Git的基本知识和操作方法入手,详细介绍了GitHub的各种功能,GitHub与其他工具或服务的协作,使用GitHub的开发流程以及如何将GitHub引入到企业中。在讲解GitHub的代表功能Pull Request时,本书专门搭建了供各位读者实践的仓库,邀请各位读者进行Pull Request并共同维护。一起来看看 《GitHub入门与实践》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具