Как работать с постоянством с помощью богатой доменной модели

Я занимаюсь редизайном своего приложения NodeJS, потому что хочу использовать концепцию Rich Domain Model. В настоящее время я использую Anemic Domain Model, и это плохо масштабируется.. Я просто вижу повсюду 'ifs', lol.

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

Для начала я хотел бы описать определенные мной слои и их назначение:

Модель постоянства

  • Определяет модели таблиц. Определяет имя таблицы, столбцы, ключи и отношения
  • Я использую Sequelize в качестве ORM, поэтому модели, определенные с помощью Sequelize, считаются моей моделью персистентности.

Модель предметной области

  • Сущности и Поведения. Объекты, которые соответствуют абстракциям, созданным как часть бизнес-домена
  • Я создал несколько классов, и лучше всего то, что я могу извлечь выгоду из иерархии, чтобы решить все проблемы (без нагрузки ifs yay).

Объект доступа к данным (DAO)

  • Отвечает за управление данными и преобразование записей персистентной модели в сущности доменной модели. Все связанные с персистентностью действия относятся к этому слою
  • В моем случае DAO работают поверх моделей Sequelize, созданных на основе модели постоянства, однако я сериализирую записи, возвращаемые при взаимодействии с базой данных, в различных объектах на основе их свойств. Например: если у меня есть таблица со столбцом с именем "UserType", который содержит два значения [ADMIN,USER], когда я выбираю записи в этой таблице, я бы сериализовал возврат в соответствии с типом пользователя, поэтому пользователь с типом: ADMIN будет экземпляром класса AdminUser, где User с типом: USER будет просто DefaultUser...

Сервисный уровень

  • Отвечает за всю Универсальную бизнес-логику, такую ​​как Утилиты и другие Сервисы, которые не являются частью поведения ни одного из Объектов Домена

Уровень клиента

  • Любой класс Consumer, который играет с объектами и отвечает за запуск постоянства.

Теперь путаница начинается, когда я реализую клиентский уровень...

Допустим, я реализую новый REST API:

POST: .../api/CreateOrderForUser/
{
  items: [{
    productId: 1,
    quantity: 4
  },{
    productId: 3,
    quantity: 2
  }]
}

В моей функции обработчика у меня будет что-то вроде:

function(oReq){
  var oRequestBody = oReq.body;
  var oCurrentUser = oReq.user; //This is already a Domain Object
  var aOrderItems = oRequestBody.map(function(mOrderData){
    return new Order(mOrderData); //Constructor sets the properties internally
  });
  var oOrder = new Order({
    items: aOrderItems
  });

  oCurrentUser.addOrder(oOrder);

  // Okay... So far so good... But how do I persist whatever 
  // happened above? Should I call each DAO for each entity 
  // created? Like, first create the Order, then create the 
  // Items, then update the User?

}

Хорошо... Итак, я нашел способ заставить его работать на самом деле - объединить модель постоянства и модель предметной области, что означает, что oCurrentUser.addOrder(...) будет выполнять требуемую бизнес-логику и будет вызывать OrderDAO для сохранения Order. вместе с пунктами в конце. Плохая вещь в этом заключается в том, что теперь addOrder также должен обрабатывать транзакции, потому что я не хочу добавлять заказ без элементов или обновлять пользователя без заказа.

Итак, что мне здесь не хватает?

0 ответов

Заполнители.

Это недостающий фрагмент истории.

В вашем примере, скорее всего, не было бы отдельной таблицы для позиций заказа (и никаких отношений, никаких внешних ключей...). Элементы здесь, по-видимому, представляют собой значения (описывающие сущность, то есть: "45 долларов США"), а не сущности (вещи, которые меняются во времени, и мы отслеживаем, то есть: банковский счет). Таким образом, вы не сохраняете напрямую элементы OrderItems, а вместо этого сохраняете только Order (с элементами в нем).

Кусок кода, который я ожидаю найти вместо вашего комментария, может выглядеть так orderRepository.save(oOrder);, Кроме того, я ожидаю, что пользователь будет слабой ссылкой (только по идентификатору) в заказе, а не заказами, содержащимися в пользователе как вашем oCurrentUser.addOrder(oOrder); Код предполагает.

Более того, слои, которые вы описываете, имеют смысл, но в вашем примере вы смешиваете вопросы доставки (концепции, такие как запрос, ответ...) с концепциями домена (добавление элементов в новый заказ), я бы посоветовал вам взглянуть на установленные шаблоны чтобы сохранить эти проблемы, такие как шестиугольная архитектура. Это особенно важно для модульного тестирования, поскольку ваш "клиентский код", скорее всего, будет тестом, а не функцией-обработчиком. Код извлечения / создания - выполнения чего-либо - сохранения обычно представляет собой функцию в службе приложений, описывающую ваш вариант использования.

"Внедрение доменного дизайна" Вона Вернона - хорошая книга по DDD, которая определенно пролила бы больше света на эту тему.

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