Кодекс EF 4.2 First и вопросы проектирования DDD - PullRequest
13 голосов
/ 19 декабря 2011

У меня есть несколько проблем при первой попытке разработки DDD с кодом EF 4.2 (или EF 4.1). Я провел несколько обширных исследований, но не нашел конкретных ответов на мои конкретные вопросы. Вот мои проблемы:

  1. Домен не может знать о постоянном уровне, или, другими словами, домен полностью отделен от EF. Однако для сохранения данных в базе данных каждый объект должен быть присоединен или добавлен в контекст EF. Я знаю, что вы должны использовать фабрики для создания экземпляров агрегатных корней, чтобы фабрика могла зарегистрировать созданную сущность в контексте EF. Это нарушает правила DDD, поскольку фабрика является частью домена, а не частью уровня персистентности. Как мне создать и зарегистрировать объекты, чтобы они правильно сохранялись в базе данных при необходимости?

  2. Должна ли составная сущность быть той, которая создает дочерние сущности? Я имею в виду, что если у меня Organization и Organization имеет коллекцию Employee сущностей, должен ли Organization иметь такой метод, как CreateEmployee или AddEmployee? Если нет, то при создании сущности Employee следует помнить, что совокупный корень Organization "владеет" каждой сущностью Employee.

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

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

Заранее спасибо!

Ответы [ 2 ]

24 голосов
/ 19 декабря 2011

О 1: я не очень хорошо знаком с EF, но использую подход отображения на основе кода / соглашения, я бы предположил, что не так уж и сложно отобразить POCO с помощью геттеров и сеттеров (даже с учетом того, что "DbContextс классом DbSet properties "в другом проекте не должно быть так сложно).Я бы не стал считать POCO совокупным корнем.Скорее они представляют «состояние в совокупности, которую вы хотите сохранить».Пример ниже:

// This is what gets persisted
public class TrainStationState {
  public Guid Id { get; set; }
  public string FullName { get; set; }
  public double Latitude { get; set; }
  public double Longitude { get; set; }

  // ... more state here
}

// This is what you work with
public class TrainStation : IExpose<TrainStationState> { 
  TrainStationState _state;

  public TrainStation(TrainStationState state) {
    _state = state;
    //You can also copy into member variables
    //the state that's required to make this
    //object work (think memento pattern).
    //Alternatively you could have a parameter-less
    //constructor and an explicit method
    //to restore/install state.
  }

  TrainStationState IExpose.GetState() {
    return _state;
    //Again, nothing stopping you from
    //assembling this "state object"
    //manually.
  }

  public void IncludeInRoute(TrainRoute route) {
    route.AddStation(_state.Id, _state.Latitude, _state.Longitude);
  }
}

Теперь, что касается жизненного цикла агрегата, есть два основных сценария:

  1. Создание нового агрегата: вы можете использовать фабрику, фабрикуметод, конструктор, конструктор, ... все, что соответствует вашим потребностям.Когда вам нужно сохранить агрегат, запросите его состояние и сохраните его (обычно этот код не находится в вашем домене и является достаточно общим).
  2. Получение существующего агрегата: вы можете использовать репозиторий,Дао, ... все, что соответствует вашим потребностям.Важно понимать, что то, что вы извлекаете из постоянного хранилища, - это состояние POCO, которое вам нужно вставить в первоначальный агрегат (или использовать его для заполнения своих частных членов).Все это происходит за фасадом хранилища / DAO.Не путайте ваши колл-сайты с таким общим поведением.

On 2: На ум приходит несколько вещей.Вот список:

  1. Совокупные корни являются границами согласованности.Какие требования в отношении согласованности вы видите между Организацией и Сотрудником?
  2. Организация МОЖЕТ действовать как фабрика Сотрудника, не изменяя состояния Организации.
  3. «Собственность» - это не то, что представляют собой агрегаты.
  4. Агрегатные корни обычно имеют методы, которые создают сущности внутри агрегата.Это имеет смысл, поскольку корни отвечают за обеспечение согласованности в совокупности.

Вкл. 3: Назначать идентификаторы извне, преодолевать их, двигаться дальше.Это, однако, не подразумевает их разоблачения (только в штате POCO).

2 голосов
/ 04 апреля 2013
  1. Основная проблема совместимости с EF-DDD заключается в том, как сохранить личные свойства.Решение, предложенное Ивом, в некоторых случаях кажется обходным решением для недостаточной мощности EF.Например, вы не можете сделать DDD с Fluent API, который требует, чтобы свойства состояния были общедоступными.Я обнаружил, что только сопоставление с файлами .edmx позволяет вам оставить доменные объекты чистыми.Это не заставляет вас делать вещи открытыми или добавлять какие-либо EF-зависимые атрибуты.

  2. Объекты всегда должны создаваться каким-либо совокупным корнем.Смотрите отличный пост Уди Дахана: http://www.udidahan.com/2009/06/29/dont-create-aggregate-roots/ Всегда загрузка некоторого агрегата и создание оттуда сущностей также решает проблему присоединения сущности к контексту EF.Вам не нужно ничего прикреплять вручную в этом случае.Он будет присоединен автоматически, потому что агрегат, загруженный из репозитория, уже подключен и имеет ссылку на новый объект.Хотя интерфейс репозитория принадлежит домену, реализация репозитория принадлежит инфраструктуре и знает об EF, контекстах, присоединении и т. Д.

  3. Я склонен рассматривать автоматически сгенерированные идентификаторы как подробности реализации постоянногоstore, это должно быть учтено сущностью домена, но не должно быть открыто.Таким образом, у меня есть свойство закрытого идентификатора, которое сопоставляется с автоматически сгенерированным столбцом, и некоторый другой, открытый идентификатор, который имеет значение для домена, например идентификатор удостоверения личности или номер паспорта для класса Person.Если таких значимых данных нет, я использую тип Guid, который имеет отличную возможность создания (почти) уникальных идентификаторов без необходимости обращения к базе данных.Поэтому в этом паттерне я использую эти Guid / MeaningfulID для загрузки агрегатов из хранилища, в то время как автоматически генерируемые идентификаторы используются базой данных для более быстрых соединений (Guid для этого не годится).

...