У меня есть следующая модель 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
В моем предыдущем вопросе были обнаружены некоторые полезные ссылки, которые, как я подозреваю, имеют отношение к вашему вопросу, в частности обсуждения Джимми Богарда ] Перечислимые классы .
Я нашел 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();
Это намного чище, и позволит вам добавить новые государства, если это необходимо.
В моем чемпионате слой домена должен быть отделен от модели БД/дизайна 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 на уровне персистентности. Там перечисление должно быть отображено на ключ таблицы состояний. Это можно сделать с помощью аспекта, чтобы сохранить оригинальную модель домена чистой.
Обычная практика - включать в перечисление элемент 'Unknown' со значением 0. Вы можете сделать это и использовать его для состояния New, если очень хотите.
Но то, что вы описываете, является бизнес-логикой... установка состояния после создания нового объекта должна происходить в слое бизнес-логики, а не в самом классе.
Вы хотите создать фабричный метод, который будет инстанцировать нужный класс состояния на основе хранимого значения.
что-то вроде
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.
Я лично думаю, что программировать против идентификаторов - это ошибка. Вместо этого я бы изменил вашу таблицу следующим образом:
**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) могут быть настроены заказчиком, т.е. они могут не хотеть, чтобы некоторые состояния были активны в системе, или они могут назвать их как-то иначе. Программируя против атрибутов, клиент может удалять / редактировать записи по своему усмотрению, и даже если этот процесс генерирует новые идентификаторы, это не влияет на систему, им просто нужно установить атрибуты.