Как работать с наследованием на DDD

в настоящее время я пробую DDD и читаю книгу Эванса. Я пришел к модели, которая имеет совокупность, корнем которой является Student. Теперь мне нужно иметь (или уметь различать) RegisteredStudent и EnrolledStudent (наследует RegisteredStudent). Я не знаю, как обрабатывать наследование в DDD.

  1. Если 2 наследуемых классах быть в совокупности? Если да, то они также считаются агрегатными корнями, поскольку их идентичность совпадает с корнем (существуют только добавленные свойства к ним)? Если нет, то как я могу предоставить доступ к ним от других сущностей?

  2. или я не должен использовать наследование? Почему?

  3. а также, что, если у вас есть сущность в совокупности, которая не является корнем, но вам нужно, чтобы она унаследовала сущность снаружи? Как вы должны это сделать?

1 ответов


здесь вам нужно спросить себя, являются ли RegisteredStudent и EnrolledStudent разными понятиями. Разве они оба не студенты, а просто находятся в другом состоянии?

В общем, вы должны отдать предпочтение композиции над наследованием.

вот пример того, что я буду делать. (Обратите внимание, что это только мой пример, я не знаю домен, поэтому это не окончательное решение).

у вас может быть класс Student, который является вашим совокупным корнем, а затем несколько различных государственных классов:Registered и Enrolled. Таким образом, вам не нужно выставлять эти классы состояния на студента, но вы можете просто выставить методы на студента. Маленький пример (на 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-editor, поэтому могут быть синтаксические ошибки)

правка после комментарии:

нужно ли выставлять свойство состояния или нет, зависит от контекста. В общем, я бы рекомендовал этого не делать, потому что вы обнажаете внутренности ученика. Было бы лучше выставить метод под названием CanEnroll например. Таким образом, вы можете изменить внутреннюю реализацию шаблона состояния, не затрагивая никаких клиентов.

что касается вопроса 3, трудно сказать без прецедента. Тем не менее, вот некоторые рекомендации:

  • предпочтение композиции над наследованием (опять же, я знаю)
  • у вас может быть ссылка изнутри агрегата на внешний мир, но у вас не должно быть ссылки наоборот.