You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 9模块化单体(Modulith)跨模块调用方案咨询

针对.NET 9模块化单体跨模块调用的替代方案

方案1:独立契约包+轻量本地事件总线

核心思路是将跨模块的事件/消息契约从Base模块剥离,放到独立的小型类库(如ContratosComuns),各模块按需引用该契约包,Base模块仅提供轻量的事件总线核心实现,避免Base臃肿。

实现步骤:

  1. 创建独立的ContratosComuns类库,定义跨模块事件:
public class RecebimentoCompraCriadoEvent
{
    public int CompraId { get; set; }
    public decimal ValorTotal { get; set; }
}
  1. Base模块实现轻量内存事件总线(无需依赖第三方库):
public interface IEventBus
{
    void Publish<TEvent>(TEvent @event) where TEvent : class;
    void Subscribe<TEvent>(Action<TEvent> handler) where TEvent : class;
}

public class InMemoryEventBus : IEventBus
{
    private readonly Dictionary<Type, List<Delegate>> _handlers = new();

    public void Publish<TEvent>(TEvent @event) where TEvent : class
    {
        if (_handlers.TryGetValue(typeof(TEvent), out var handlers))
        {
            foreach (var handler in handlers)
            {
                ((Action<TEvent>)handler).Invoke(@event);
            }
        }
    }

    public void Subscribe<TEvent>(Action<TEvent> handler) where TEvent : class
    {
        var eventType = typeof(TEvent);
        _handlers.TryAdd(eventType, new List<Delegate>());
        _handlers[eventType].Add(handler);
    }
}
  1. 在Base的DI容器中注册IEventBus:
builder.Services.AddSingleton<IEventBus, InMemoryEventBus>();
  1. RecebimentoCompras模块引用ContratosComuns,在新增操作后发布事件:
public class RecebimentoCompraService
{
    private readonly IEventBus _eventBus;

    public RecebimentoCompraService(IEventBus eventBus)
    {
        _eventBus = eventBus;
    }

    public void CriarRecebimento(CompraNovaDto dto)
    {
        // 执行RecebimentoCompras的新增逻辑
        var recebimento = new RecebimentoCompra { Id = dto.Id, Valor = dto.Valor };
        _dbContext.Recebimentos.Add(recebimento);
        _dbContext.SaveChanges();

        // 发布事件
        _eventBus.Publish(new RecebimentoCompraCriadoEvent 
        { 
            CompraId = recebimento.Id, 
            ValorTotal = recebimento.Valor 
        });
    }
}
  1. Faturamento模块引用ContratosComuns,通过模块初始化器订阅事件:
public class FaturamentoService
{
    public void ProcessarFaturamentoParaRecebimento(RecebimentoCompraCriadoEvent @event)
    {
        // 执行Faturamento模块的业务逻辑
        var fatura = new Fatura { CompraId = @event.CompraId, Valor = @event.ValorTotal };
        // 保存或处理发票逻辑
    }
}

// 使用.NET 9的ModuleInitializer自动注册订阅
public static class FaturamentoModuleSetup
{
    [ModuleInitializer]
    public static void Initialize()
    {
        var eventBus = ServiceProviderAccessor.Current.GetService<IEventBus>();
        eventBus.Subscribe<RecebimentoCompraCriadoEvent>(
            new FaturamentoService().ProcessarFaturamentoParaRecebimento);
    }
}

优缺点:

  • 优点:完全解耦模块,Base模块仅负责总线核心,契约独立管理,各模块可单独编译发布。
  • 缺点:异步调用模式,需自行处理事件重试、幂等性问题;内存总线重启后事件会丢失,若需持久化可搭配本地消息队列(如SQL Server队列)。

方案2:独立契约包+依赖反转(接口驱动)

通过在独立契约包中定义服务接口,Faturamento模块实现该接口,RecebimentoCompras模块通过DI获取接口实例调用,避免Base模块包含业务相关接口。

实现步骤:

  1. 创建ContratosComuns类库,定义跨模块服务接口:
public interface IFaturamentoProcessamentoService
{
    void ProcessarFaturamento(int compraId, decimal valorTotal);
}
  1. Faturamento模块引用ContratosComuns,实现接口:
public class FaturamentoProcessamentoService : IFaturamentoProcessamentoService
{
    public void ProcessarFaturamento(int compraId, decimal valorTotal)
    {
        // Faturamento模块的业务逻辑实现
    }
}
  1. Faturamento模块通过ModuleInitializer注册服务到DI:
public static class FaturamentoModuleSetup
{
    [ModuleInitializer]
    public static void Initialize(IServiceCollection services)
    {
        services.AddScoped<IFaturamentoProcessamentoService, FaturamentoProcessamentoService>();
    }
}
  1. RecebimentoCompras模块引用ContratosComuns,通过DI注入接口并调用:
public class RecebimentoCompraService
{
    private readonly IFaturamentoProcessamentoService _faturamentoService;

    public RecebimentoCompraService(IFaturamentoProcessamentoService faturamentoService)
    {
        _faturamentoService = faturamentoService;
    }

    public void CriarRecebimento(CompraNovaDto dto)
    {
        // 新增RecebimentoCompras逻辑
        _dbContext.Recebimentos.Add(new RecebimentoCompra { Id = dto.Id, Valor = dto.Valor });
        _dbContext.SaveChanges();

        // 直接调用Faturamento模块的方法
        _faturamentoService.ProcessarFaturamento(dto.Id, dto.Valor);
    }
}

优缺点:

  • 优点:同步调用,类型安全,模块间仅依赖契约接口,Base模块无业务耦合。
  • 缺点:模块间存在显式依赖(RecebimentoCompras依赖契约接口),若接口频繁变更需同步更新契约包和相关模块。

方案3:模块间弱引用(仅依赖接口项目)

如果两个模块的依赖关系稳定,可让RecebimentoCompras模块仅引用Faturamento的接口项目(而非实现项目),实现编译时依赖接口、运行时依赖实现的弱耦合。

实现步骤:

  1. Faturamento模块拆分出Faturamento.Contracts类库,定义对外暴露的接口:
public interface IFaturamentoService
{
    void GerarFatura(int compraId, decimal valor);
}
  1. Faturamento的实现项目Faturamento.Implementation引用Faturamento.Contracts并实现接口:
public class FaturamentoService : IFaturamentoService
{
    public void GerarFatura(int compraId, decimal valor)
    {
        // 业务逻辑
    }
}
  1. RecebimentoCompras模块引用Faturamento.Contracts,通过DI注入接口调用:
public class RecebimentoService
{
    private readonly IFaturamentoService _faturamentoService;

    public RecebimentoService(IFaturamentoService faturamentoService)
    {
        _faturamentoService = faturamentoService;
    }

    public void CriarRecebimento(CompraDto dto)
    {
        // 新增逻辑
        _faturamentoService.GerarFatura(dto.Id, dto.Valor);
    }
}
  1. Base模块通过扫描所有模块的程序集,自动注册实现类:
builder.Services.Scan(scan => scan
    .FromAssembliesOf(typeof(IFaturamentoService))
    .AddClasses()
    .AsImplementedInterfaces());

优缺点:

  • 优点:实现简单,同步调用类型安全,各模块可单独编译(修改Faturamento实现无需重新编译RecebimentoCompras)。
  • 缺点:模块间存在定向依赖,若后续依赖关系变化可能需要调整模块引用结构,不适合频繁变更的业务场景。

方案选择建议

  • 若模块间交互频繁且需要解耦,优先选择方案1(事件总线)。
  • 若需要同步调用且业务逻辑依赖稳定,优先选择方案2(依赖反转)。
  • 若依赖关系非常稳定且追求简单实现,可选用方案3(弱引用)。

所有方案均保持Base模块的轻量化,仅负责核心基础设施(DI、总线核心等),业务相关契约/接口均独立管理,满足模块化单体的单独编译发布需求。

内容的提问来源于stack exchange,提问作者Iury

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 20:35:06