Наличие отдельной доменной модели и персистентной модели в DDD

Я читал о доменно-ориентированном проектировании и о том, как его реализовать, используя вначале код для создания базы данных. Из того, что я прочитал и исследовал, есть два мнения по этому вопросу:

  1. Имейте 1 класс, который служит и моделью предметной области и моделью постоянства

  2. Имеют 2 разных класса, один из которых реализует доменную логику, а другой используется для подхода, основанного на коде

Теперь я знаю, что мнение 1) упрощает небольшие решения, в которых не так много различий между моделями доменов и персистентности, но я думаю, что оно нарушает принцип единой ответственности и тем самым создает много проблем, когда соглашения ORM мешают DDD.

Что меня удивляет, так это то, что существует множество примеров кода того, как реализовать мнение 1). Но мы не нашли ни одного примера того, как реализовать мнение 2) и как отобразить 2 объекта. (Возможно, есть такие примеры, но мне не удалось найти C# один)

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

Допустим, у меня есть система продажи билетов и срок действия билетов. Моя модель домена будет выглядеть так:

/// <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. Есть ли какие-либо причины, по которым мнение 1) было бы предпочтительнее мнения 2), кроме ускорения разработки и повторного использования кода.

  2. Есть ли проблемы в моем подходе к картированию моделей? Есть ли что-то, что я пропустил, что могло вызвать проблемы, когда решение растет?

3 ответа

Решение

Есть ли какие-либо причины, по которым мнение 1) было бы предпочтительнее мнения 2), кроме ускорения разработки и повторного использования кода.

Вариант 1 только из-за чистой лени и воображаемой увеличенной скорости разработки. Это правда, что эти приложения будут построены версии 1.0 быстрее. Но когда эти разработчики достигают версии 3.0 приложения, они не думают, что поддерживать приложение так весело из-за всех компромиссов, которые им пришлось сделать в модели предметной области из-за сопоставителя ORM.

Есть ли проблемы в моем подходе к картированию моделей? Есть ли что-то, что я пропустил, что вызвало бы проблемы, когда решение растет?

Да. Хранилище должно отвечать за сокрытие механизма персистентности. Его API должен работать только с сущностями домена, а не с персистентными сущностями.

Хранилище отвечает за выполнение преобразований в / из доменных сущностей (чтобы иметь возможность их сохранять). Метод выборки обычно использует ADO.NET или ORM-подобный Entity Framework для загрузки объекта / объекта базы данных. Затем преобразуйте его в правильный бизнес-объект и, наконец, верните его.

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

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

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

1) Постоянство и модель предметной области как одно и то же

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

Используя Entity Framework в качестве ORM, вы можете практически полностью исключить проблемы, связанные с доменом, от проблем ORM, используя быстрые сопоставления.

Хорошие части:

  • Быстро, легко, красиво (если база данных предназначена для этой задачи)

Плохие части:

  • Возможно, разработчики начинают думать дважды, прежде чем вносить изменения / рефакторинг в домене, опасаясь, что это повлияет на базу данных. Этот страх не годится для домена.
  • Если домен начинает слишком сильно расходиться с базой данных, вы столкнетесь с некоторыми трудностями в поддержании домена в гармонии с ORM. Чем ближе к домену, тем сложнее настроить ORM. Чем ближе к ORM, тем грязнее домен.

2) Постоянство и модель предметной области как две разные вещи

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

Хорошие части:

  • Полностью бесплатный рефакторинг домена
  • Будет легко покопаться в других темах DDD, таких как Bounded Context.

Плохие части:

  • Больше усилий с преобразованиями данных между слоями. Время разработки (возможно, также и время выполнения) станет медленнее.

  • Но главное и, поверьте мне, что повредит больше: вы потеряете основные преимущества использования ORM! Нравится отслеживание изменений. Возможно, вы в конечном итоге будете использовать фреймворки, такие как GraphDiff, или даже отказаться от ORM и перейти на чистый ADO.NET.

Есть ли проблемы в моем подходе к картированию моделей?

Я согласен с @jgauffin: "В репозитории должно выполняться отображение". Таким образом, ваши модели постоянства никогда не выйдут из уровня Repository, в предпочтении никто не должен видеть эти объекты (кроме самого хранилища).

Есть ли какие-либо причины, по которым мнение 1) было бы предпочтительнее мнения 2), кроме ускорения разработки и повторного использования кода.

Я могу видеть большое (опрометчивые вещи впереди): нет "Модели постоянства". Все, что у вас есть, это реляционная модель в вашей базе данных и объектная модель домена. Сопоставление между ними представляет собой набор действий, а не структур данных. Более того, именно это и должно делать ORM.

Большинство ORM теперь поддерживают то, что они должны были предоставить с самого начала - способ объявить эти действия непосредственно в коде, не затрагивая сущности вашего домена. Например, текущие конфигурации Entity Framework позволяют вам сделать это.

У вас может сложиться впечатление, что никакая модель постоянства = нарушение SRP и попрание DDD, потому что многие реализации, которые вы можете там найти, делают. Но это не должно быть так.

Другие вопросы по тегам