Как должно быть выражено добавление совокупного корня в репозиторий?

StackOverflow https://stackoverflow.com/questions/2096705

Вопрос

Допустим, у нас есть совокупная корневая сущность типа Заказ, которая связывает клиентов и строки заказов.Когда я думаю о сущности заказа, более естественно представить ее как не определяемую без идентификатора.Заказ без идентификатора, по-видимому, лучше представить как запрос заказа, чем как заказ.

Чтобы добавить заказ в репозиторий, я обычно вижу, как люди создают экземпляр заказа без идентификатора, а затем репозиторий завершает объект:

class OrderRepository
{
    void Add(Order order)
    {
        // Insert order into db and populate Id of new order
    }
}

Что мне нравится в этом подходе, так это то, что вы добавляете экземпляр Order в OrderRepository.Это имеет большой смысл.Однако экземпляр заказа не имеет идентификатора, и в рамках потребителя репозитория для меня все еще не имеет смысла, что заказ не имеет идентификатора.Я мог бы определить OrderRequest как экземпляр порядка и добавить его в репозиторий, но это похоже на получение яблока из апельсина и последующее добавление его в список апельсинов.

Альтернативно, я также видел такой подход:

class OrderRepository
{
    Order AddOrder(Customer customer)
        // It might be better to call this CreateOrder
    {
        // Insert record into db and return a new instance of Order
    }
}

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

В любом случае работает, поэтому мой вопрос:Должен ли я жить с одной из этих двух интерпретаций или существует лучшая практика моделирования вставки?

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

Это было полезно?

Решение

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

Остальные параметры зависят от логики предметной области.Если Логика предметной области диктует, что Порядок без идентификатора бессмысленен, идентификатор является обязательным инвариантом Порядка, и мы должны смоделировать его следующим образом:

public class Order
{
    private readonly int id;

    public Order(int id)
    {
        // consider a Guard Clause here if you have constraints on the ID
        this.id = id;
    }
}

Обратите внимание, что, отметив id поле как readonly мы сделали его инвариантом.Мы не можем изменить его для данного экземпляра Order.Это идеально сочетается с Доменно-ориентированный дизайн's Сущность шаблон.

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

К настоящему моменту вам, вероятно, интересно, как это будет работать с автоматически сгенерированными идентификаторами из базы данных.Ну, это не так.

Не существует хорошего способа убедиться, что предоставленный идентификатор еще не используется.

Это оставляет вам два варианта:

  • Измените идентификатор на Guid.Это позволяет любому вызывающему абоненту предоставить уникальный идентификатор для нового заказа.Однако для этого необходимо также использовать Guids в качестве ключей базы данных.
  • Измените API так, чтобы для создания нового заказа требовался не объект Order, а OrderRequest, как вы предложили - OrderRequest может быть почти идентичен классу Order, за вычетом идентификатора.

Во многих случаях создание нового заказа — это бизнес-операция, которая в любом случае требует специфического моделирования, поэтому я не вижу проблем с проведением такого различия.Хотя Order и OrderRequest могут быть очень похожи семантически, они даже не обязательно должны быть связаны в иерархии типов.

Я мог бы даже пойти еще дальше и сказать, что они не должна быть связаны, потому что OrderRequest является Объект значения тогда как Порядок – это Сущность.

Если выбран этот подход, метод AddOrder должен возвращать экземпляр Order (или, по крайней мере, идентификатор), поскольку в противном случае мы не сможем узнать идентификатор заказа, который мы только что создали.Это возвращает нас к нарушению CQS, поэтому я склонен предпочитаю руководства для идентификаторов объектов.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top