@alexg-nn

Как использовать DDD подход к реализации каталога?

Привет!

На небольшом pet-проекте изучаю премудрости DDD. Итак, имеется некий аналог магазина: каталог, страница товара, корзина, и т.д. Размышляя, пришёл к выводу, что понятие "Товар" в системе явно имеет как минимум два значения. В контексте каталога это больше репрезентативная модель - название, описание, картинки и т.д. В контекте покупки это уже цены, всякие программы лояльности, скидки и проч. По большому счёту, первый контекст вообще скорее обычный блог с набором страниц. Поэтому я решил выделить два независимых контекста (Bounded Context):
1. Catalog
2. Shop

В обоих контекстах есть понятие Product, реализованный одноимённым классом
namespace Acme\Catalog;

class Product {
  private $id; // Это идентификатор продукта в контексте каталога
  private $name;
  private $description;
  private $poster;
  // ...
}


namespace Acme\Shop;

class Product {
  private $id; // Это идентификатор продукта в контексте продаж
  private $sku;
  private $price;
  // ...
}


Контекст Shop опирается на Catalog, поэтому ключ $id в продукте в контексте Shop суррогатный и всегда можно получить его на основе $id из контекста Catalog. Что то типа:
namespace Acme\Shop

class ShopService {
  public function fromCatalogProduct($id) {
    // тут строим идентификатор на основе каталожного
  }
}


В принципе, такой подход вполне себе работает, когда нужно показать одну карточку товара
class ProductPageController
{
  // ...
  public function pageAction($id) {
    $catalogProduct = $catalogRepo->get($id);
    $shopId = $shopService->fromCatalogProduct($id);
    $shopProduct = $shopRepo->get($shopId);
    // Дальше рендерим страницу, опираясь на оба контекста
  }
}


Конечно, лишний запрос дело не ахти. И при бОльшем числе контекстов, задействованных для отображения, число запросов будет расти. Но пока, например, терпимо.
А вот как поступить, когда надо отобразить страницу каталога, где много товаров. По идее, мне так же нужны оба контекста. Решения видятся такими:
  1. В лоб - сначала из контекста Catalog запрошиваем продукты, а потом для каждого из них из контекста Shop дёргаем цену. Минусы очевидны.
  2. Получше - сначала из контекста Catalog запрошиваем продукты, а потом пакетно из контекста Shop дёргаем цены. Всё равно как то не ахти смотрится.
  3. Отдельная модель чтения для каталога. После создания или редактирования товаров в обоих контекстах, данные сливаются в какую нибудь MongoDB базу, откуда забирается уже готовый агрегат. Тут CQRS в помощь, даже без Event Sourcing'а вроде бы. Вопрос только в создании модели чтения и поддержании её на плаву.


Есть ли у уважаемых участников сообщества мысли по этому поводу?
  • Вопрос задан
  • 122 просмотра
Пригласить эксперта
Ответы на вопрос 2
@ddd329
Модель предметной области по DDD, это чисто для организации бизнес-логики, и такие понятия как Агрегат, Сущность, Объект-значение служит исключительно этим целям.
Для отображения данных в графическом интерфейсе можно, а то и нужно создавать вторую модель для чтения.
Т.е. по итогу должно получиться две модели, во многом они будут себя дублировать в простых областях.
Ответ написан
Комментировать
@Mindgrow
Контекст должен быть один.
Сущность товара должна быть одна.
В твоем случае Shop\Product - это будет товарная позиция, но она должна быть связана с Catalog\Product, чтобы не дублировать сущности. Просто надо использовать аггрегат, получаемый либо от Product, либо от какой-то ещё сущсности (то есть сервиса, относящегося к сущности), например ProdcutCatalog, PriceList, Reporter или что-то ещё в этом духе.

Например сервис будет иметь метод PriceList.GetPriceList(from, to), а он внутри себя запросами уже делает нужный аггрегат, но при построении аггрегата, метод будет обращаться к таблицам, относящимся к сущности Product
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы