所属阶段:第四阶段「语言与框架」(第 17-22 课) 前置条件:第 21 课(API 设计) 本课收获:理解 ECC 支持的架构模式,能写一个 ADR
一、本课概述
前面几课我们学习了语言、框架、数据库和 API 设计。这些都是"怎么做"的问题。架构关注的是更高层的问题 --- "为什么这样做"和"系统的边界在哪里"。
ECC 提供了三个架构相关的 Skill 和一个专用 Agent,帮助你做出有据可查的架构决策。
本课回答三个问题:
- 六边形架构是什么? --- Port、Adapter、领域边界
- ADR 如何写? --- 记录决策的标准模板
- architect Agent 如何辅助决策? --- 深度推理 + 多视角分析
二、架构 Skill 全景
2.1 完整 Skill 清单
| Skill | 定位 | 核心内容 |
|---|---|---|
hexagonal-architecture |
架构模式 | Ports & Adapters、领域边界、依赖反转 |
architecture-decision-records |
决策管理 | ADR 格式、决策追踪、状态管理 |
codebase-onboarding |
架构理解 | 架构地图、入口点、依赖图、新人引导 |
2.2 Skill 关系图
markdown
architect Agent(Opus 模型深度推理)
│
├── hexagonal-architecture
│ (系统怎么分层?边界在哪里?)
│
├── architecture-decision-records
│ (为什么这样决定?决策记录在哪?)
│
└── codebase-onboarding
(新人如何理解这个系统?)
这三个 Skill 形成了架构工作的完整闭环:设计架构 → 记录决策 → 帮助理解。
三、六边形架构详解
3.1 核心思想
六边形架构(Hexagonal Architecture),又称 Ports & Adapters 模式,由 Alistair Cockburn 提出。核心思想只有一句话:
业务逻辑不依赖基础设施。
markdown
传统分层架构的问题:
Controller → Service → Repository → Database
↑
业务逻辑依赖数据库
六边形架构的解法:
Controller → Service → [Repository 接口] ← Database 实现
↑
业务逻辑只依赖接口,不依赖实现
3.2 Port 和 Adapter
Port(端口) 是接口,定义了边界:
ini
Port = 接口定义
入站 Port(Driving Port):
外部世界如何调用我的业务逻辑
例:UserService 接口
出站 Port(Driven Port):
我的业务逻辑如何访问外部世界
例:UserRepository 接口
Adapter(适配器) 是实现,连接了边界:
ini
Adapter = 接口实现
入站 Adapter(Driving Adapter):
HTTP Controller、GraphQL Resolver、CLI Handler、消息消费者
→ 它们调用入站 Port
出站 Adapter(Driven Adapter):
PostgreSQL 实现、Redis 实现、S3 实现、邮件服务实现
→ 它们实现出站 Port
3.3 完整架构图
scss
入站 Adapter 出站 Adapter
┌────────────────────┐ ┌────────────────────┐
│ HTTP Controller │ │ PostgreSQL Repo │
│ GraphQL Resolver │ │ Redis Cache │
│ CLI Handler │ │ S3 Storage │
│ Message Consumer │ │ Email Service │
└────────┬───────────┘ └────────┬───────────┘
│ ▲
▼ │
┌────────────────┐ ┌────────────────────┐
│ 入站 Port │ │ 出站 Port │
│ (Service 接口) │ │ (Repository 接口) │
└────────┬───────┘ └────────▲───────────┘
│ │
▼ │
┌────────────────────────────────────────┐
│ Domain Core │
│ │
│ 实体(Entity) │
│ 值对象(Value Object) │
│ 领域服务(Domain Service) │
│ 业务规则(Business Rules) │
│ │
│ ← 不依赖任何框架和基础设施 → │
└────────────────────────────────────────┘
3.4 代码示例
以"用户注册"为例展示六边形架构的代码组织:
出站 Port(接口定义):
python
# domain/ports/user_repository.py
from abc import ABC, abstractmethod
class UserRepository(ABC):
@abstractmethod
def find_by_email(self, email: str) -> User | None:
pass
@abstractmethod
def save(self, user: User) -> User:
pass
Domain Core(业务逻辑):
python
# domain/services/registration_service.py
class RegistrationService:
def __init__(self, user_repo: UserRepository, email_sender: EmailSender):
# 依赖接口,不依赖实现
self.user_repo = user_repo
self.email_sender = email_sender
def register(self, name: str, email: str, password: str) -> User:
# 纯业务逻辑
existing = self.user_repo.find_by_email(email)
if existing:
raise EmailAlreadyRegisteredError(email)
user = User.create(name=name, email=email, password=password)
saved_user = self.user_repo.save(user)
self.email_sender.send_welcome(saved_user.email)
return saved_user
出站 Adapter(具体实现):
python
# adapters/postgres_user_repository.py
class PostgresUserRepository(UserRepository):
def __init__(self, db_session):
self.db = db_session
def find_by_email(self, email: str) -> User | None:
row = self.db.query(UserModel).filter_by(email=email).first()
return User.from_model(row) if row else None
def save(self, user: User) -> User:
model = user.to_model()
self.db.add(model)
self.db.commit()
return User.from_model(model)
入站 Adapter(HTTP Controller):
python
# adapters/http/user_controller.py
@router.post("/api/v1/users", status_code=201)
def register_user(request: RegisterRequest):
user = registration_service.register(
name=request.name,
email=request.email,
password=request.password,
)
return ApiResponse.success(UserDTO.from_entity(user))
3.5 六边形架构的好处
| 好处 | 说明 |
|---|---|
| 可测试性 | Domain Core 不依赖数据库,用 Mock 实现 Port 即可测试 |
| 可替换性 | 从 PostgreSQL 换到 MongoDB?只改 Adapter |
| 可理解性 | 业务逻辑集中在 Domain Core,不散落在 Controller 中 |
| 框架无关 | 从 Django 换到 FastAPI?只改入站 Adapter |
| 并行开发 | 定义好 Port 后,Domain 和 Adapter 可以并行开发 |
3.6 何时使用六边形架构
适合:
✓ 业务逻辑复杂的应用
✓ 需要长期维护的系统
✓ 可能更换技术栈的项目
✓ 需要高测试覆盖率的系统
不适合:
✗ 简单的 CRUD 应用(过度设计)
✗ 原型验证(速度优先)
✗ 短期项目(维护周期短)
四、架构决策记录(ADR)
4.1 为什么需要 ADR
六个月后,没有人记得为什么选择了 PostgreSQL 而不是 MongoDB。
ADR 解决的核心问题是:记录"为什么",而不只是"是什么"。
arduino
代码告诉你"是什么"(What)
注释告诉你"怎么做"(How)
ADR 告诉你"为什么"(Why)
4.2 ADR 标准模板
architecture-decision-records Skill 定义了标准的 ADR 模板:
markdown
# ADR-001: 选择 PostgreSQL 作为主数据库
## 状态
已接受(Accepted)
## 上下文
我们需要为新的订单管理系统选择主数据库。系统预期处理
每日 100 万笔订单,需要 ACID 事务保证,需要支持复杂
查询和全文搜索。
候选方案:
- PostgreSQL
- MySQL 8.0
- MongoDB
## 决策
选择 PostgreSQL 作为主数据库。
理由:
1. JSONB 类型支持灵活的 Schema 演进,减少 Migration 频率
2. 原生全文搜索(tsvector)避免引入 Elasticsearch
3. RLS(行级安全)简化多租户权限控制
4. 丰富的索引类型(B-tree、GIN、GiST)覆盖所有查询模式
5. 团队有 3 年 PostgreSQL 运维经验
## 后果
正面:
- 减少技术栈复杂度(不需要额外的搜索引擎)
- 团队无需学习新技术
- 运维工具链成熟
负面:
- 单机写入性能上限约 5 万 TPS,未来可能需要分片
- 全文搜索能力不如专用搜索引擎
- 需要额外配置连接池(PgBouncer)
## 参考
- PostgreSQL vs MySQL 性能对比:[链接]
- 团队数据库经验调查结果:[链接]
4.3 ADR 状态流转
提议(Proposed)
│
├─→ 已接受(Accepted)
│ │
│ ├─→ 已废弃(Deprecated)→ 被新 ADR 取代
│ │
│ └─→ 已取代(Superseded)→ 被 ADR-XXX 取代
│
└─→ 已拒绝(Rejected)→ 记录拒绝原因,避免重复讨论
4.4 ADR 文件组织
perl
docs/adr/
├── 001-use-postgresql.md
├── 002-adopt-hexagonal-architecture.md
├── 003-use-jwt-for-authentication.md
├── 004-choose-kubernetes-over-ecs.md
└── README.md # ADR 索引
4.5 好的 ADR vs 差的 ADR
| 维度 | 好的 ADR | 差的 ADR |
|---|---|---|
| 上下文 | 说明了约束条件和需求 | 只说"我们需要一个数据库" |
| 候选方案 | 列出了 2-3 个备选 | 只有最终选择 |
| 理由 | 解释了为什么选 A 而不选 B | 只说"A 更好" |
| 后果 | 包含正面和负面 | 只有正面 |
| 可追溯 | 链接到相关讨论和数据 | 无参考链接 |
五、codebase-onboarding Skill
5.1 架构地图
codebase-onboarding Skill 帮助团队创建可读的架构文档:
架构地图模板:
markdown
# 系统架构地图
## 入口点
- HTTP API: `src/adapters/http/` → 端口 8080
- Worker: `src/adapters/worker/` → 消息队列消费
- CLI: `src/adapters/cli/` → 管理命令
## 核心模块
- 用户管理: `src/domain/user/`
- 订单处理: `src/domain/order/`
- 支付集成: `src/domain/payment/`
## 外部依赖
- PostgreSQL → 主数据库
- Redis → 缓存 + 会话
- S3 → 文件存储
- Stripe → 支付处理
## 依赖图
User ←── Order ←── Payment
│
└──── Inventory
5.2 新人引导清单
codebase-onboarding 推荐为新成员准备以下内容:
- 5 分钟概览 --- 系统做什么、服务谁、核心流程
- 架构地图 --- 模块关系、入口点、外部依赖
- 本地环境 --- 一键启动脚本、环境变量模板
- 第一个任务 --- 一个简单但端到端的修改任务
- ADR 列表 --- 阅读最近 5 个 ADR 理解历史决策
六、architect Agent
6.1 Agent 特点
architect Agent 是 ECC 中最重量级的 Agent:
| 维度 | 说明 |
|---|---|
| 模型 | Opus(最深度推理能力) |
| 用途 | 系统设计、架构决策、技术选型 |
| 触发 | 涉及架构决策时自动触发,或手动调用 |
| 输出 | 架构方案 + ADR 草稿 + 风险分析 |
6.2 architect 的工作方式
architect Agent 采用多视角分析:
arduino
输入:架构问题(如"是否应该拆分为微服务?")
│
├── 视角 1:技术可行性
│ 评估技术复杂度、团队能力、工具链成熟度
│
├── 视角 2:业务适配性
│ 评估业务增长预期、变更频率、独立部署需求
│
├── 视角 3:运维成本
│ 评估监控、日志、链路追踪、故障排查复杂度
│
└── 视角 4:演进路径
评估从当前架构迁移的步骤和风险
│
▼
输出:ADR 草稿 + 推荐方案 + 风险清单
6.3 与其他 Agent 的协作
css
architect(架构决策)
│
├── planner(将架构方案拆解为实施步骤)
│
├── security-reviewer(审查架构的安全性)
│
└── code-reviewer(审查架构实现的代码质量)
七、Skeleton Projects 模式
7.1 patterns.md 中的定义
rules/common/patterns.md 定义了 Skeleton Projects 模式 --- 在实现新功能时,先寻找成熟的骨架项目作为基础:
markdown
Skeleton Projects 四步法:
1. 搜索(Search)
寻找经过验证的骨架项目
→ GitHub、公司内部模板、框架官方 Starter
2. 评估(Evaluate)
用并行 Agent 从多个维度评估:
- 安全性评估
- 扩展性分析
- 相关度评分
- 实施规划
3. 克隆(Clone)
选择最佳匹配作为基础
4. 迭代(Iterate)
在成熟的结构上迭代开发
7.2 评估维度
| 维度 | 评估内容 | Agent |
|---|---|---|
| 安全性 | 依赖漏洞、认证方案、密钥管理 | security-reviewer |
| 扩展性 | 模块化程度、插件机制、配置灵活性 | architect |
| 相关度 | 与需求的匹配程度、定制成本 | planner |
| 代码质量 | 测试覆盖、文档完整、CI/CD 配置 | code-reviewer |
7.3 常见骨架项目
| 技术栈 | 推荐骨架 | 特点 |
|---|---|---|
| Next.js | create-next-app | 官方模板,App Router |
| Django | django-cookiecutter | 生产级配置 |
| Spring Boot | Spring Initializr | 依赖选择器 |
| Go | go-kit/kit | 微服务工具集 |
| Kotlin/Ktor | ktor-project-generator | 插件选择器 |
八、架构反模式
8.1 常见反模式
| 反模式 | 问题 | ECC 如何帮助 |
|---|---|---|
| 大泥球(Big Ball of Mud) | 无边界、无分层 | hexagonal-architecture 定义边界 |
| 过度微服务 | 复杂度爆炸、网络调用多 | architect Agent 评估拆分必要性 |
| 无文档决策 | 六个月后无人记得原因 | architecture-decision-records |
| 简历驱动开发 | 选技术看简历而非需求 | ADR 强制记录技术选型理由 |
| 分布式单体 | 微服务但强耦合 | hexagonal-architecture 强调接口边界 |
8.2 如何判断需要重构架构
markdown
信号(可能需要重构):
□ 一个小功能需要改 5 个以上的文件
□ 团队之间频繁出现代码冲突
□ 部署一个服务需要同时部署其他服务
□ 测试覆盖率持续下降
□ 新成员入职需要超过 2 周才能提第一个 PR
行动:
1. 用 architect Agent 分析当前架构问题
2. 写 ADR 记录重构决策
3. 用 planner Agent 制定渐进式重构计划
4. 不要"大爆炸"重构 --- 逐步演进
九、本课练习
练习 1:查看架构 Skill(10 分钟)
bash
ls skills/hexagonal-architecture/
ls skills/architecture-decision-records/
ls skills/codebase-onboarding/
回答问题:
hexagonal-architecture中的 Port 和 Adapter 分别对应什么?architecture-decision-records中 ADR 的必填字段有哪些?
练习 2:写一个 ADR(25 分钟)
这是本课最重要的练习。
为你当前项目的一个技术决策写一个 ADR。选择以下任一主题(或自定义):
- 选择某个数据库
- 选择某个框架
- 选择单体还是微服务
- 选择某个认证方案
要求:
- 使用第四节中的 ADR 标准模板
- 至少列出 2 个候选方案
- 理由部分至少有 3 条
- 后果部分包含正面和负面
练习 3:画六边形架构图(15 分钟)
为你当前项目(或你熟悉的项目)画一个六边形架构图,标注:
- Domain Core 中有哪些实体和业务规则
- 入站 Port 和 Adapter 有哪些
- 出站 Port 和 Adapter 有哪些
练习 4(选做):思考题
六边形架构和 Clean Architecture(Bob 大叔的洋葱架构)有什么相同点和不同点?它们解决的核心问题是一样的吗?
十、本课小结
| 你应该记住的 | 内容 |
|---|---|
| 六边形架构 | Port 是接口,Adapter 是实现;业务逻辑不依赖基础设施 |
| ADR | 记录"为什么"而不只是"是什么";标准模板:状态/上下文/决策/后果 |
| architect Agent | Opus 模型深度推理,多视角分析,输出 ADR 草稿 |
| Skeleton Projects | 搜索 → 评估 → 克隆 → 迭代,不从零开始 |
| 架构反模式 | 大泥球、过度微服务、无文档决策、简历驱动开发 |
十一、下节预告
第 23 课:DevOps 工作流 --- CI/CD 与部署模式
下节课我们将从代码架构上升到交付流程。你将学习 ECC 的 deployment-patterns 和 devops-workflow Skill,掌握 CI/CD 管线配置、蓝绿部署、金丝雀发布等现代交付实践。
预习建议 :提前浏览 skills/deployment-patterns 和 skills/devops-workflow 目录。