如何在DDD中建模继承?

13

我目前正在尝试DDD并阅读Evans的书。我已经创建了一个聚合根为学生(Student)的模型。现在我需要有(或能够区分)一个注册学生(RegisteredStudent)和一个已入学学生(EnrolledStudent)(继承于注册学生)。我不知道如何在DDD中处理继承。

  1. 这两个继承类应该放在聚合内部吗?如果是,它们也被视为聚合根吗,因为它们的身份与根相同(只是增加了属性)? 如果不是,我该如何使其他实体可以访问它们?

  2. 或者我不应该使用继承吗?为什么?

  3. 还有,如果您在聚合中有一个实体不是根,但需要继承外部的实体,该怎么办?应该如何操作?

1个回答

15

在这里,你需要问自己一个问题,即RegisteredStudentEnrolledStudent是否是不同的概念。它们不都是学生,只是处于不同的状态吗?

总的来说,应该优先使用组合而不是继承。

以下是我会做的一个例子。(请注意,这只是我的示例,我不知道领域,因此不是确定性解决方案)。

你可以有一个Student类,它是聚合根,然后有几个不同的状态类:RegisteredEnrolled。这样,你就不需要在学生上公开这些状态类,而是只需在学生上公开方法。以下是C#代码的一个简单示例:

class Student
{
    State _currentState;
    void Enroll()
    {
        if(!_currentState is Registered)
            throw new InvalidOperationException("Cannot enroll student who is not registered");

        this._currentState = new Enrolled();
    }

    void Register(string name)
    {
        this._currentState = new Registered(name);
    }
}

class StudentState{}

class Enrolled : StudentState
{}

class Registered : StudentState
{
    public Registered(string name)
    {
        Name = name;
    }
    public string Name {get; private set;}
}

这是状态设计模式的一个简单应用,你可以将更多部分外部化并构建完整的状态机,但我会把这留给你自己。 (此外,它直接键入SO编辑器,因此可能会有语法错误)

评论后编辑

是否需要公开 State 属性取决于上下文。一般而言,我建议不要这样做,因为这样会暴露出 Student 的内部细节。最好公开一个名为 CanEnroll 的方法。这样你就可以更改内部实现而不影响任何客户端。

至于问题3,没有使用案例很难说。但是,以下是一些指导原则:

  • 优先选择组合而非继承(再次说明,我知道)。
  • 您可以从聚合内部引用外部世界,但不应反过来引用。

嗨 Kenneth:感谢您指引我这个模式。我可能需要公开一个State属性或GetState方法来从客户端检查,对吧?顺便说一下:我认为您的私有State变量应该是StudentState。谢谢! - g_b
此外,您对问题3有什么想法?这是允许的吗?举个例子,我有一个有界上下文 HumanResource 和另一个 Enrollment。HumanResource 有一个 Employee 实体,而 Enrollment 有 Adviser 和 Instructor 实体。我可以继承跨 BCs(Adviser 和 Instructor 继承 Employee)吗? - g_b
再次感谢你,肯尼斯!非常感激! - g_b

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接