8.5 KiB
8.5 KiB
📋 OCDP 开发规范
本文档定义了 OCDP 项目的开发规范和架构要求。
🎯 整体架构 (Full Stack)
1. OpenAPI 驱动开发
采用 OpenAPI 规范来驱动前后端开发,确保 API 契约的一致性。
优势:
- API 设计优先,前后端并行开发
- 自动生成类型安全的代码
- 文档和代码永远同步
- 减少沟通成本
实践:
# 1. 设计 API (编辑 backend/docs/openapi.yaml)
# 2. 验证规范
make openapi-validate
# 3. 生成代码
make openapi-gen
# 4. 实现功能
2. Docker Compose 部署
使用 Docker Compose 进行整个应用的部署。新版的 Docker 已经将 Compose 集成到 Docker 里面了,所以使用 docker compose(带空格)而非旧版的 docker-compose(带连字符)。
部署服务:
- PostgreSQL - 数据持久化
- Redis - 缓存和会话
- Backend - Go 后端服务
- Frontend - React 前端应用
- Nginx - 反向代理(生产环境)
🎨 前端规范 (Frontend)
1. 纯函数渲染
要求:使用纯函数进行组件渲染,避免不必要的副作用。
原则:
- 组件应该是可预测的(相同输入→相同输出)
- 避免在渲染过程中修改外部状态
- 使用
useEffect等 Hook 处理副作用 - 保持组件的可测试性
示例:
// ✅ 好的实践 - 纯函数组件
interface Props {
name: string;
count: number;
}
const UserCard = ({ name, count }: Props) => {
// 纯函数:只依赖 props,不修改外部状态
return (
<div>
<h3>{name}</h3>
<p>Count: {count}</p>
</div>
);
};
// ✅ 副作用在 useEffect 中处理
const UserList = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
// 副作用(API 调用)在这里处理
fetchUsers().then(setUsers);
}, []);
return users.map(user => <UserCard {...user} />);
};
// ❌ 不好的实践 - 在渲染中产生副作用
const BadComponent = () => {
// 不要在这里调用 API 或修改外部状态
globalState.count++; // ❌ 副作用
fetchData(); // ❌ 副作用
return <div>Bad</div>;
};
2. 技术栈
- 框架: React 18+ (使用 Hooks)
- 语言: TypeScript 5+
- 构建工具: Vite
- 样式: Tailwind CSS
- 路由: React Router 6+
- 状态管理: React Context + Hooks
- API 客户端: 从 OpenAPI 自动生成
🔧 后端规范 (Backend)
1. 六边形架构 (Hexagonal Architecture)
后端采用六边形架构(也称为端口和适配器架构),将业务逻辑与技术实现解耦。
核心目录结构:
backend/internal/
├── domain/ # 领域层 - 业务逻辑核心
│ ├── entity/ # 领域实体
│ ├── service/ # 领域服务
│ └── repository/ # 仓库接口(端口)
├── application/ # 应用层 - 用例编排
│ └── usecase/ # 用例实现
└── adapter/ # 适配器层 - 技术实现
├── input/ # 输入适配器
│ └── http/ # HTTP REST API
└── output/ # 输出适配器
├── persistence/
│ ├── mock/ # Mock 实现
│ └── postgres/ # PostgreSQL 实现
├── oci/ # OCI Registry 客户端
└── helm/ # Helm SDK 封装
职责划分:
- Domain 层:纯业务逻辑,不依赖任何框架或外部库
- Application 层:编排 Domain 层的服务,实现具体的用例
- Adapter 层:处理所有技术细节(HTTP、数据库、第三方 API)
2. Mock Adapter 实现
要求:除了实现 ports 的 adapters 外,还要做 mock。Mock 的是 adapter 的行为反应而非假数据。
Mock 原则:
- ✅ 模拟真实 adapter 的行为
- ✅ 可以注入真实数据
- ✅ 可以通过调用接口自行加入数据
- ✅ 使用内存来模拟 adapter 的交互
- ❌ 不是返回固定的假数据
示例:
// Mock Repository - 模拟真实的数据库行为
type RegistryRepositoryMock struct {
registries map[string]*entity.Registry // 内存存储
mu sync.RWMutex
}
func (r *RegistryRepositoryMock) Create(ctx context.Context, registry *entity.Registry) error {
r.mu.Lock()
defer r.mu.Unlock()
// 模拟真实行为:检查重复、生成 ID、加密等
if _, exists := r.registries[registry.ID]; exists {
return errors.New("registry already exists")
}
r.registries[registry.ID] = registry
return nil
}
func (r *RegistryRepositoryMock) GetByID(ctx context.Context, id string) (*entity.Registry, error) {
r.mu.RLock()
defer r.mu.RUnlock()
registry, exists := r.registries[id]
if !exists {
return nil, errors.New("registry not found")
}
return registry, nil
}
3. Makefile 支持
要求:采用 Makefile 来支持 mock 启动以及 real 启动。
命令规范:
# 开发模式(Mock Adapter)
run-mock:
@echo "Starting backend with Mock adapters..."
MODE=mock go run cmd/api/main.go
# 生产模式(Real Adapter)
run-real:
@echo "Starting backend with Real adapters..."
MODE=real go run cmd/api/main.go
# 运行测试
test:
go test -v ./...
# 生成代码
generate:
go generate ./...
使用方式:
# 开发模式(无需数据库)
make run-mock
# 生产模式(需要 PostgreSQL)
make run-real
4. 技术栈
- 语言: Go 1.21+
- Web 框架: Gin (轻量、高性能)
- ORM: GORM (可选,用于 PostgreSQL adapter)
- OCI 客户端: ORAS Go SDK v2
- Helm 客户端: Helm SDK v3
- K8s 客户端: client-go
📐 架构原则
1. 依赖方向
Adapter → Application → Domain
(技术) (编排) (业务)
- Domain 层不依赖任何外部库(除了标准库)
- Application 层依赖 Domain 层
- Adapter 层依赖 Application 和 Domain 层
2. 端口和适配器
端口(Port):接口定义,在 Domain 层
// domain/repository/registry_repository.go
type RegistryRepository interface {
Create(ctx context.Context, registry *entity.Registry) error
GetByID(ctx context.Context, id string) (*entity.Registry, error)
List(ctx context.Context) ([]*entity.Registry, error)
}
适配器(Adapter):接口实现,在 Adapter 层
// adapter/output/persistence/mock/registry_repository_mock.go
type RegistryRepositoryMock struct {
// Mock 实现
}
// adapter/output/persistence/postgres/registry_repository_postgres.go
type RegistryRepositoryPostgres struct {
// PostgreSQL 实现
}
3. 依赖注入
使用构造函数注入依赖:
// 创建 Mock 模式的应用
func NewMockApp() *App {
// 创建 Mock Repository
registryRepo := mock.NewRegistryRepositoryMock()
// 创建 Service(注入 Repository)
registryService := service.NewRegistryService(registryRepo)
// 创建 Handler(注入 Service)
registryHandler := handler.NewRegistryHandler(registryService)
return &App{
RegistryHandler: registryHandler,
}
}
// 创建 Production 模式的应用
func NewProductionApp(db *gorm.DB) *App {
// 创建 PostgreSQL Repository
registryRepo := postgres.NewRegistryRepositoryPostgres(db)
// ... 其他相同
}
🔄 开发工作流
1. 功能开发流程
# 1. 设计 API
vim backend/docs/openapi.yaml
# 2. 生成代码
make openapi-gen
# 3. 实现 Domain 层
vim backend/internal/domain/service/xxx_service.go
# 4. 实现 Mock Adapter
vim backend/internal/adapter/output/persistence/mock/xxx_mock.go
# 5. 实现 Handler
vim backend/internal/adapter/input/http/handler/xxx_handler.go
# 6. 启动测试
make run-mock
# 7. 实现前端
vim frontend/src/features/xxx/pages/XxxPage.tsx
# 8. 集成测试
make dev
# 9. 实现 Production Adapter
vim backend/internal/adapter/output/persistence/postgres/xxx_postgres.go
# 10. 部署测试
docker compose up
2. 测试策略
- 单元测试:Domain 层和 Service 层(使用 Mock Repository)
- 集成测试:使用 Mock Adapter 测试完整流程
- E2E 测试:使用真实 Adapter 测试生产环境
📚 参考文档
版本: 1.0
最后更新: 2025-11-07