Подтвердить что ты не робот

DDD: перечислять как объекты

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

**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; }
}

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

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

Итак, из моего понимания, государство действует как объект ценности, который используется для проверки логики домена. Но он также должен присутствовать в модели БД для представления чистой 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
4b9b3361

Ответ 2

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

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

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

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

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

person.State.DoSomething();

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

Ответ 3

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

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

Ответ 4

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

что-то вроде

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 как Invalid. Под капотом перечисление представляет собой тип значения, а неназначенный int всегда равен 0.

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

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

Ответ 5

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

   **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();
    }

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

    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) могут быть сконфигурированы клиентом, то есть они могут не хотеть, чтобы некоторые из состояний были активными в системе, или они может пожелать назвать их чем-то другим. Путем программирования против атрибутов клиент может удалять/редактировать записи по своему усмотрению, и даже если этот процесс генерирует новый идентификатор, он не влияет на систему, им просто нужно установить атрибуты.

Ответ 6

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

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

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

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

enum Days {Working, Vacation};

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