DDD: перечисление как сущности - PullRequest
12 голосов
/ 10 февраля 2010

У меня есть следующая модель БД:

**Person table**
ID    |    Name    | StateId
------------------------------
1          Joe       1
2          Peter     1
3          John      2

**State table**
ID    |    Desc
------------------------------
1          Working
2          Vacation

и модель предметной области будет (упрощенной):

public class Person
{
    public int Id { get; }
    public string Name { get; set; }
    public State State { get; set; }
}

public class State
{
    private int id;
    public string Name { get; set; }
}

Состояние может быть использовано в доменной логике, например ::1007*

if(person.State == State.Working)
    // some logic

Итак, насколько я понимаю, State действует как объект значения, который используется для проверки логики домена. Но он также должен присутствовать в модели БД для представления чистого ERM.

Таким образом, состояние может быть расширено до:

public class State
{
    private int id;
    public string Name { get; set; }

    public static State New {get {return new State([hardCodedIdHere?], [hardCodeNameHere?]);}}
}

Но при таком подходе название государства будет жестко закодировано в домене.

Вы знаете, что я имею в виду? Есть ли стандартный подход для такой вещи? С моей точки зрения, я пытаюсь использовать объект (который сохраняется с точки зрения разработки ERM) как своего рода объект ценности в моем домене. Что ты думаешь?

Обновление вопроса: Возможно, мой вопрос не был достаточно ясен.

Что мне нужно знать, так это то, как я буду использовать сущность (например, пример состояния), которая хранится в базе данных в рамках логики моего домена. Чтобы избежать таких вещей, как:

  if(person.State.Id == State.Working.Id)
      // some logic

или

if(person.State.Id == WORKING_ID)
// some logic

Ответы [ 6 ]

8 голосов
/ 12 февраля 2010

A мой предыдущий вопрос раскрыл некоторые полезные ссылки, которые, как я подозреваю, имеют отношение к вашему вопросу, в частности дискуссии Джимми Богарда о классах перечисления .

7 голосов
/ 10 февраля 2010

Ваша предложенная структура выглядит хорошо. (Терминологическое отступление: поскольку у State есть идентификатор, это не объект значения , а скорее сущность .)

Перечисления - это запах кода, поэтому не пытайтесь идти по этому пути. Гораздо более объектно-ориентировано перемещать поведение в объект State с использованием шаблона State .

Вместо того, чтобы писать

if (person.State == State.Working)
    // do something...

по всему вашему коду, это позволит вам написать

person.State.DoSomething();

Это намного чище и позволит вам добавлять новые состояния в случае необходимости.

3 голосов
/ 10 февраля 2010

Обычной практикой является включение в перечисление элемента «Неизвестно» со значением 0. Вы можете сделать это и использовать его для состояния New, если вы действительно этого хотите.

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

2 голосов
/ 10 февраля 2010

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

что-то вроде

public  static State GetStateByID( StateEnum value)
{
   if(value.Invalid)
        throw new Exception();

switch(value)
   case State.Working
        return new WorkingState();
   case State.somethingelse
         return new somethingelseState();
   case State.something
         return new somethingState();
   case State.whatever
         return new whateverState();

}

При использовании перечислений всегда пытайтесь использовать 0 как недопустимый. Под капотом перечисление является типом значения, а неназначенный int всегда равен 0.

Обычно используется фабрика, такая как эта, в сочетании с шаблоном состояния.

Таким образом, когда вы читаете сохраненное целочисленное значение из базы данных, вы можете привести int к enum и вызвать фабрику с ним, чтобы получить соответствующий объект State.

1 голос
/ 09 марта 2010

Лично я считаю, что программировать против идентификаторов - ошибка. Вместо этого я бы изменил вашу таблицу следующим образом:

   **State table**
ID    |    Desc               | IsWorking  | IsVacation
-----------------------------------------------------------
1          Working                True         False
2          Vacation               False        True

Я бы затем использовал эти атрибуты для принятия бизнес-решений, таких как:

    public void MakeDecisionOnState(State state)
    {
        if (state.IsVacation)
            DoSomething();
        if (state.IsWorking)
            DoSomethingElse();
    }

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

    public abstract class State
    {
        public Guid Id { get; set; }

        public string Description { get; set; }

        public abstract void DoSomething();

    }

    public class WorkingState : State
    {
        public override void DoSomething()
        {
            //Do something specific for the working state
        }
    }

    public class VacationState : State
    {
        public override void DoSomething()
        {
            //Do something specific for the vacation state
        } 
    }

    public class StateFactory
    {
        public static State CreateState(IDataRecord record)
        {
            if (record.GetBoolean(2))
                return new WorkingState { Id = record.GetGuid(0), Description = record.GetString(1) };
            if (record.GetBoolean(3))
                return new VacationState { Id = record.GetGuid(0), Description = record.GetString(1) };

            throw new Exception("Data is screwed");
        }
    }

Теперь вы удалили оператор if / switch, и ваш код может быть просто:

state.DoSomething();

Причина, по которой я это делаю, заключается в том, что часто эти типы сущностей (или более вероятные объекты значений в DDD) могут настраиваться заказчиком, то есть они могут не хотеть иметь некоторые активные состояния в системе или может пожелать назвать их чем-то еще. Программируя атрибуты, клиент может удалять / редактировать записи по своему усмотрению, и даже если этот процесс генерирует новые идентификаторы, это не влияет на систему, ему просто нужно установить атрибуты.

0 голосов
/ 10 февраля 2010

По моему мнению, доменный слой должен быть отделен от модели DB / ERM. У меня были проблемы с пониманием вашего окончательного предложения для государственного класса. ИМХО, это не очень хорошая вещь для установления общего языка, который является одной из основных целей DDD.

Я бы пошел за более простой дизайн. Государство относится к классу Person. Я бы включил его в класс.

public class Person
{
    public int Id { get; }
    public string Name { get; set; }
    public PersonState State { get; set; }
}

Кажется, что само состояние имеет определенные значения (я полагаю, что человек является сотрудником в вашем контексте), которые меняются не часто. Поэтому я бы смоделировал его как enum и обработал бы его как тип данных.

enum Days {Working, Vacation};

Это простой для понимания дизайн на мой взгляд. Отображение в дизайн ERM принадлежит IMHO на уровне персистентности. Там перечисление должно быть сопоставлено с ключом таблицы состояний. Это можно сделать с помощью аспекта, чтобы сохранить исходную модель домена в чистоте.

...