# CleanDDD 开发工作流 本文档描述 Fengling 微服务项目的标准开发流程,基于 Clean Architecture 和 Domain-Driven Design 原则。 ## 开发技能栈 | 技能 | 用途 | 位置 | |------|------|------| | `cleanddd-requirements-analysis` | 需求澄清与拆解 | `~/.agents/skills/` | | `cleanddd-modeling` | 领域模型设计(聚合/命令/事件/查询) | `~/.agents/skills/` | | `cleanddd-dotnet-coding` | 代码实现(实体/命令/端点/事件) | `~/.agents/skills/` | | `cleanddd-dotnet-init` | 新项目初始化 | `~/.agents/skills/` | ## 标准开发流程 ### 阶段 1:需求分析 ``` 业务需求描述 ↓ cleanddd-requirements-analysis ↓ 结构化需求文档(标注业务实体、干系人) ``` ### 阶段 2:领域建模 ``` 结构化需求 ↓ cleanddd-modeling ↓ 领域蓝图: - 聚合根定义(含强类型ID) - 领域事件列表 - 命令与查询分离 - API 端点设计 - 仓储接口 ``` ### 阶段 3:代码实现 ``` 领域蓝图 ↓ cleanddd-dotnet-coding ↓ 项目结构: ├── Domain/ │ ├── AggregatesModel/{Aggregate}/ │ │ ├── {Aggregate}.cs # 聚合根 │ │ ├── {Aggregate}Id.cs # 强类型ID │ │ └── Events/ # 领域事件 │ └── DomainEvents/ ├── Infrastructure/ │ ├── EntityConfigurations/ # EF 配置 │ └── Repositories/ # 仓储实现 └── Web/ ├── Application/ │ ├── Commands/ # CQRS 命令 │ ├── Queries/ # CQRS 查询 │ └── DomainEventHandlers/ # 领域事件处理器 └── Endpoints/ # API 端点 ``` ## 项目结构规范 ### CleanDDD 标准结构 ``` {ServiceName}/ ├── src/ │ ├── {ServiceName}.Domain/ # 领域层(无依赖) │ │ ├── AggregatesModel/ │ │ │ └── {Aggregate}/ │ │ │ ├── {Aggregate}.cs │ │ │ ├── {Aggregate}Id.cs │ │ │ └── Events/ │ │ └── DomainEvents/ │ │ │ ├── {ServiceName}.Infrastructure/ # 基础设施层 │ │ ├── EntityConfigurations/ │ │ ├── Repositories/ │ │ └── {ServiceName}DbContext.cs │ │ │ └── {ServiceName}.Web/ # 表现层 │ ├── Application/ │ │ ├── Commands/ │ │ │ └── {Feature}/ │ │ │ ├── {Command}.cs │ │ │ └── {Command}Handler.cs │ │ ├── Queries/ │ │ └── DomainEventHandlers/ │ └── Endpoints/ │ └── {Feature}Endpoints.cs │ └── tests/ # 测试项目 ├── {ServiceName}.Domain.UnitTests/ ├── {ServiceName}.Infrastructure.UnitTests/ └── {ServiceName}.Web.UnitTests/ ``` ### 命名约定 | 类型 | 命名格式 | 示例 | |------|----------|------| | 聚合根 | `{名词}` | `Campaign`, `Order` | | 强类型ID | `{名词}Id` | `CampaignId`, `OrderId` | | 领域事件 | `{名词}{动词}edEvent` | `CampaignCreatedEvent` | | 命令 | `{动词}{名词}Command` | `CreateCampaignCommand` | | 查询 | `Get{名词}Query` | `GetCampaignQuery` | | 端点 | `{名词}Endpoints` | `CampaignEndpoints` | | 仓储 | `I{名词}Repository` | `ICampaignRepository` | ## 代码规范 ### 必须遵守 1. **强类型 ID**:所有实体使用 `IInt64StronglyTypedId` 或 `IGuidStronglyTypedId` 2. **聚合根**:继承 `Entity`,实现 `IAggregateRoot` 3. **领域事件**:在状态变更时发布 `this.AddDomainEvent()` 4. **仓储**:使用异步方法(`GetAsync`, `AddAsync`),禁止在 Handler 中调用 `SaveChanges` 5. **命令验证**:每个命令必须有对应的 `AbstractValidator` 6. **端点配置**:使用属性配置 `[HttpPost]`, `[Tags]`,不使用 `Configure()` 方法 ### 禁止事项 - ❌ 手动赋值实体 ID(使用 EF 值生成器) - ❌ 在领域层引用基础设施层 - ❌ 在非聚合/实体中发布领域事件 - ❌ 仓储同步方法 - ❌ 命令 Handler 中调用 `SaveChanges` ## 网关架构说明 ### 配置流向 ``` K8s Service (app-router-* label) ↓ Console (K8sServiceWatchService) ↓ PendingConfigCache (内存缓存) ↓ PendingConfigController (用户确认) ↓ PostgreSQL (GwRoutes, ServiceInstances) ↓ PostgreSQL NOTIFY (gateway_config_changed) ↓ Gateway (PgSqlConfigChangeListener) ↓ YARP DynamicProxyConfigProvider ↓ TenantRoutingTransform (按 TenantCode 路由) ↓ 后端服务 ``` ### 多租户路由 | 路由类型 | URL 模式 | 说明 | |----------|----------|------| | 全局路由 | `/{ServicePrefix}/{Version}/{Resource}` | 所有租户共享 | | 租户路由 | `/tenant/{tenantCode}/{ServicePrefix}/{Version}/{Resource}` | 租户专属目标 | ### 关键实体 - **GwRoute**:全局路由配置(Path → Cluster) - **GwCluster**:服务集群(含 Destinations 列表) - **GwDestination**:目标端点(含 TenantCode 用于租户路由) ## 故障排除 | 问题 | 原因 | 解决 | |------|------|------| | `bigint = character varying` | 表结构过时 | 见 `DATABASE_SCHEMA_FIX.md` | | `libgssapi_krb5.so.2` 警告 | Alpine 缺少 Kerberos 库 | 可忽略,或设 `Logging__LogLevel__Npgsql=Error` | | Watch 连接断开不恢复 | HTTP/2 连接超时 | 已修复:onClosed 回调触发重连 | | Config 未生效 | NOTIFY 未触发 | 检查 `LISTEN config_changes;` | ## 相关文档 - `DATABASE_SCHEMA_FIX.md` - 数据库表结构修复步骤 - `GATEWAY_ADMIN_README.md` - Gateway Admin UI 开发指南 - `../../AGENTS.md` - 项目级架构概览