ASP.NET Core模块化单体架构中,API与应用层契约应置于哪个项目?
在ASP.NET Core 7模块化单体架构中规划API与应用层的契约位置
针对API层仅能访问契约、无法触及应用/领域层内部的需求,结合.NET生态特性和架构原则,以下是可行方案分析与最佳实践:
1. 契约放在API层(遵循DIP原始定义,但不适配多API场景)
按照Robert C. Martin《Agile Software Development》中的依赖倒置原则(DIP),抽象由上层(策略层)拥有,即API层定义契约接口,应用层实现接口。但该方案存在明显缺陷:
- 无法支持REST、GraphQL、gRPC等多种API技术共享同一契约,不同API层需重复维护契约定义
- 导致应用层依赖API层,违背分层原则;跨限界上下文调用时,调用方需依赖目标上下文的API层,耦合性过高
2. 契约放在应用层(常见简化方案,但存在访问泄漏风险)
多数示例将命令/查询、接口等契约放在应用层,好处是多API层可直接共享契约。但受C#项目依赖传递性影响:
- API层依赖应用层后,会间接获得领域层的访问权限,开发人员可能绕过应用层直接操作领域逻辑,破坏分层边界
3. 用internal修饰符+[InternalsVisibleTo]限制访问(可行但繁琐)
通过.NET访问控制特性实现隔离:
- 应用层:将命令/查询处理程序、服务实现设为
internal,仅暴露契约接口为public;添加[InternalsVisibleTo]特性,授权DI容器所在项目(如Web层)注册内部服务 - 领域层:将领域实体、领域服务设为
internal,通过[InternalsVisibleTo]授权应用层访问 - 缺点:配置繁琐,需在多个项目中维护特性授权,团队协作易出现遗漏,长期维护成本高
4. 单独的契约层(推荐的生产级方案)
新增独立的契约类库(如YourApp.BoundedContext.Contracts),专门存放API层所需的所有契约:
- 依赖关系规划:
- API层 → 仅依赖契约层
- 应用层 → 依赖契约层 + 领域层
- 领域层 → 无上层依赖
- 核心优势:
- 彻底隔离API层与应用/领域层,API层仅能看到契约定义,无法触及内部实现
- 天然支持多API技术共享同一契约,无需重复定义
- 跨限界上下文调用时,只需依赖目标上下文的契约层,避免耦合其API或应用层
- 应用/领域层的类可设为
public(API层未依赖这些项目),省去大量internal和特性配置的麻烦
.NET中的常见实践说明
.NET没有Java的package protected访问修饰符,通常通过项目边界+访问控制+分层设计组合实现边界隔离:
- 简化示例中不做严格隔离,是为了降低入门门槛、减少项目数量便于演示;但在注重边界清晰的模块化单体架构中,单独契约层是更符合架构原则的选择
- 结合MediatR使用时,契约层存放自定义的
IRequest/IRequest<TResponse>模型,应用层存放internal的IRequestHandler实现,API层仅发送请求,完全看不到处理逻辑
内容的提问来源于stack exchange,提问作者M. Koch
相关产品推荐
相关产品推荐

