在 DDD 整体中跨服务/模块时,在我的处理程序中调用调解器是不是正确

Posted

技术标签:

【中文标题】在 DDD 整体中跨服务/模块时,在我的处理程序中调用调解器是不是正确【英文标题】:Would calling a mediator be correct in my Handler when crossing services/modules in a DDD monolith在 DDD 整体中跨服务/模块时,在我的处理程序中调用调解器是否正确 【发布时间】:2020-06-01 00:03:31 【问题描述】:

架构前言

在微服务中,这可能是符合干净架构的服务:

篮子服务

API 应用程序 [CQRS] 核心 基础设施

目录服务

API 应用程序 [CQRS] 核心 基础设施

但由于我将 DDD 应用于单体应用,我目前可以删除我的 Api 层并减少“项目”/模块。因此,每个模块(例如购物篮和目录)目前都包含 3 个项目:

应用 核心 基础设施

两者都依赖于.Core

问题描述

我的应用程序有一个 BasketModule 和一个 CatalogModule。 使用 Mediator 从 CatalogModule(包含产品)获取信息是否正确:

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));

这样做是否正确?我不是在谈论示例代码的其余部分,而是特别是关于调用调解器以获取产品并在“Catalog.Application”上具有项目“Basket.Application”的依赖关系

【问题讨论】:

【参考方案1】:

说实话,只有您知道您的问题的答案。从外观上看,尽管命名为“GetBasketByBuyerIdCommand”,但您似乎只是在代码中运行查询。所以这意味着你只在研究 CQRS 的“Q”,从这个角度来看,你正在做的事情很好。

在我看来,您的问题的答案在于您的架构的其余部分,尤其是您目前试图用它实现的目标。如果您希望您的 BasketModule 和 CatalogModule 在未来某个地方完全独立,那么现在采取的这种类型的选择可能会在未来产生很大的影响。

例如,如果您稍后希望将这两个模块分成两个不同的微服务,您将不得不想出一种不同的方式来执行您描述的查询。如何处理这件事本身就是一个全新的话题。

【讨论】:

赞成只是因为您注意到我将其命名为 Command 并且应该是 Query。我仍在检查答案以评估我下一步应该做什么。感谢您的关注:)【参考方案2】:

中介的主要作用是将表示层与应用程序服务分离,并使用中间件进行一些默认处理,而不是在有界上下文之间进行通信。

因此,我希望每个有界上下文都有一个中介 :)

要集成两个 BC,您可以将 GetProductByIdQuery 接口添加到 Basket 域(或应用程序)层,并在 Basket/infra 中实现。

GetProductByIdQuery 的实现(被视为辅助适配器)可能会调用 Catalog 的 BC 的中介,并转换结果以保留无处不在的语言。

恕我直言,如果您决定拆分其中的某些部分,即只有辅助适配器实现会改为进行一些进程间通信,这将保持整体清洁并降低迁移成本。

PS:我对干净的架构不是很熟悉,但我认为它是 Hexagonal 的一个更有特色的版本..

【讨论】:

以上是关于在 DDD 整体中跨服务/模块时,在我的处理程序中调用调解器是不是正确的主要内容,如果未能解决你的问题,请参考以下文章

我的 JBehave 验收测试应该针对 DDD 应用程序中的哪一层?

如何在 NestJS 中跨模块全局注入价值?

来自事件处理程序的 ReactiveUI MVVM 更新属性

在 iOS9 中跨应用程序共享 cookie

DDD开发实践流程

在池中跨 Akka Actor 实例共享可变数据