如何在DDD方法中建立关联模型?

时间:2016-05-26 19:31:02

标签: domain-driven-design aggregate aggregateroot

我通过阅读Eric Evans和Vaughn Vernon的书籍逐步学习DDD方法与虚构的业务领域,我尝试在我的项目中使用PHP实现它(但它在这里并不重要)。

最近,我一直在阅读应该由域定义的模型的大量聚合聚合根实体模式。而且,坦率地说,我不确定我是否理解所有定义,所以我决定在这里提出我的问题。

首先,我想提交我的(子)域名,负责员工的工作。假期管理应该使我的问题的答案更容易。

最简单的情况是Employee可以在许多Teams中找到。当员工决定休息几天时,他必须发送HolidaysRequest元数据,如节假日类型(如休息假期,休息几天以照顾孩子等),接受状态和课程时间范围,他不会出现在他的办公室。请HolidaysRequest注意Employee已发送HolidaysRequest的{​​{1}}。我还希望找到HolidaysRequest发送的所有Employee

我非常确定DateRangeHolidayType之类的内容是纯粹的ValueObjects。这对我来说很清楚。当我必须定义实体的边界时,问题就开始了。我可能通过在实体中嵌套对象来定义关联的不良做法,所以请告诉我在这里找出责任的定义。

  1. 这里的实体是什么?什么应该是Aggregate以及AggregateRoot的位置?
  2. 如何定义实体之间的关联?例如。 Employee可以属于多个TeamHolidaysRequestEmployee创作,并分配给可以接受它的另一个Employee。它们应该作为聚合实现吗?
  3. 为什么我要问这些问题?因为几个星期前我在这里发布了一个问题,其中一个答案是考虑EmployeeTeams之间的关系,它们应该在名为EmployeeInTeam的单个Aggreate中。我不确定我是否以正确的方式理解它。

    感谢您的任何建议。

1 个答案:

答案 0 :(得分:0)

关于DDD的主要问题是将重点放在域中,这就是为什么它被称为Domain Driven Design。

当您开始询问关系,聚合和实体时,甚至没有深入探索您的域的内容,您实际上是在寻找数据库建模而不是域。

拜托,我不是说你提出错误的问题,也不是在批评他们,我认为你在学习期间尝试实践时根本没有错。

我不是DDD专家,我和你一样学习,但我会尽力帮助。

首先想一想 Holydays Management 可能出现的情况。当你对某些事情有不同的规则时,你可以先使用策略(我说这是最终解决方案)。

建立一个美好而有意义的域名,非常困难(至少对我而言)。你写代码。测试一下。有见解,抛出你的代码并重写它。重构它。在您的软件生命周期中,您应该关注域名,因此您应该始终在改进它。

从编码开始(如域名草稿),看看它的样子。让我们来锻炼吧。首先,为什么我们需要管理这些东西?我们试图解决什么问题?啊,有时候员工会请几天假,我们想控制它。我们可能会批准或不批准,这取决于他们想要的原因" holyday",以及我们的团队状态如何。如果我们拒绝并且他们仍然回家,我们将迟到决定我们是开薪还是打折。执行无处不在的语言,让我们在代码中表达这个问题:

public interface IHolydayStrategy
{
    bool CanTakeDaysOff(HolydayRequest request);
}

public class TakeCareOfChildren : IHolydayStrategy
{
    public bool CanTakeDaysOff(HolydayRequest request)
    {
        return IsTotalDaysRequestedUnderLimit(request.Range.TotalDays());
    }

    public bool IsTotalDaysRequestedUnderLimit(int totalDays)
    {
        return totalDays < 3;
    }
}

public class InjuredEmployee : IHolydayStrategy
{
    public bool CanTakeDaysOff(HolydayRequest request)
    {
        return true;
    }
}

public class NeedsToRelax : IHolydayStrategy
{
    public bool CanTakeDaysOff(HolydayRequest request)
    {
        return IsCurrentPercentageOfWorkingEmployeesAcceptable(request.TeamRealSize, request.WorkingEmployees)
            || AreProjectsWithinDeadline(request.Projects);
    }

    private bool AreProjectsWithinDeadline(IEnumerable<Project> projects)
    {
        return !projects.Any(p => p.IsDeadlineExceeded());
    }

    private bool IsCurrentPercentageOfWorkingEmployeesAcceptable(int teamRealSize, int workingEmployees)
    {
        return workingEmployees / teamRealSize > 0.7d;
    }
}

public class Project
{
    public bool IsDeadlineExceeded()
    {
        throw new NotImplementedException();
    }
}

public class DateRange
{
    public DateTime Start { get; set; }
    public DateTime End { get; set; }

    public int TotalDays()
    {
        return End.Subtract(Start).Days;
    }

    public bool IsBetween(DateTime date)
    {
        return date > Start && date < End;
    }
}

public enum HolydayTypes
{
    TakeCareOfChildren,
    NeedToRelax,
    BankOfHours,
    Injured,
    NeedToVisitDoctor,
    WannaVisitDisney
}

public class HolydayRequest
{
    public IEnumerable<Project> Projects { get; internal set; }
    public DateRange Range { get; set; }
    public HolydayTypes Reason { get; set; }
    public int TeamRealSize { get; internal set; }
    public int WorkingEmployees { get; internal set; }
}

以下是我如何快速写下这个:

  • 根据情况和情况,可以给予或不给予瞻礼日 原因,让我们创建一个IHolydayStrategy
  • 创建了一个空(无属性HolydayRequest类。
  • 为了每个可能的原因,让我们创建一个不同的策略。
  • 如果理由是照顾孩子,他们可以休息几天 总天数请求不受限制。
  • 如果原因是因为员工受伤了,我们没有 除了允许请求之外的其他选择。
  • 如果原因是因为他们需要放松,我们会检查是否有 工作人员的可接受百分比,或项目是否在内 最后期限。
  • 一旦我需要策略中的一些数据,我就使用了CTRL + .HolydayRequest
  • 中自动创建属性

看看我怎么也不知道这些东西将如何存储/映射?我只是编写代码来解决问题,并获得解决问题所需的一些信息。

显然这不是最终领域,只是一个草案。我可能会带走这些代码,如果需要,可以重新编写代码。

人们可能认为创建一个InjuredEmployee类只是为了总是返回true是没用的,但这里的重点是利用无所不在的语言来制作一个尽可能明确,任何人都会阅读并理解同样的事情:&#34; 嗯,如果我们有一名受伤的员工,他们总是被允许休假,无论团队的情况如何,有多少他们需要的日子。&#34;。 DDD中这个概念解决的问题之一是开发人员,产品所有者,领域专家和其他参与者之间对术语和规则的误解。

在此之后,我会开始用模拟数据编写一些测试。我可能会重构代码。

这&#34; 3&#34;:

    public bool IsTotalDaysRequestedUnderLimit(int totalDays)
    {
        return totalDays < 3;
    }

这个&#34; 0.7d&#34;:

    private bool IsCurrentPercentageOfWorkingEmployeesAcceptable(int teamRealSize, int workingEmployees)
    {
        return workingEmployees / teamRealSize > 0.7d;
    }

是规范,在我看来,它不应该存在于策略中。我们可能会应用规范模式来解决问题。

通过测试后,我们得到了一个合理的初始解决方案,现在让我们考虑如何存储它。我们可能会在这里使用最终定义的类(例如Team,Project,Employee)来映射ORM。

一旦您开始编写域名,您的实体之间就会出现关系,这就是为什么我通常不关心ORM将如何保留我的域名,什么是Aggregate。

看看我是如何创建一个Employee类的,尽管听起来非常重要。这就是为什么我们不应该从创建实体及其属性开始,因为它与创建表和字段完全相同。

你的DDD变成了数据库驱动设计那样,我们不想要这个。当然,最终我们会成为员工,但让我们一步一步,只在您需要时创建。不要试图立即开始建模所有内容,预测您将需要的所有实体。把重点放在你的问题上,以及如何解决它。

关于您的问题,什么是实体以及什么是汇总,我认为您不会问它们的定义,而是考虑您的域名,员工是否被视为一个或另一个。一旦您的域名开始被您的代码显示,您最终将自己回答。当您开始开发 Application Laye r时,您将会知道它,该应负责加载数据并委派给您的域。我的域逻辑期望什么数据,从哪里开始查询。

我希望我能帮助别人。