Будет ли правильным вызов посредника в моем обработчике при пересечении служб / модулей в монолите DDD

Предисловие об архитектуре

В микросервисе это может быть сервис в соответствии с чистой архитектурой:

КорзинаСервис

  • Api
    • Application [CQRS]
      • Core
    • Инфраструктура

Каталог

  • Api
    • Application [CQRS]
      • Core
    • Инфраструктура

Но поскольку я применяю DDD к монолиту, в настоящее время я могу удалить свой слой Api и иметь меньше «проектов» / модулей. Таким образом, каждый модуль (например, корзина и каталог) в настоящее время содержит 3 проекта:

  • Application
    • Core
  • Инфраструктура

Оба имеют зависимость от .Core

Описание проблемы

В моем приложении есть BasketModule и CatalogModule. Было бы правильно получать информацию из CatalogModule (который содержит продукты) с помощью Mediator следующим образом:

public Task<BasketDTO> Handle(GetBasketByBuyerIdCommand request, CancellationToken cancellationToken)
    {
        BasketDTO result;
        var basket = m_basketRepo.GetById(request.BuyerId);

        if (basket == null)
        {
            result = new BasketDTO()
            {
                BuyerId = request.BuyerId,
                Items = new List<BasketItem>()
            };
            return Task.FromResult(result);
        }

        //Could be automapper, but not now currently //ignore
        var products = new List<DTO.Product>();

        foreach (var item in basket.Items)
        {
          var product = m_mediator.Send(new GetProductByIdQuery(item.ProductId)).Result; //ignore the non-async. It's example code 

            products.Add(new DTO.Product()
            {
                Id = product.Id,
                Price = product.Price,
                Title = product.Title
            });
        }

        result = new BasketDTO()
        {
            BuyerId = basket.BuyerId,
            Items = basket.Items
            .Select(dl => new DTO.BasketItem()
            {

                ProductId = dl.ProductId,
                Quantity = dl.Quantity,
                Product = products
                .Where(cl => cl.Id == dl.ProductId)
                .FirstOrDefault()
            }).ToList()
        };

        return Task.FromResult(result);
    }
}

Речь идет о следующей строчке:

 var product = await m_mediator.Send(new GetProductByIdQuery(item.ProductId));

Это правильно? Я не говорю об остальной части кода примера, а конкретно о вызове посредника для получения продукта и о зависимости проекта «Корзина.Приложение» от «Каталог.Приложение».


person NicoJuicy    schedule 01.06.2020    source источник


Ответы (2)


Если честно, ответ на свой вопрос знаете только вы. Судя по внешнему виду и несмотря на название GetBasketByBuyerIdCommand, кажется, что вы выполняете только запросы в своем коде. Это означает, что вы работаете только над «Q» CQRS, и с этой точки зрения то, что вы делаете, нормально.

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

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

person Joonas Lindholm    schedule 01.06.2020
comment
Проголосовали только потому, что вы заметили, что я назвал его Command, и это должно быть Query. Я все еще проверяю ответы, чтобы понять, что мне делать дальше. Спасибо, что заметили :) - person NicoJuicy; 01.06.2020

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

Таким образом, я бы предпочел иметь посредника для ограниченного контекста :)

Для интеграции двух BC вы можете добавить интерфейс GetProductByIdQuery на уровень домена (или приложения) корзины и реализовать его в корзине / ниже.

Реализация GetProductByIdQuery (рассматриваемого как вторичный адаптер) может вызывать посредника BC каталога и преобразовывать результат для сохранения повсеместного языка.

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

PS: Я не очень знаком с чистой архитектурой, но считаю ее более функциональной версией гексагональной.

person reda la    schedule 01.06.2020