1306 lines
43 KiB
Markdown
1306 lines
43 KiB
Markdown
# 🏗️ 架构文档
|
||
|
||
## 目录
|
||
|
||
- [2.1.1 需求描述](#211-需求描述)
|
||
- [项目背景](#项目背景)
|
||
- [核心需求](#核心需求)
|
||
- [功能需求](#功能需求)
|
||
- [非功能需求](#非功能需求)
|
||
- [2.1.2 业务建模](#212-业务建模)
|
||
- [业务领域](#业务领域)
|
||
- [核心实体](#核心实体)
|
||
- [业务流程](#业务流程)
|
||
- [用例场景](#用例场景)
|
||
- [2.1.3 技术建模](#213-技术建模)
|
||
- [2.1.3.1 六边形架构](#2131-六边形架构)
|
||
- [2.1.3.2 技术选型](#2132-技术选型)
|
||
|
||
---
|
||
|
||
# 2.1.1 需求描述
|
||
|
||
## 项目背景
|
||
|
||
### 问题陈述
|
||
|
||
在云原生时代,Kubernetes 已成为容器编排的事实标准,Helm 是 Kubernetes 的包管理工具。然而,当前企业在使用 Helm 进行应用部署时面临以下挑战:
|
||
|
||
1. **多集群管理复杂** - 企业通常有多个 Kubernetes 集群(开发、测试、生产),需要统一的管理界面
|
||
2. **制品仓库分散** - Helm Chart 可能分布在多个 OCI Registry(Harbor、Docker Hub、GHCR)中,难以统一浏览
|
||
3. **部署流程繁琐** - 需要手动编写 `helm install` 命令,配置复杂的 values.yaml
|
||
4. **缺乏可视化** - 命令行操作对非技术人员不友好,缺少直观的 UI
|
||
5. **版本管理困难** - 应用升级需要记住历史版本,容易出错
|
||
6. **监控信息分散** - 需要单独访问 Kubernetes Dashboard 查看应用状态
|
||
|
||
### 解决方案
|
||
|
||
OCDP Backend 提供统一的后端 API 服务,实现:
|
||
|
||
- ✅ **统一管理** - 集中管理多个 Kubernetes 集群和 OCI Registry
|
||
- ✅ **可视化部署** - 通过 API 简化 Helm Chart 的浏览和部署流程
|
||
- ✅ **版本控制** - 完整的应用生命周期管理(安装、升级、卸载)
|
||
- ✅ **实时监控** - 集成 Kubernetes API,实时获取应用状态和资源使用情况
|
||
- ✅ **安全认证** - 支持用户认证和敏感数据加密存储
|
||
|
||
---
|
||
|
||
## 核心需求
|
||
|
||
### 业务需求
|
||
|
||
| 需求 ID | 需求描述 | 优先级 |
|
||
|---------|---------|--------|
|
||
| BR-001 | 支持管理多个 Kubernetes 集群 | P0 |
|
||
| BR-002 | 支持管理多个 OCI Registry | P0 |
|
||
| BR-003 | 浏览和搜索 Helm Chart 制品 | P0 |
|
||
| BR-004 | 部署 Helm Chart 到 Kubernetes 集群 | P0 |
|
||
| BR-005 | 升级已部署的应用 | P0 |
|
||
| BR-006 | 查看应用实时状态和资源使用 | P1 |
|
||
| BR-007 | 用户认证和权限管理 | P1 |
|
||
| BR-008 | 审计日志记录 | P2 |
|
||
|
||
### 用户角色
|
||
|
||
1. **平台管理员** - 管理集群、Registry、用户
|
||
2. **开发者** - 部署和管理自己的应用
|
||
3. **运维人员** - 监控和维护应用状态
|
||
4. **访客** - 只读查看应用列表和状态
|
||
|
||
---
|
||
|
||
## 功能需求
|
||
|
||
### F1. 集群管理
|
||
|
||
**描述**: 管理多个 Kubernetes 集群的连接配置
|
||
|
||
**功能点**:
|
||
- 添加集群(配置 API Server 地址、证书)
|
||
- 查看集群列表和详情
|
||
- 测试集群连接健康状态
|
||
- 更新和删除集群配置
|
||
- 查看集群资源使用情况(CPU、内存、节点数)
|
||
|
||
**验收标准**:
|
||
- ✅ 支持证书认证和 Token 认证
|
||
- ✅ 敏感信息(证书、密钥)加密存储
|
||
- ✅ 连接失败时给出清晰的错误提示
|
||
- ✅ 支持测试连接功能
|
||
|
||
---
|
||
|
||
### F2. Registry 管理
|
||
|
||
**描述**: 管理多个 OCI Registry 的连接配置
|
||
|
||
**功能点**:
|
||
- 添加 Registry(Harbor、Docker Hub、GHCR 等)
|
||
- 配置认证信息(用户名/密码)
|
||
- 查看 Registry 列表和详情
|
||
- 测试 Registry 连接健康状态
|
||
- 更新和删除 Registry 配置
|
||
|
||
**验收标准**:
|
||
- ✅ 支持 Basic Auth 和 Bearer Token
|
||
- ✅ 密码加密存储
|
||
- ✅ 支持 HTTP/HTTPS 和自签名证书
|
||
- ✅ 连接测试返回响应时间
|
||
|
||
---
|
||
|
||
### F3. Artifact 浏览
|
||
|
||
**描述**: 浏览和搜索 OCI Registry 中的 Helm Chart
|
||
|
||
**功能点**:
|
||
- 列出 Registry 中的所有仓库
|
||
- 列出仓库中的所有制品(tags)
|
||
- 查看制品详情(大小、创建时间、annotations)
|
||
- 自动识别制品类型(Helm Chart、Docker Image)
|
||
- 获取 Helm Chart 的 values schema
|
||
|
||
**验收标准**:
|
||
- ✅ 符合 OCI Distribution Specification
|
||
- ✅ 支持 URL 编码的仓库名称(如 `charts/app`)
|
||
- ✅ 正确解析 manifest 和 config
|
||
- ✅ 计算制品总大小(包含所有 layers)
|
||
|
||
---
|
||
|
||
### F4. 应用部署
|
||
|
||
**描述**: 部署 Helm Chart 到 Kubernetes 集群
|
||
|
||
**功能点**:
|
||
- 选择集群、Registry、Chart 和版本
|
||
- 配置 values(JSON 或 YAML)
|
||
- 安装应用到指定 namespace
|
||
- 查看安装进度和状态
|
||
- 获取应用访问端点
|
||
|
||
**验收标准**:
|
||
- ✅ 支持自定义 Release 名称
|
||
- ✅ 支持 JSON 和 YAML 格式的 values
|
||
- ✅ 记录部署历史
|
||
|
||
---
|
||
|
||
### F5. 应用生命周期管理
|
||
|
||
**描述**: 管理已部署应用的完整生命周期
|
||
|
||
**功能点**:
|
||
- 查看应用列表和详情
|
||
- 升级应用到新版本
|
||
- 查看部署历史
|
||
- 卸载应用
|
||
|
||
**验收标准**:
|
||
- ✅ 升级时保留配置
|
||
- ✅ 卸载时可选保留历史
|
||
- ✅ 显示每次部署的描述信息
|
||
|
||
---
|
||
|
||
### F6. 监控和状态
|
||
|
||
**描述**: 实时监控应用和集群状态
|
||
|
||
**功能点**:
|
||
- 查看应用实时状态(Running、Failed 等)
|
||
- 查看应用资源使用(CPU、内存)
|
||
- 查看集群整体监控
|
||
- 查看节点资源使用
|
||
- 监控摘要统计
|
||
|
||
**验收标准**:
|
||
- ✅ 实时获取 Kubernetes 资源状态
|
||
- ✅ 支持 Prometheus 指标集成
|
||
- ✅ 显示 Pod 状态和事件
|
||
- ✅ 资源使用百分比显示
|
||
|
||
---
|
||
|
||
### F7. 认证和授权
|
||
|
||
**描述**: 用户身份认证和访问控制
|
||
|
||
**功能点**:
|
||
- 用户注册和登录
|
||
- JWT Token 认证
|
||
- Token 刷新机制
|
||
- 密码加密存储
|
||
|
||
**验收标准**:
|
||
- ✅ 使用 bcrypt 哈希密码
|
||
- ✅ JWT Token 有效期配置
|
||
- ✅ Refresh Token 支持
|
||
- ✅ 密码强度验证
|
||
|
||
---
|
||
|
||
## 非功能需求
|
||
|
||
### NFR1. 性能要求
|
||
|
||
| 指标 | 要求 |
|
||
|------|------|
|
||
| API 响应时间 | P95 < 500ms |
|
||
| 并发用户数 | 支持 100+ 并发 |
|
||
| 数据库连接池 | 25 个连接 |
|
||
| Helm 操作超时 | 5 分钟 |
|
||
|
||
### NFR2. 可用性
|
||
|
||
- **系统可用性**: 99.5%
|
||
- **故障恢复时间**: < 5 分钟
|
||
- **数据备份**: 每日备份
|
||
- **健康检查**: 提供 `/health` 端点
|
||
|
||
### NFR3. 安全性
|
||
|
||
- **数据加密**: AES-256 加密敏感数据
|
||
- **传输安全**: 支持 HTTPS
|
||
- **认证方式**: JWT Token
|
||
- **密码策略**: 最小长度 8 位
|
||
- **审计日志**: 记录所有操作(未来)
|
||
|
||
### NFR4. 可扩展性
|
||
|
||
- **水平扩展**: 支持多实例部署
|
||
- **数据库**: PostgreSQL 支持主从复制
|
||
- **无状态设计**: API 服务无状态
|
||
- **缓存策略**: 支持 Redis(未来)
|
||
|
||
### NFR5. 可维护性
|
||
|
||
- **代码质量**: 遵循 Go 最佳实践
|
||
- **测试覆盖**: > 70%
|
||
- **文档完整**: API 文档、架构文档、部署文档
|
||
- **日志记录**: 结构化日志
|
||
- **监控指标**: Prometheus 指标(未来)
|
||
|
||
### NFR6. 兼容性
|
||
|
||
- **Kubernetes 版本**: 1.24+
|
||
- **Helm 版本**: 3.x
|
||
- **OCI Registry**: 符合 OCI Distribution Spec
|
||
- **数据库**: PostgreSQL 15+
|
||
- **Go 版本**: 1.21+
|
||
|
||
---
|
||
|
||
# 2.1.2 业务建模
|
||
|
||
## 业务领域
|
||
|
||
OCDP Backend 属于**云原生应用管理**领域,主要涉及以下子域:
|
||
|
||
1. **制品管理域** - OCI Registry、Helm Chart、Docker Image
|
||
2. **集群管理域** - Kubernetes Cluster、Node、Namespace
|
||
3. **应用部署域** - Helm Release、Application Instance
|
||
4. **监控运维域** - Metrics、Logs、Events
|
||
|
||
---
|
||
|
||
## 核心实体
|
||
|
||
### 领域模型图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Core Domain Entities │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
|
||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ User │─────────│ Cluster │─────────│ Instance │
|
||
└──────────┘ └──────────┘ └──────────┘
|
||
│ │ │
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ Auth │ │ Health │ │ Status │
|
||
│ Token │ │ Check │ │ Resource │
|
||
└──────────┘ └──────────┘ └──────────┘
|
||
|
||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ Registry │─────────│Artifact │─────────│ Chart │
|
||
└──────────┘ └──────────┘ └──────────┘
|
||
│ │ │
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ Health │ │Repository│ │ Values │
|
||
│ Check │ │ │ │ Schema │
|
||
└──────────┘ └──────────┘ └──────────┘
|
||
```
|
||
|
||
### 实体详解
|
||
|
||
#### 1. User(用户)
|
||
|
||
**职责**: 表示系统用户,负责身份认证
|
||
|
||
**属性**:
|
||
- `ID`: 唯一标识符
|
||
- `Username`: 用户名(唯一)
|
||
- `Email`: 邮箱
|
||
- `PasswordHash`: 密码哈希(bcrypt)
|
||
- `CreatedAt`: 创建时间
|
||
- `UpdatedAt`: 更新时间
|
||
|
||
**业务规则**:
|
||
- 用户名唯一
|
||
- 邮箱格式验证
|
||
- 密码最小长度 8 位
|
||
|
||
---
|
||
|
||
#### 2. Cluster(集群)
|
||
|
||
**职责**: 表示 Kubernetes 集群连接配置
|
||
|
||
**属性**:
|
||
- `ID`: 唯一标识符
|
||
- `Name`: 集群名称
|
||
- `Host`: API Server 地址
|
||
- `Description`: 描述
|
||
- `CAData`: CA 证书(加密存储)
|
||
- `CertData`: 客户端证书(加密存储)
|
||
- `KeyData`: 客户端密钥(加密存储)
|
||
- `Token`: Bearer Token(可选)
|
||
- `CreatedAt`: 创建时间
|
||
- `UpdatedAt`: 更新时间
|
||
|
||
**业务规则**:
|
||
- 集群名称唯一
|
||
- 必须提供证书或 Token
|
||
- Host 必须是有效的 HTTPS URL
|
||
|
||
---
|
||
|
||
#### 3. Registry(镜像仓库)
|
||
|
||
**职责**: 表示 OCI Registry 连接配置
|
||
|
||
**属性**:
|
||
- `ID`: 唯一标识符
|
||
- `Name`: Registry 名称
|
||
- `URL`: Registry URL
|
||
- `Description`: 描述
|
||
- `Username`: 用户名
|
||
- `Password`: 密码(加密存储)
|
||
- `Insecure`: 是否跳过 SSL 验证
|
||
- `CreatedAt`: 创建时间
|
||
- `UpdatedAt`: 更新时间
|
||
|
||
**业务规则**:
|
||
- Registry 名称唯一
|
||
- URL 必须是有效的 HTTP/HTTPS URL
|
||
- 密码加密存储
|
||
|
||
---
|
||
|
||
#### 4. Instance(应用实例)
|
||
|
||
**职责**: 表示部署在 Kubernetes 中的 Helm Release
|
||
|
||
**属性**:
|
||
- `ID`: 唯一标识符
|
||
- `Name`: Release 名称
|
||
- `Namespace`: Kubernetes namespace
|
||
- `ClusterID`: 所属集群
|
||
- `RegistryID`: Chart 来源 Registry
|
||
- `Repository`: Chart 仓库名
|
||
- `Chart`: Chart 名称
|
||
- `Version`: Chart 版本
|
||
- `Status`: 部署状态
|
||
- `Revision`: 当前版本号
|
||
- `Values`: 配置值(JSON)
|
||
- `Description`: 描述
|
||
- `CreatedAt`: 创建时间
|
||
- `UpdatedAt`: 更新时间
|
||
|
||
**业务规则**:
|
||
- 同一集群和 namespace 下 Release 名称唯一
|
||
- 必须关联有效的 Cluster 和 Registry
|
||
- Values 必须是有效的 JSON 或 YAML
|
||
|
||
---
|
||
|
||
#### 5. Artifact(制品)
|
||
|
||
**职责**: 表示 OCI Registry 中的制品(Helm Chart、Docker Image 等)
|
||
|
||
**属性**:
|
||
- `RepositoryName`: 仓库名称
|
||
- `Tag`: 标签
|
||
- `Digest`: SHA256 摘要
|
||
- `Type`: 制品类型(helm、docker、oci)
|
||
- `Size`: 总大小
|
||
- `MediaType`: 媒体类型
|
||
- `Annotations`: 元数据
|
||
- `CreatedAt`: 创建时间
|
||
|
||
**业务规则**:
|
||
- Tag 或 Digest 至少提供一个
|
||
- 自动识别制品类型
|
||
- 计算所有 layers 的总大小
|
||
|
||
---
|
||
|
||
## 业务流程
|
||
|
||
### 流程 1: 用户注册和登录
|
||
|
||
```
|
||
┌──────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ User │ │ Handler │ │ Service │ │ Repo │
|
||
└──┬───┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
|
||
│ │ │ │
|
||
│ Register │ │ │
|
||
├────────────>│ │ │
|
||
│ │ Create User │ │
|
||
│ ├──────────────>│ │
|
||
│ │ │ Hash Password │
|
||
│ │ ├──────┐ │
|
||
│ │ │<─────┘ │
|
||
│ │ │ Save User │
|
||
│ │ ├──────────────>│
|
||
│ │ │<──────────────┤
|
||
│ │<──────────────┤ │
|
||
│<────────────┤ │ │
|
||
│ │ │ │
|
||
│ Login │ │ │
|
||
├────────────>│ │ │
|
||
│ │ Authenticate │ │
|
||
│ ├──────────────>│ │
|
||
│ │ │ Verify Pwd │
|
||
│ │ ├──────┐ │
|
||
│ │ │<─────┘ │
|
||
│ │ │ Gen JWT │
|
||
│ │ ├──────┐ │
|
||
│ │ │<─────┘ │
|
||
│ │<──────────────┤ │
|
||
│<────────────┤ Return Token │ │
|
||
│ │ │ │
|
||
```
|
||
|
||
---
|
||
|
||
### 流程 2: 部署应用
|
||
|
||
```
|
||
┌──────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ User │ │ Handler │ │ Service │ │HelmClient│ │Kubernetes│
|
||
└──┬───┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
|
||
│ │ │ │ │
|
||
│ Deploy App │ │ │ │
|
||
├────────────>│ │ │ │
|
||
│ │ Create Inst │ │ │
|
||
│ ├──────────────>│ │ │
|
||
│ │ │ Validate │ │
|
||
│ │ ├──────┐ │ │
|
||
│ │ │<─────┘ │ │
|
||
│ │ │ Pull Chart │ │
|
||
│ │ ├──────────────>│ │
|
||
│ │ │<──────────────┤ │
|
||
│ │ │ Install Chart │ │
|
||
│ │ ├──────────────>│ │
|
||
│ │ │ │ Apply K8s │
|
||
│ │ │ ├──────────────>│
|
||
│ │ │ │<──────────────┤
|
||
│ │ │<──────────────┤ │
|
||
│ │ │ Save Instance │ │
|
||
│ │ ├──────┐ │ │
|
||
│ │ │<─────┘ │ │
|
||
│ │<──────────────┤ │ │
|
||
│<────────────┤ Return Status │ │ │
|
||
│ │ │ │ │
|
||
```
|
||
|
||
---
|
||
|
||
### 流程 3: 浏览制品
|
||
|
||
```
|
||
┌──────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
|
||
│ User │ │ Handler │ │ Service │ │OCIClient │ │ Registry │
|
||
└──┬───┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘
|
||
│ │ │ │ │
|
||
│List Repos │ │ │ │
|
||
├────────────>│ │ │ │
|
||
│ │ Get Repos │ │ │
|
||
│ ├──────────────>│ │ │
|
||
│ │ │ List Catalog │ │
|
||
│ │ ├──────────────>│ │
|
||
│ │ │ │ GET _catalog │
|
||
│ │ │ ├──────────────>│
|
||
│ │ │ │<──────────────┤
|
||
│ │ │<──────────────┤ │
|
||
│ │<──────────────┤ │ │
|
||
│<────────────┤ │ │ │
|
||
│ │ │ │ │
|
||
│List Tags │ │ │ │
|
||
├────────────>│ │ │ │
|
||
│ │ Get Artifacts │ │ │
|
||
│ ├──────────────>│ │ │
|
||
│ │ │ List Tags │ │
|
||
│ │ ├──────────────>│ │
|
||
│ │ │ │ GET tags/list │
|
||
│ │ │ ├──────────────>│
|
||
│ │ │ │<──────────────┤
|
||
│ │ │ Get Manifest │ │
|
||
│ │ ├──────────────>│ │
|
||
│ │ │ │ GET manifest │
|
||
│ │ │ ├──────────────>│
|
||
│ │ │ │<──────────────┤
|
||
│ │ │<──────────────┤ │
|
||
│ │<──────────────┤ │ │
|
||
│<────────────┤ │ │ │
|
||
│ │ │ │ │
|
||
```
|
||
|
||
---
|
||
|
||
## 用例场景
|
||
|
||
### UC1: 开发者部署测试应用
|
||
|
||
**主角**: 开发者 Alice
|
||
|
||
**前置条件**:
|
||
- Alice 已登录系统
|
||
- 系统中已配置开发环境集群
|
||
- 系统中已配置 Harbor Registry
|
||
|
||
**基本流程**:
|
||
1. Alice 选择"开发集群"
|
||
2. Alice 浏览 Harbor 中的"charts/nginx"仓库
|
||
3. Alice 选择 nginx 1.0.0 版本
|
||
4. Alice 配置 values: `{"replicaCount": 2}`
|
||
5. Alice 点击"部署"
|
||
6. 系统显示部署进度
|
||
7. 部署成功,显示应用访问地址
|
||
|
||
**后置条件**:
|
||
- nginx 应用成功部署到开发集群
|
||
- 应用状态为 "deployed"
|
||
- Alice 可以访问应用
|
||
|
||
**异常流程**:
|
||
- 3a. Chart 版本不存在 → 显示错误提示
|
||
- 5a. 部署失败 → 显示错误日志
|
||
|
||
---
|
||
|
||
### UC2: 运维人员升级生产应用
|
||
|
||
**主角**: 运维 Bob
|
||
|
||
**前置条件**:
|
||
- Bob 已登录系统
|
||
- 生产环境有运行中的 nginx 应用(版本 1.0.0)
|
||
|
||
**基本流程**:
|
||
1. Bob 进入"生产集群"应用列表
|
||
2. Bob 选择 nginx 应用
|
||
3. Bob 查看当前版本和配置
|
||
4. Bob 点击"升级"
|
||
5. Bob 选择新版本 1.1.0
|
||
6. Bob 更新配置: `{"replicaCount": 3}`
|
||
7. Bob 添加升级说明
|
||
8. Bob 确认升级
|
||
9. 系统执行滚动升级
|
||
10. 升级成功,Revision 增加到 2
|
||
|
||
**后置条件**:
|
||
- nginx 应用升级到 1.1.0
|
||
- 副本数增加到 3
|
||
- 历史记录中保留了 Revision 1
|
||
|
||
**异常流程**:
|
||
- 9a. 升级失败 → 显示错误信息,保持原状态
|
||
- 9b. 超时 → 取消升级,保持原状态
|
||
|
||
---
|
||
|
||
### UC3: 管理员添加新集群
|
||
|
||
**主角**: 管理员 Charlie
|
||
|
||
**前置条件**:
|
||
- Charlie 已登录系统
|
||
- Charlie 有集群的 kubeconfig 文件
|
||
|
||
**基本流程**:
|
||
1. Charlie 进入"集群管理"页面
|
||
2. Charlie 点击"添加集群"
|
||
3. Charlie 填写集群信息:
|
||
- 名称: "Production Cluster"
|
||
- API Server: "https://k8s.prod.com:6443"
|
||
- 描述: "生产环境集群"
|
||
4. Charlie 从 kubeconfig 提取证书数据
|
||
5. Charlie 粘贴 CA、Cert、Key 数据
|
||
6. Charlie 点击"测试连接"
|
||
7. 系统显示"连接成功"
|
||
8. Charlie 保存配置
|
||
|
||
**后置条件**:
|
||
- 新集群添加到系统
|
||
- 集群证书加密存储
|
||
- 其他用户可以使用此集群
|
||
|
||
**异常流程**:
|
||
- 6a. 连接失败 → 显示具体错误信息
|
||
- 6b. 证书格式错误 → 提示正确的格式
|
||
|
||
---
|
||
|
||
# 2.1.3 技术建模
|
||
|
||
## 2.1.3.1 六边形架构
|
||
|
||
### 架构概述
|
||
|
||
OCDP Backend 采用**六边形架构**(Hexagonal Architecture,也称为端口和适配器架构),这是一种分层架构模式,强调业务逻辑与外部依赖的分离。
|
||
|
||
### 核心原则
|
||
|
||
1. **依赖倒置** - 所有层依赖 Domain,Domain 无外部依赖
|
||
2. **端口和适配器** - 通过接口(Port)定义交互协议,通过适配器(Adapter)实现具体技术
|
||
3. **可测试性** - 业务逻辑可独立测试,无需外部依赖
|
||
4. **可替换性** - 适配器可轻松替换(Mock ↔ Production)
|
||
|
||
### 架构分层图
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Input Adapters │
|
||
│ (HTTP REST API) │
|
||
│ │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ Auth Handler │ │Cluster Handl │ │Instance Handl│ │
|
||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||
│ │
|
||
└──────────────────────┬──────────────────────────────────────┘
|
||
│ DTO / Request
|
||
↓
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Domain Layer │
|
||
│ (Business Logic) │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────────────┐ │
|
||
│ │ Entities │ │
|
||
│ │ User | Cluster | Registry | Instance | Artifact │ │
|
||
│ └──────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────────────┐ │
|
||
│ │ Services │ │
|
||
│ │ AuthService | ClusterService | InstanceService │ │
|
||
│ └──────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────────────┐ │
|
||
│ │ Repository Interfaces (Ports) │ │
|
||
│ │ UserRepo | ClusterRepo | OCIClient | HelmClient │ │
|
||
│ └──────────────────────────────────────────────────────┘ │
|
||
│ │
|
||
└──────────────────────┬──────────────────────────────────────┘
|
||
│ Interface Contract
|
||
↓
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ Output Adapters │
|
||
│ (Infrastructure Implementations) │
|
||
│ │
|
||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||
│ │ Database │ │ OCI Client │ │ Helm Client │ │
|
||
│ │ │ │ │ │ │ │
|
||
│ ├──────────────┤ ├──────────────┤ ├──────────────┤ │
|
||
│ │ Mock: Memory │ │ Mock: Static │ │ Mock: Fake │ │
|
||
│ │ Prod:Postgres│ │ Prod: ORAS │ │ Prod: Helm │ │
|
||
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 目录结构
|
||
|
||
```
|
||
internal/
|
||
├── domain/ # 🎯 领域层(核心)
|
||
│ ├── entity/ # 领域实体
|
||
│ │ ├── user.go
|
||
│ │ ├── cluster.go
|
||
│ │ ├── registry.go
|
||
│ │ ├── instance.go
|
||
│ │ └── artifact.go
|
||
│ │
|
||
│ ├── service/ # 业务逻辑服务
|
||
│ │ ├── auth_service.go
|
||
│ │ ├── cluster_service.go
|
||
│ │ ├── registry_service.go
|
||
│ │ ├── artifact_service.go
|
||
│ │ ├── instance_service.go
|
||
│ │ └── monitoring_service.go
|
||
│ │
|
||
│ └── repository/ # 接口定义(Output Ports)
|
||
│ ├── user_repository.go
|
||
│ ├── cluster_repository.go
|
||
│ ├── registry_repository.go
|
||
│ ├── instance_repository.go
|
||
│ ├── oci_client.go
|
||
│ ├── helm_client.go
|
||
│ └── metrics_client.go
|
||
│
|
||
├── adapter/
|
||
│ ├── input/ # 📥 输入适配器
|
||
│ │ └── http/
|
||
│ │ ├── rest/ # REST API Handlers
|
||
│ │ │ ├── auth_handler.go
|
||
│ │ │ ├── cluster_handler.go
|
||
│ │ │ ├── registry_handler.go
|
||
│ │ │ ├── artifact_handler.go
|
||
│ │ │ ├── instance_handler.go
|
||
│ │ │ ├── monitoring_handler.go
|
||
│ │ │ └── utils.go
|
||
│ │ │
|
||
│ │ └── dto/ # 数据传输对象
|
||
│ │ ├── auth_dto.go
|
||
│ │ ├── cluster_dto.go
|
||
│ │ ├── registry_dto.go
|
||
│ │ └── instance_dto.go
|
||
│ │
|
||
│ └── output/ # 📤 输出适配器
|
||
│ ├── persistence/
|
||
│ │ ├── mock/ # ✅ Mock 实现
|
||
│ │ │ ├── user_repository_mock.go
|
||
│ │ │ ├── cluster_repository_mock.go
|
||
│ │ │ ├── registry_repository_mock.go
|
||
│ │ │ └── instance_repository_mock.go
|
||
│ │ │
|
||
│ │ └── postgres/ # 🐘 PostgreSQL 实现
|
||
│ │ ├── user_repository.go
|
||
│ │ ├── cluster_repository.go
|
||
│ │ ├── registry_repository.go
|
||
│ │ └── instance_repository.go
|
||
│ │
|
||
│ ├── oci/
|
||
│ │ ├── mock/ # ✅ Mock OCI Client
|
||
│ │ │ └── oci_client_mock.go
|
||
│ │ └── oras_client.go # ORAS SDK 实现
|
||
│ │
|
||
│ ├── helm/
|
||
│ │ ├── mock/ # ✅ Mock Helm Client
|
||
│ │ │ └── helm_client_mock.go
|
||
│ │ └── helm_client.go # Helm SDK 实现
|
||
│ │
|
||
│ ├── metrics/
|
||
│ │ ├── mock/ # ✅ Mock Metrics Client
|
||
│ │ │ └── metrics_client_mock.go
|
||
│ │ └── prometheus_client.go
|
||
│ │
|
||
│ └── factory.go # 🏭 适配器工厂
|
||
│
|
||
├── bootstrap/ # Bootstrap 预注入模块
|
||
│ ├── config.go
|
||
│ └── seeder.go
|
||
│
|
||
└── pkg/ # 🔧 工具包
|
||
├── jwt/ # JWT 工具
|
||
├── password/ # 密码哈希
|
||
└── encryption/ # AES 加密
|
||
```
|
||
|
||
### 数据流
|
||
|
||
```
|
||
1. HTTP Request
|
||
↓
|
||
2. [REST Handler] (Input Adapter)
|
||
- 验证请求参数
|
||
- 转换为 Domain 对象(Entity)
|
||
↓
|
||
3. [Domain Service] (Business Logic)
|
||
- 执行业务逻辑
|
||
- 调用 Repository 接口(Port)
|
||
↓
|
||
4. [Repository Implementation] (Output Adapter)
|
||
- Mock: 操作内存数据
|
||
- PostgreSQL: 操作数据库
|
||
- ORAS: 与 OCI Registry 交互
|
||
- Helm: 与 Kubernetes 交互
|
||
↓
|
||
5. Response
|
||
- 返回结果到 Service
|
||
- Service 返回到 Handler
|
||
- Handler 转换为 HTTP 响应
|
||
```
|
||
|
||
### 依赖注入
|
||
|
||
在 `cmd/api/main.go` 中组装所有组件:
|
||
|
||
```go
|
||
func main() {
|
||
// 1. 加载配置
|
||
config := loadConfig()
|
||
|
||
// 2. 创建适配器工厂
|
||
factory := output.NewAdapterFactory(
|
||
config.AdapterMode,
|
||
config.DatabaseURL,
|
||
)
|
||
|
||
// 3. 创建 Output Adapters
|
||
repos, _ := factory.CreateAllRepositories()
|
||
ociClient, _ := factory.CreateOCIClient()
|
||
helmClient, _ := factory.CreateHelmClient()
|
||
|
||
// 4. 创建工具类
|
||
hasher := password.NewBcryptHasher()
|
||
jwtGen := jwt.NewJWTGenerator(config.JWTSecret)
|
||
|
||
// 5. 创建 Domain Services
|
||
authService := service.NewAuthService(repos.UserRepo, hasher, jwtGen)
|
||
clusterService := service.NewClusterService(repos.ClusterRepo)
|
||
registryService := service.NewRegistryService(repos.RegistryRepo)
|
||
instanceService := service.NewInstanceService(
|
||
repos.InstanceRepo,
|
||
repos.ClusterRepo,
|
||
repos.RegistryRepo,
|
||
helmClient,
|
||
)
|
||
|
||
// 6. 创建 Input Adapters (REST Handlers)
|
||
authHandler := rest.NewAuthHandler(authService)
|
||
clusterHandler := rest.NewClusterHandler(clusterService)
|
||
instanceHandler := rest.NewInstanceHandler(instanceService)
|
||
|
||
// 7. 设置路由
|
||
router := setupRouter(
|
||
authHandler,
|
||
clusterHandler,
|
||
instanceHandler,
|
||
)
|
||
|
||
// 8. 启动服务器
|
||
http.ListenAndServe(":8080", router)
|
||
}
|
||
```
|
||
|
||
### 适配器模式
|
||
|
||
#### 适配器工厂
|
||
|
||
```go
|
||
// internal/adapter/output/factory.go
|
||
type AdapterMode string
|
||
|
||
const (
|
||
ModeMock AdapterMode = "mock"
|
||
ModeProduction AdapterMode = "production"
|
||
)
|
||
|
||
type AdapterFactory struct {
|
||
mode AdapterMode
|
||
dbConnString string
|
||
}
|
||
|
||
func (f *AdapterFactory) CreateUserRepository() (repository.UserRepository, error) {
|
||
switch f.mode {
|
||
case ModeMock:
|
||
return mock.NewUserRepositoryMock(), nil
|
||
case ModeProduction:
|
||
return postgres.NewUserRepository(f.dbConnString)
|
||
default:
|
||
return nil, fmt.Errorf("unknown adapter mode: %s", f.mode)
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Mock vs Production
|
||
|
||
| 接口 | Mock 模式 | Production 模式 |
|
||
|------|----------|----------------|
|
||
| UserRepository | ✅ 内存 map | 🐘 PostgreSQL |
|
||
| ClusterRepository | ✅ 内存 map | 🐘 PostgreSQL |
|
||
| RegistryRepository | ✅ 内存 map | 🐘 PostgreSQL |
|
||
| InstanceRepository | ✅ 内存 map | 🐘 PostgreSQL |
|
||
| OCIClient | ✅ 静态数据 | 🌐 ORAS SDK v2 |
|
||
| HelmClient | ✅ 模拟部署 | ☸️ Helm SDK |
|
||
| MetricsClient | ✅ 假数据 | 📊 Prometheus |
|
||
|
||
---
|
||
|
||
## 2.1.3.2 技术选型
|
||
|
||
### 编程语言
|
||
|
||
**Go 1.21+**
|
||
|
||
**选型理由**:
|
||
- ✅ 高性能,原生并发支持
|
||
- ✅ 静态类型,编译时检查
|
||
- ✅ 丰富的云原生生态(Kubernetes client-go、Helm SDK、ORAS)
|
||
- ✅ 简单易维护,部署方便(单一二进制文件)
|
||
- ✅ 广泛应用于云原生领域
|
||
|
||
**替代方案**: Java/Spring Boot, Python/FastAPI
|
||
|
||
---
|
||
|
||
### Web 框架
|
||
|
||
**gorilla/mux**
|
||
|
||
**选型理由**:
|
||
- ✅ 轻量级,性能好
|
||
- ✅ 灵活的路由匹配(支持正则表达式)
|
||
- ✅ 与标准库 `net/http` 完美兼容
|
||
- ✅ 活跃的社区支持
|
||
|
||
**使用示例**:
|
||
```go
|
||
router := mux.NewRouter()
|
||
api := router.PathPrefix("/api/v1").Subrouter()
|
||
api.HandleFunc("/clusters", handler.CreateCluster).Methods(http.MethodPost)
|
||
api.HandleFunc("/clusters/{clusterId}", handler.GetCluster).Methods(http.MethodGet)
|
||
```
|
||
|
||
**替代方案**: Gin, Echo, Fiber
|
||
|
||
---
|
||
|
||
### 数据库
|
||
|
||
**PostgreSQL 15+**
|
||
|
||
**选型理由**:
|
||
- ✅ 开源、成熟、可靠
|
||
- ✅ 支持复杂查询和事务
|
||
- ✅ JSONB 类型适合存储动态配置
|
||
- ✅ 主从复制、高可用支持
|
||
- ✅ 与 Go 生态集成良好
|
||
|
||
**数据库驱动**: `lib/pq` 或 `pgx`
|
||
|
||
**替代方案**: MySQL, MongoDB
|
||
|
||
---
|
||
|
||
### OCI Registry 客户端
|
||
|
||
**ORAS Go SDK v2**
|
||
|
||
**选型理由**:
|
||
- ✅ 符合 OCI Distribution Specification
|
||
- ✅ 官方维护,质量可靠
|
||
- ✅ 支持所有 OCI 兼容的 Registry
|
||
- ✅ 完整的 manifest、blob 操作
|
||
|
||
**使用示例**:
|
||
```go
|
||
repo, err := remote.NewRepository("harbor.example.com/charts/nginx")
|
||
repo.Client = &auth.Client{
|
||
Credential: auth.StaticCredential("username", "password"),
|
||
}
|
||
|
||
tags, err := repo.Tags(ctx)
|
||
manifest, err := repo.FetchReference(ctx, "1.0.0")
|
||
```
|
||
|
||
**官网**: https://oras.land/
|
||
|
||
**替代方案**: containerd, go-containerregistry
|
||
|
||
---
|
||
|
||
### Kubernetes 客户端
|
||
|
||
**client-go**
|
||
|
||
**选型理由**:
|
||
- ✅ Kubernetes 官方 Go 客户端
|
||
- ✅ 完整的 API 支持
|
||
- ✅ 动态客户端支持
|
||
- ✅ 与 Helm SDK 集成
|
||
|
||
**使用示例**:
|
||
```go
|
||
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
|
||
clientset, err := kubernetes.NewForConfig(config)
|
||
|
||
pods, err := clientset.CoreV1().Pods("default").List(ctx, metav1.ListOptions{})
|
||
```
|
||
|
||
**替代方案**: 无(标准库)
|
||
|
||
---
|
||
|
||
### Helm 客户端
|
||
|
||
**Helm SDK (helm.sh/helm/v3)**
|
||
|
||
**选型理由**:
|
||
- ✅ Helm 官方 SDK
|
||
- ✅ 完整的 Helm 操作支持
|
||
- ✅ Chart 解析和渲染
|
||
- ✅ Release 生命周期管理
|
||
|
||
**使用示例**:
|
||
```go
|
||
actionConfig := new(action.Configuration)
|
||
actionConfig.Init(settings.RESTClientGetter(), namespace, os.Getenv("HELM_DRIVER"), log.Printf)
|
||
|
||
install := action.NewInstall(actionConfig)
|
||
install.ReleaseName = "my-release"
|
||
install.Namespace = "default"
|
||
|
||
chart, err := loader.Load(chartPath)
|
||
release, err := install.Run(chart, values)
|
||
```
|
||
|
||
**替代方案**: 命令行调用 helm(不推荐)
|
||
|
||
---
|
||
|
||
### 认证和授权
|
||
|
||
**JWT (golang-jwt/jwt)**
|
||
|
||
**选型理由**:
|
||
- ✅ 无状态,易扩展
|
||
- ✅ 标准化(RFC 7519)
|
||
- ✅ 支持过期时间和刷新
|
||
- ✅ 广泛应用
|
||
|
||
**使用示例**:
|
||
```go
|
||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||
"user_id": user.ID,
|
||
"exp": time.Now().Add(time.Hour * 24).Unix(),
|
||
})
|
||
|
||
tokenString, err := token.SignedString([]byte(jwtSecret))
|
||
```
|
||
|
||
**替代方案**: OAuth 2.0, Session
|
||
|
||
---
|
||
|
||
### 密码哈希
|
||
|
||
**bcrypt (golang.org/x/crypto/bcrypt)**
|
||
|
||
**选型理由**:
|
||
- ✅ 安全、抗暴力破解
|
||
- ✅ 自动加盐
|
||
- ✅ 计算成本可调
|
||
- ✅ Go 标准扩展库
|
||
|
||
**使用示例**:
|
||
```go
|
||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||
err = bcrypt.CompareHashAndPassword(hash, []byte(password))
|
||
```
|
||
|
||
**替代方案**: argon2, scrypt
|
||
|
||
---
|
||
|
||
### 数据加密
|
||
|
||
**AES-256 (crypto/aes + crypto/cipher)**
|
||
|
||
**选型理由**:
|
||
- ✅ 对称加密,性能好
|
||
- ✅ 256 位密钥,安全性高
|
||
- ✅ Go 标准库支持
|
||
- ✅ 适合敏感数据加密(证书、密码)
|
||
|
||
**使用模式**: GCM(Galois/Counter Mode)
|
||
|
||
**使用示例**:
|
||
```go
|
||
block, err := aes.NewCipher(key) // 32 bytes key
|
||
gcm, err := cipher.NewGCM(block)
|
||
nonce := make([]byte, gcm.NonceSize())
|
||
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
|
||
```
|
||
|
||
**替代方案**: RSA (非对称加密,性能较差)
|
||
|
||
---
|
||
|
||
### 容器化
|
||
|
||
**Docker + Docker Compose**
|
||
|
||
**选型理由**:
|
||
- ✅ 标准化容器技术
|
||
- ✅ 简化部署和环境一致性
|
||
- ✅ 丰富的镜像生态
|
||
- ✅ Docker Compose 简化多容器编排
|
||
|
||
**Dockerfile 示例**:
|
||
```dockerfile
|
||
FROM golang:1.21-alpine AS builder
|
||
WORKDIR /app
|
||
COPY . .
|
||
RUN go build -o ocdp-backend cmd/api/main.go
|
||
|
||
FROM alpine:latest
|
||
RUN apk --no-cache add ca-certificates
|
||
WORKDIR /root/
|
||
COPY --from=builder /app/ocdp-backend .
|
||
EXPOSE 8080
|
||
CMD ["./ocdp-backend"]
|
||
```
|
||
|
||
**替代方案**: Podman, containerd
|
||
|
||
---
|
||
|
||
### 日志记录
|
||
|
||
**标准库 log + 结构化日志(未来:zerolog/zap)**
|
||
|
||
**当前实现**:
|
||
```go
|
||
log.Printf("✅ User created: %s", user.Username)
|
||
log.Printf("⚠️ Warning: %v", err)
|
||
log.Printf("❌ Error: %v", err)
|
||
```
|
||
|
||
**未来优化**:
|
||
```go
|
||
logger.Info().
|
||
Str("user_id", user.ID).
|
||
Str("username", user.Username).
|
||
Msg("User created")
|
||
```
|
||
|
||
---
|
||
|
||
### 测试框架
|
||
|
||
**testify**
|
||
|
||
**选型理由**:
|
||
- ✅ 丰富的断言函数
|
||
- ✅ Mock 支持
|
||
- ✅ 测试套件支持
|
||
- ✅ 活跃的社区
|
||
|
||
**使用示例**:
|
||
```go
|
||
import (
|
||
"testing"
|
||
"github.com/stretchr/testify/assert"
|
||
"github.com/stretchr/testify/require"
|
||
)
|
||
|
||
func TestCreateUser(t *testing.T) {
|
||
user, err := service.CreateUser(ctx, "test", "password")
|
||
require.NoError(t, err)
|
||
assert.NotEmpty(t, user.ID)
|
||
assert.Equal(t, "test", user.Username)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 热重载(开发)
|
||
|
||
**Air**
|
||
|
||
**选型理由**:
|
||
- ✅ Go 项目热重载
|
||
- ✅ 监听文件变化自动重启
|
||
- ✅ 配置简单
|
||
|
||
**配置文件**: `.air.toml`
|
||
|
||
```toml
|
||
[build]
|
||
cmd = "go build -o ./tmp/main cmd/api/main.go"
|
||
bin = "./tmp/main"
|
||
include_ext = ["go", "yaml", "json"]
|
||
exclude_dir = ["tmp", "vendor"]
|
||
```
|
||
|
||
**启动**: `air -c .air.toml`
|
||
|
||
---
|
||
|
||
### 技术栈总结表
|
||
|
||
| 组件 | 技术 | 版本 | 用途 |
|
||
|------|------|------|------|
|
||
| **语言** | Go | 1.21+ | 主要编程语言 |
|
||
| **Web 框架** | gorilla/mux | latest | HTTP 路由 |
|
||
| **数据库** | PostgreSQL | 15+ | 持久化存储 |
|
||
| **数据库驱动** | lib/pq 或 pgx | latest | Go 数据库驱动 |
|
||
| **OCI 客户端** | ORAS Go SDK | v2 | OCI Registry 操作 |
|
||
| **Kubernetes** | client-go | latest | K8s API 交互 |
|
||
| **Helm** | Helm SDK | v3 | Helm 操作 |
|
||
| **JWT** | golang-jwt/jwt | v5 | 认证 Token |
|
||
| **密码哈希** | bcrypt | latest | 密码加密 |
|
||
| **数据加密** | AES-256 | stdlib | 敏感数据加密 |
|
||
| **容器化** | Docker | latest | 应用容器化 |
|
||
| **编排** | Docker Compose | latest | 多容器编排 |
|
||
| **测试** | testify | latest | 单元测试 |
|
||
| **热重载** | Air | latest | 开发环境 |
|
||
|
||
---
|
||
|
||
### Bootstrap 预注入
|
||
|
||
**概述**: 在应用启动时自动初始化用户、Registry、Cluster 等数据。
|
||
|
||
**配置文件**: `config/bootstrap.json`
|
||
|
||
```json
|
||
{
|
||
"enabled": true,
|
||
"users": [
|
||
{
|
||
"username": "admin",
|
||
"password": "admin123",
|
||
"email": "admin@example.com"
|
||
}
|
||
],
|
||
"registries": [
|
||
{
|
||
"name": "Harbor Production",
|
||
"url": "https://harbor.example.com",
|
||
"username": "admin",
|
||
"password": "secret"
|
||
}
|
||
],
|
||
"clusters": [
|
||
{
|
||
"name": "Production Cluster",
|
||
"host": "https://k8s.example.com:6443",
|
||
"caData": "LS0tLS...",
|
||
"certData": "LS0tLS...",
|
||
"keyData": "LS0tLS..."
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**特性**:
|
||
- ✅ 自动加密敏感数据
|
||
- ✅ 幂等性(重复启动不会重复创建)
|
||
- ✅ 可通过环境变量配置
|
||
- ✅ 支持禁用
|
||
|
||
**从 kubeconfig 提取证书**:
|
||
```bash
|
||
kubectl config view --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}'
|
||
kubectl config view --raw -o jsonpath='{.users[0].user.client-certificate-data}'
|
||
kubectl config view --raw -o jsonpath='{.users[0].user.client-key-data}'
|
||
```
|
||
|
||
---
|
||
|
||
### 开发指南
|
||
|
||
#### 添加新功能的步骤
|
||
|
||
1. **定义 Entity** (`internal/domain/entity/`)
|
||
2. **定义 Repository 接口** (`internal/domain/repository/`)
|
||
3. **实现 Domain Service** (`internal/domain/service/`)
|
||
4. **实现 Mock Adapter** (`internal/adapter/output/persistence/mock/`)
|
||
5. **实现 Production Adapter** (`internal/adapter/output/persistence/postgres/`)
|
||
6. **添加到 Factory** (`internal/adapter/output/factory.go`)
|
||
7. **创建 REST Handler** (`internal/adapter/input/http/rest/`)
|
||
8. **注册路由** (`cmd/api/main.go`)
|
||
|
||
#### 依赖方向规则
|
||
|
||
- ✅ Input Adapters → Domain Layer
|
||
- ✅ Domain Layer → Repository Interfaces
|
||
- ✅ Output Adapters → Domain Layer (实现接口)
|
||
- ❌ Domain Layer → Output Adapters(禁止)
|
||
|
||
#### 测试策略
|
||
|
||
- **单元测试**: 测试 Domain Service,使用 Mock Repository
|
||
- **集成测试**: 测试 Handler + Service,使用 Mock Adapters
|
||
- **E2E 测试**: 完整流程测试,使用真实环境
|
||
|
||
---
|
||
|
||
## 相关文档
|
||
|
||
- [API 与测试](api-and-test.md)
|
||
- [部署文档](deployment.md)
|
||
- [主 README](../README.md)
|
||
|
||
---
|
||
|
||
**Last Updated**: 2025-11-09
|