模块化单体(Modular Monolith)是 BitzOrcas.Modern 的核心架构形态。它既具备单体架构的单机部署、极速开发与事务内调用的优势,又通过严格的物理项目隔离与架构门禁杜绝了传统单体代码腐化为“大泥球”的致命风险。
模块物理结构与依赖边界拓扑
模块物理项目标准结构
每个标准业务领域模块均划分为 3 个物理 .csproj 工程:
| 工程名称 | 物理命名 | 核心定位与包含内容 | 允许被哪些项目引用 |
|---|---|---|---|
| 1. 公开契约层 | *.Contracts | 模块对外暴露的只读查询模型、集成事件契约(IIntegrationEvent)与请求 DTO | 允许任何外部业务模块引用 |
| 2. 领域内核层 | *.Domain | 统一聚合根、实体、值对象、领域事件与仓储接口契约 | 仅限当前模块的 Application 层引用 |
| 3. 应用切片层 | *.Application (或无后缀) | 垂直切片(Commands / Queries / Rules / Handlers)、IAppModule 装配根 | 仅限 API Host / AppHost 宿主组合根引用 |
跨模块协作的 5 条硬性约束
为了防止模块边界退化,所有业务模块必须严格遵守以下 5 条铁律:
- 单向契约依赖:跨模块项目引用(
ProjectReference)只允许指向目标模块的*.Contracts包; - 查询走只读端口或读模型:模块 A 如需获取模块 B 的数据,必须通过 B 暴露在
Contracts中的只读查询端口或共享读模型,严禁直接注入对方的IAggregateRepository; - 关键状态广播走集成事件:当领域状态发生变更时,通过 CAP 事务性发件箱发布集成事件(
IIntegrationEvent),下游模块基于“至少一次(At-Least-Once)”原则幂等消费; - 事务原子性保障:需要与业务持久化保证强一致的消息,必须在聚合根所在的同一个数据库事务中提交至本地 Outbox 表;
- 领域事件仅限模块内部:聚合根产出的
DomainEvent仅在当前请求的事务内同进程流转,不得直接跨模块广播。
IAppModule 编译期模块装配机制
BitzOrcas 废弃了运行时反射扫描装配。每个模块在根目录下实现 IAppModule 契约,供 Host 静态装配:
using BitzOrcas.Application.Abstractions;using Microsoft.Extensions.Configuration;using Microsoft.Extensions.DependencyInjection;
namespace BitzOrcas.Modules.Legal;
/// <summary>/// 法律业务模块静态装配根/// </summary>/// <remarks>/// 实现 <see cref="IAppModule"/> 接口,供 Host 在编译期无反射静态注册。/// </remarks>public sealed class LegalModule : IAppModule{ public string Name => "Legal";
public void ConfigureServices(IServiceCollection services, IConfiguration configuration) { // 1. 注册模块特有的领域计算器或适配器 // 2. 垂直切片 Handler 会由 Roslyn 增量生成器自动扫描并生成静态注册代码 }}ArchUnit 自动化架构守卫防线
在 tests/BitzOrcas.Architecture.Tests/ModuleBoundaryTests.cs 中,ArchUnit 会在 CI 构建期间强制扫描全仓依赖:
using ArchUnitNET.Domain;using ArchUnitNET.Fluent;using ArchUnitNET.Loader;using ArchUnitNET.xUnit;using Xunit;using static ArchUnitNET.Fluent.ArchRuleDefinition;
namespace BitzOrcas.Architecture.Tests;
public sealed class ModuleBoundaryTests{ // 1. 加载所有业务模块程序集元数据 private static readonly Architecture Architecture = new ArchLoader().LoadAssemblies( typeof(Legal.LegalModule).Assembly, typeof(Legal.Domain.MatterIntake).Assembly).Build();
[Fact] public void ExternalModules_MustNotDependOn_InternalModuleImplementations() { // 2. 架构红线:外部模块严禁依赖 Legal 模块内部 Domain 与 Application 实现 IArchRule rule = Types().That() .ResideInNamespace("BitzOrcas.Modules.Billing..") .ShouldNot() .DependOnAny(Types().That().ResideInNamespace("BitzOrcas.Modules.Legal.Domain.."));
rule.Check(Architecture); }}总结
BitzOrcas.Modern 的模块化单体设计确保了:
- 物理强隔离:团队分工清晰,模块代码自包含;
- 架构自愈:ArchUnit 自动化测试随时拦截违规依赖;
- 演进自由:未来若业务需要拆分为独立微服务,只需将物理模块工程剥离部署,无需重构内部领域逻辑。