DDD инварианты бизнес-правила и проверка

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

Я хотел бы знать, каков рекомендуемый способ проверки того, что свойства не являются нулевыми, в определенном диапазоне или длине и т. д... Я видел несколько способов использования IsValid () и других обсуждений о применении в конструкторе, чтобы сущность никогда не находилась в недопустимом состоянии или использование предварительной и постобработки, а также других с использованием api FluentValidation, как инварианты влияют на DRY и SRP.

может кто-нибудь дать мне хороший пример того, где ставить такие проверки, при использовании службы приложений, ограниченного контекста, службы домена, агрегатного корня, наслоения сущностей. К чему это приведет и каков наилучший подход?

спасибо.

2 ответов


при моделировании объекта домена лучше всего учитывать реальные последствия. Предположим, вы имеете дело с Employee сущности.

сотрудникам нужно имя

мы знаем, что в реальном мире у сотрудника всегда должно быть имя. Невозможно, чтобы у сотрудника не было имени. Другими словами, нельзя "сконструировать" сотрудника без указания его имени. Итак, используйте параметризованные конструкторы! Мы также знаем, что имя сотрудника не может измениться - поэтому мы предотвращаем это даже путем создания частного сеттера. Использование системы типов .NET для проверки Вашего сотрудника является очень сильной формой проверки.

public string Name { get; private set; }

public Employee(string name)
{
    Name = name;
}

допустимые имена имеют некоторые правила

теперь это начинает становиться интересным. У имени есть определенные правила. Давайте просто возьмем упрощенный маршрут и предположим, что допустимое имя не является нулевым или пустым. В приведенном выше примере кода, следующее бизнес-правило не против. На данный момент мы все еще можем создавать недействительных сотрудников! Давайте предотвратим это, изменив наш сеттер:

public string Name
{
    get
    {
        return name;
    }
    private set
    {
        if (String.IsNullOrWhiteSpace(value))
        {
            throw new ArgumentOutOfRangeException("value", "Employee name cannot be an empty value");
        }

        name = value;
    }
}

лично я предпочитаю иметь эту логику в частном сеттере, чем в конструкторе. Сеттер не совсем невидим. Сама сущность все еще может изменить ее, и нам нужно обеспечить ее действительность. Кроме того, всегда есть исключения!

как насчет разоблачения какой-то формы IsValid() метод?

бери выше Employee сущности. Где и как IsValid() метод работы?

вы бы позволили создать недопустимого сотрудника, а затем ожидать, что разработчик проверит его действительность с помощью IsValid() проверить? Это слабый дизайн-прежде чем вы это узнаете, безымянные сотрудники будут курсировать вокруг вашей системы, вызывая хаос.

но, возможно, вы хотели бы подвергнуть проверке имени логика?

мы не хотим ловить исключения для потока управления. Исключение составляют катастрофические сбои системы. Мы также не хотим дублировать эти правила проверки нашего кода. Итак, возможно, разоблачение этой логики проверки не такая уж плохая идея (но все же не самая большая!).

то, что вы могли бы сделать, это обеспечить статический IsValidName(string) способ:

public static bool IsValidName(string name)
{
    return (String.IsNullOrWhiteSpace(value))
}

наша собственность теперь несколько изменится:

public string Name
{
    get
    {
        return name;
    }
    private set
    {
        if (!Employee.IsValidName(value))
        {
            throw new ArgumentOutOfRangeException("value", "Employee name cannot be an empty value");
        }

        name = value;
    }
}

но в этом дизайне есть что-то подозрительное...

теперь мы начинаем создавать методы проверки отдельных свойств нашей сущности. Если свойство имеет все виды правил и поведения, связанных с ним, возможно, это знак того, что мы можем создать объект value для него!

public PersonName : IEquatable<PersonName>
{
    public string Name
    {
        get
        {
            return name;
        }
        private set
        {
            if (!PersonName.IsValid(value))
            {
                throw new ArgumentOutOfRangeException("value", "Person name cannot be an empty value");
            }

            name = value;
        }
    }

    private PersonName(string name)
    {
        Name = name;
    }

    public static PersonName From(string name)
    {
        return new PersonName(name);
    }

    public static bool IsValid(string name)
    {
        return !String.IsNullOrWhiteSpace(value);
    }

    // Don't forget to override .Equals
}

теперь наш!--7--> сущность может быть упрощена (я исключил проверку нулевой ссылки):

public Employee
{
    public PersonName Name { get; private set; }

    public Employee(PersonName name)
    {
        Name = name;
    }
}

наш клиент код может выглядеть примерно так это:

if(PersonName.IsValid(name))
{
    employee = new Employee(PersonName.From(name));
}
else
{
    // Send a validation message to the user or something
}

так что мы здесь сделали?

мы обеспечили что наша модель домена всегда последовательна. Исключительно важный. Недопустимая сущность не может быть создана. Кроме того, мы использовали объекты стоимости для обеспечения дальнейшего "богатства". PersonName дал код клиента больше контроля и больше власти, а также упрощенный Employee.


Я построил библиотеку, которая может вам помочь.

https://github.com/mersocarlin/ddd-validation