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

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

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

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

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

Но с помощью этого подхода название состояния было бы hardcoded в домен.

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

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

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

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

или

if(person.State.Id == WORKING_ID)
// some logic
12
задан Chris 10 February 2010 в 15:56
поделиться

6 ответов

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

8
ответ дан 2 December 2019 в 07:21
поделиться

Я нашел fancybox хорошей альтернативой.

-121--4998158-

Используйте SQLite. Затем можно запросить определенные данные и сохранить локальный файл. К вашему сведению - предложение PDO автоматически добавляет отдельные предложения вокруг значения.

$Filename = "MyDB.db";

try {
    $SQLHandle = new PDO("sqlite:".$Filename);
}
catch(PDOException $e) {
    echo $e->getMessage()." :: ".$Filename;
}

$SQLHandle->exec("CREATE TABLE IF NOT EXISTS MyTable (ID INTEGER PRIMARY KEY, MyColumn TEXT)");

$SQLHandle->beginTransaction();

$SQLHandle->exec("INSERT INTO MyTable (MyColumn) VALUES (".$SQLHandle->quote("MyValue").")");
$SQLHandle->exec("INSERT INTO MyTable (MyColumn) VALUES (".$SQLHandle->quote("MyValue 2").")");

$SQLHandle->commit();

$Iterator = $SQLHandle->query("SELECT * FROM MyTable ORDER BY MyColumn ASC");

unset($SQLHandle);

foreach($Iterator as $Row) {
    echo $Row["MyColumn"]."\n";
}
-121--3439009-

Предлагаемая структура выглядит хорошо. (Разбор терминологии: поскольку State имеет идентификатор, это не Value Object , а Entity .)

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

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

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

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

person.State.DoSomething();

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

7
ответ дан 2 December 2019 в 07:21
поделиться

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

Я бы выбрал более простую конструкцию. Государство принадлежит классу "Человек". Я бы включил его в класс.

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

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

enum Days {Working, Vacation};

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

0
ответ дан 2 December 2019 в 07:21
поделиться

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

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

3
ответ дан 2 December 2019 в 07:21
поделиться

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

что-то вроде

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.

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

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

2
ответ дан 2 December 2019 в 07:21
поделиться

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

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

1
ответ дан 2 December 2019 в 07:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: