Имея модель отдельного домена и модель сохранения в DDD
Я читал о дизайне, управляемом доменом, и о том, как его реализовать, при использовании первого подхода кода для создания базы данных. Из того, что я прочитал и исследовал, есть два мнения по этому вопросу:
-
Имейте 1 класс, который служит как модель домена, так и модель сохранения.
-
У вас есть 2 разных класса, один из которых реализует логику домена, а другой - для подхода, основанного на кодах
Теперь я знаю мнение 1), как говорят, упрощает небольшие решения, которые не имеют много различий между моделями домена и персистентности, но я думаю, что это нарушает принцип единой ответственности и тем самым вводит множество проблем, когда соглашения ORM мешают DDD.
Что для меня неожиданно, есть многочисленные примеры кода, как реализовать мнение 1). Но a не нашел ни одного примера того, как реализовать мнение 2) и как сопоставить 2 объекта. (Наверное, есть такие примеры, но я не смог найти С# один)
Итак, я попытался реализовать пример самостоятельно, но я не уверен, что это хороший способ сделать это.
Скажем, у меня есть система билетов и билеты имеют срок действия. Моя модель домена будет выглядеть так:
/// <summary>
/// Domain Model
/// </summary>
public class TicketEntity
{
public int Id { get; private set; }
public decimal Cost { get; private set; }
public DateTime ExpiryDate { get; private set; }
public TicketEntity(int id, decimal cost, DateTime expiryDate)
{
this.Id = id;
this.Cost = cost;
this.ExpiryDate = expiryDate;
}
public bool IsTicketExpired()
{
if (DateTime.Now > this.ExpiryDate)
{
return true;
}
else
{
return false;
}
}
}
Модель персистентности с использованием Entity Framework как ORM будет выглядеть почти так же, но по мере роста решения это может быть не так.
/// <summary>
/// ORM code first Persistence Model
/// </summary>
public class Ticket
{
[Key]
public int Id { get; set; }
public decimal Cost { get; set; }
public DateTime ExpiryDate { get; set; }
}
Все выглядит великолепно. Теперь я не уверен, что это лучшее место для получения модели сохранения Ticket
из репозитория и как сопоставить ее с моделью TicketEntity
Я сделал это на уровне приложения/службы.
public class ApplicationService
{
private ITicketsRepository ticketsRepository;
public ApplicationService(ITicketsRepository ticketsRepository)
{
this.ticketsRepository = ticketsRepository;
}
public bool IsTicketExpired(int ticketId)
{
Ticket persistanceModel = this.ticketsRepository.GetById(ticketId);
TicketEntity domainModel = new TicketEntity(
persistanceModel.Id,
persistanceModel.Cost,
persistanceModel.ExpiryDate);
return domainModel.IsTicketExpired();
}
}
Мои вопросы:
-
Есть ли какие-либо соображения по соображениям 1) предпочли бы мнение 2), кроме ускорения разработки и повторного использования кода.
-
Есть ли какие-либо проблемы в моем подходе к отображению моделей? Есть ли что-то, что я пропустил, что вызовет проблемы, когда решение будет расти?
Ответы
Ответ 1
Есть ли какие-либо соображения по соображениям 1) предпочли бы мнение 2), кроме ускорения разработки и повторного использования кода.
Вариант 1 - это просто из-за чистой лени и воображаемой увеличенной скорости развития. Верно, что эти приложения быстрее получат версию 1.0. Но когда эти разработчики достигают версии 3.0 приложения, они не считают, что это так весело поддерживать приложение из-за всех компромиссов, которые они должны были делать в модели домена из-за устройства ORM.
Есть ли какие-либо проблемы в моем подходе к отображению моделей? Есть ли что-то, что я пропустил, что вызовет проблемы, когда решение будет расти?
Да. Репозиторий должен нести ответственность за скрытие механизма сохранения. Он API должен работать только с объектами домена, а не с сущностями.
Репозиторий отвечает за выполнение преобразований в/из объектов домена (чтобы их можно было сохранить). Метод извлечения обычно использует ADO.NET или ORM, например Entity Framework, для загрузки объекта/объекта базы данных. Затем преобразуйте его в правильный бизнес-объект и, наконец, верните его.
В противном случае вы должны заставить каждую службу иметь знания о сохранении и работать с вашей моделью домена, тем самым имея две обязанности.
Если вы работаете с сервисами приложений в определении DDD, вам, вероятно, захочется взглянуть на шаблон разделения команд/запросов, который может быть заменой приложений. Код становится чище, и вы также получаете гораздо более легкий API-интерфейс для вашей модели домена.
Ответ 2
В этом году я столкнулся с этой дилеммой, и мне пришлось принять важное решение, это было на большом и амбициозном проекте. Я хотел бы поговорить об этой теме в течение нескольких часов, но я верну свои мысли для вас:
1) Персистенция и модель домена как одно и то же
Если вы находитесь в новом проекте с базой данных, созданной с нуля, я бы предположительно предложил этот вариант. Да, домен и ваши знания об этом будут постоянно меняться, и это потребует рефакторинга, который повлияет на вашу базу данных, но я думаю, что в большинстве случаев это того стоит.
С помощью Entity Framework в качестве ORM вы можете почти поддерживать ваши модели домена полностью свободными от проблем с ORM, используя плавные сопоставления.
Хорошие детали:
- Быстро, легко, красиво (если база данных предназначена для этой проблемы)
Плохие части:
- Возможно, разработчики начинают думать дважды, прежде чем делать изменения/рефакторинг в домене, опасаясь, что это повлияет на базу данных. Этот страх не подходит для домена.
- Если домен начинает слишком сильно отклоняться от базы данных, вам придется столкнуться с некоторыми трудностями для поддержания домена в гармонии с ORM. Чем ближе к домену, тем сложнее настроить ORM. Чем ближе к ORM, тем грязнее домен получает.
2) Модель Persistence and Domain как две разделенные вещи
Это позволит вам делать все, что угодно, с вашим доменом. Не бойтесь рефакторинга, никаких ограничений от ORM и базы данных. Я бы рекомендовал этот подход для систем, которые работают с устаревшей или плохо спроектированной базой данных, что, вероятно, закончит испортить ваш домен.
Хорошие детали:
- Полностью бесплатный рефакторинг домена
- Легко вникнуть в другие темы DDD, например Ограниченный контекст.
Плохие части:
-
Дополнительные усилия с преобразованием данных между слоями. Время разработки (возможно, также время выполнения) будет медленнее.
-
Но главный и, поверьте, что будет больнее: вы потеряете основные преимущества использования ORM! Как отслеживание изменений. Возможно, вы в конечном итоге будете использовать фреймворки, такие как GraphDiff или даже отказаться от ORM и перейти к чистым ADO.NET.
Есть ли какие-либо проблемы в моем подходе к отображению моделей?
Я согласен с @jgauffin: "он в репозитории должен иметь место". Таким образом, ваши модели Persistence никогда не выйдут из уровня репозитория, в предпочтении никто не должен видеть эти объекты (кроме самого репозитория).
Ответ 3
Есть ли какие-либо причины 1) предпочли бы мнение 2) кроме ускорения разработки и повторного использования кода.
Я вижу большой (упрямый материал впереди): нет "модели сохранения". Все, что у вас есть, это реляционная модель в вашей базе данных и объектная модель домена. Сопоставление между двумя представляет собой набор действий, а не структур данных. Что еще, это именно то, что должны делать ORM.
Большинство ORM теперь поддерживают то, что они должны были предоставить с самого начала, - способ объявить эти действия непосредственно в коде, не касаясь объектов домена. Разумные конфигурации Entity Framework позволяют вам это сделать.
У вас может сложиться впечатление, что модель персистентности = нарушение SRP и попирание DDD, потому что многие реализации вы можете найти там. Но это не должно быть так.