模块化单体架构的双引擎:Swagger文档与ADR协同实践
痛点与解决方案
你是否在维护模块化单体系统时面临API文档碎片化、架构决策追溯困难的问题?本文将系统讲解如何通过Swagger自动生成标准化API文档,结合架构决策记录(ADR)实现设计意图与代码实现的双向追踪,最终构建可演进的模块化DDD系统。
读完本文你将掌握:
- 基于XML注释的Swagger文档自动化方案
- 模块化架构中API网关的统一文档策略
- 17个关键架构决策的实践经验(附完整决策记录表)
- CQRS模式下命令/查询API的文档设计技巧
- 架构合规性的自动化测试保障机制
Swagger文档自动化构建
配置驱动的API文档生成
在模块化单体架构中,统一的API文档是跨团队协作的关键。该项目通过SwaggerExtensions.cs实现零侵入式文档生成:
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "MyMeetings API",
Version = "v1",
Description = "MyMeetings API for modular monolith .NET application."
});
// 包含XML注释文件
var commentsFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
$"{Assembly.GetExecutingAssembly().GetName().Name}.xml");
options.IncludeXmlComments(commentsFile);
// JWT安全定义
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey
});
});
关键实现要点:
- 通过
IncludeXmlComments加载项目生成的XML文档(要求.csproj中设置<GenerateDocumentationFile>true</GenerateDocumentationFile>) - 配置JWT安全模式支持API授权文档自动生成
- 使用
CustomSchemaIds解决类型名称冲突问题
模块化控制器的文档组织
系统采用"一模块一控制器命名空间"原则,确保API文档与业务边界对齐:
src/API/CompanyName.MyMeetings.API/Modules
├── UserAccess
│ ├── AuthenticatedUserController.cs
│ ├── EmailsController.cs
│ └── UserRegistrationsController.cs
├── Meetings
│ ├── MeetingsController.cs
│ ├── MeetingGroupsController.cs
│ └── MeetingCommentsController.cs
└── Payments
├── SubscriptionsController.cs
└── MeetingFeePaymentsController.cs
以MeetingsController为例,CQRS模式下的API文档自动分类:
[Route("api/meetings/meetings")]
[ApiController]
public class MeetingsController : ControllerBase
{
[HttpGet("")]
[HasPermission(MeetingsPermissions.GetAuthenticatedMemberMeetings)]
[ProducesResponseType(typeof(List<MemberMeetingDto>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetAuthenticatedMemberMeetings()
{
// 查询处理逻辑
}
[HttpPost("")]
[HasPermission(MeetingsPermissions.CreateNewMeeting)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> CreateNewMeeting([FromBody] CreateMeetingRequest request)
{
// 命令处理逻辑
}
}
Swagger将自动生成包含权限要求、请求/响应类型的API文档,实现"代码即文档"的一致性。
架构决策记录(ADR)实践
ADR核心价值与规范
架构决策记录(ADR)是解决"为什么这么设计"的关键工具。项目遵循Michael Nygard模板,每个决策包含状态、上下文、决策和后果四要素,存储于docs/architecture-decision-log目录:
# 7. Use CQRS architectural style
Date: 2019-07-01
Log date: 2019-11-04
## Status
Accepted
## Context
Our application should handle 2 types of requests - reading and writing.
For reading, we need relational data model...
## Decision
We applied the CQRS architectural style/pattern for each business module...
## Consequences
- Façade method takes only Command or Query object
- Optimized models for writes and reads (SRP principle)
关键架构决策矩阵
| 决策ID | 主题 | 核心内容 | 影响范围 |
|---|---|---|---|
| 0002 | 模块化单体架构 | 采用模块化单体而非微服务,通过DDD限界上下文划分模块 | 整体系统 |
| 0005 | 统一REST API模块 | 单个API层引用所有业务模块,避免多API项目复杂性 | API层 |
| 0007 | CQRS架构风格 | 读写模型分离,命令处理采用Clean Architecture,查询采用两层架构 | 业务逻辑 |
| 0012 | DDD战术模式 | 使用聚合、实体、值对象、领域事件等构建富领域模型 | 领域层 |
| 0017 | 架构测试 | 使用NetArchTest实现自动化架构合规性检查 | 质量保障 |
从决策到实现的闭环
以ADR 0006"创建API与业务模块间的外观"为例,决策要求:
// 模块外观接口定义
public interface IMeetingsModule
{
Task<TResult> ExecuteCommandAsync<TResult>(ICommand<TResult> command);
Task ExecuteCommandAsync(ICommand command);
Task<TResult> ExecuteQueryAsync<TResult>(IQuery<TResult> query);
}
// API控制器中使用外观
public class MeetingsController : ControllerBase
{
private readonly IMeetingsModule _meetingsModule;
[HttpPost("")]
public async Task<IActionResult> CreateNewMeeting(CreateMeetingRequest request)
{
await _meetingsModule.ExecuteCommandAsync(new CreateMeetingCommand(
request.MeetingGroupId,
request.Title,
// ...其他参数
));
return Ok();
}
}
这种设计确保API层仅通过标准化接口与业务模块交互,符合ADR 0006中"模块封装"的决策要求。架构测试项目通过NetArchTest验证此规则:
// 确保API项目仅依赖模块的Application层
var result = Types.InAssembly(apiAssembly)
.Should()
.NotReferenceAnyTypesFrom(otherLayers)
.GetResult();
协同实践:Swagger与ADR的双向赋能
架构决策驱动的API设计
ADR 0005"创建一个REST API模块"直接影响Swagger配置策略。由于采用单一API层设计,Swagger配置集中于Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerDocumentation(); // 集中配置Swagger
services.ConfigureIdentityService();
// 其他服务配置
}
public void Configure(IApplicationBuilder app)
{
app.UseSwaggerDocumentation(); // 启用Swagger中间件
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => endpoints.MapControllers());
}
这种集中式配置使Swagger能聚合所有模块的API文档,生成统一的API门户,符合"单一API入口"的架构决策。
API文档反映架构意图
Swagger生成的API文档自然呈现ADR 0007"CQRS架构风格"的决策。以会议模块API为例:
POST /api/meetings/meetings # 命令:创建会议
PUT /api/meetings/meetings/{id} # 命令:更新会议
GET /api/meetings/meetings/{id} # 查询:获取会议详情
GET /api/meetings/meetings # 查询:获取会议列表
命令操作(POST/PUT)对应写模型,查询操作(GET)对应读模型,通过HTTP方法和URL结构清晰区分,使API消费者直观理解系统架构。
实施指南与最佳实践
Swagger文档增强技巧
- XML注释强化:为所有命令/查询对象添加详细注释
/// <summary>
/// 创建会议命令
/// </summary>
/// <param name="MeetingGroupId">会议组ID</param>
/// <param name="Title">会议标题(最多100字符)</param>
public class CreateMeetingCommand : ICommand
{
public Guid MeetingGroupId { get; }
public string Title { get; }
// ...
}
- 版本控制:当架构演进需要API变更时(如ADR更新),通过Swagger文档版本管理
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "MyMeetings API", Version = "v1" });
options.SwaggerDoc("v2", new OpenApiInfo { Title = "MyMeetings API", Version = "v2" });
});
- 安全定义:在ADR 0013"使用异常保护业务规则"指导下,为Swagger配置错误响应
options.MapType<BusinessRuleValidationException>(() => new OpenApiSchema
{
Type = "object",
Properties = new Dictionary<string, OpenApiSchema>
{
["code"] = new OpenApiSchema { Type = "string" },
["message"] = new OpenApiSchema { Type = "string" }
}
});
ADR维护与演进策略
- 决策编号规范:采用
0001-record-architecture-decisions.md编号格式,确保顺序可追溯 - 状态管理:当决策变更时,更新状态为"Superseded"并引用新决策ID
- 定期审查:结合Swagger文档的API变更频率,每季度审查相关ADR的有效性
总结与展望
通过Swagger与ADR的协同实践,该模块化单体项目实现了"活的架构文档":Swagger将代码实现转化为可交互的API文档,ADR将隐性决策显性化为可追溯的设计意图。二者共同构成模块化DDD系统的两大支柱,解决了架构一致性与开发效率的核心矛盾。
随着系统演进,建议进一步探索:
- Swagger文档与ADR的自动化关联(通过自定义OperationFilter添加ADR引用)
- 基于ADR的API版本控制策略
- 领域事件与API WebHook文档的集成
掌握这些实践,你将能够构建既灵活又可维护的模块化单体系统,在保持开发效率的同时,为未来可能的微服务拆分奠定基础。
行动指南:
- 立即检查你的项目是否有明确的架构决策记录
- 实施XML注释驱动的Swagger文档生成
- 添加架构测试确保代码实现符合设计决策
- 建立ADR与API文档的交叉引用机制
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



