关于 architecture-patterns
用于采用 Clean Architecture、Hexagonal Architecture 和领域驱动设计构建后端服务的技能,帮助团队划分层次、接口和测试边界,使业务逻辑与基础设施分离。
主要功能
- 01Clean Architecture 分层指导
- 02端口与适配器设计
- 03DDD 限界上下文
- 04领域建模模式
- 05内存适配器测试
- 06依赖边界问题排查
适用场景
- 01设计后端服务
- 02分离业务逻辑与 ORM 或 HTTP 代码
- 03定义服务限界上下文
- 04无需数据库测试用例
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ wshobson/ agents --skill architecture-patterns安装后在 Claude Code 中输入/architecture-patterns确认已加载。
附带文件references/advanced-patterns.mdreferences/details.md
详细介绍
架构模式
掌握经过验证的后端架构模式,包括整洁架构、六边形架构和领域驱动设计,以构建可维护、可测试且可扩展的系统。
输入: 一个需要架构设计的服务边界或模块。 产出: 具有清晰依赖规则、接口定义和测试边界的分层结构。
何时使用此技能
- 从零开始设计新的后端服务或微服务
- 重构业务逻辑与 ORM 模型或 HTTP 关注点纠缠在一起的单体应用
- 在将系统拆分为服务之前建立限界上下文
- 调试基础设施代码渗入领域层的依赖循环
- 创建可测试的代码库,使用例测试无需运行中的数据库
- 实现领域驱动设计的战术模式(聚合、值对象、领域事件)
核心概念
1. 整洁架构(Uncle Bob)
分层(依赖向内流动):
- 实体:核心业务模型,不导入任何框架
- 用例:应用业务规则,编排实体
- 接口适配器:控制器、展示器、网关——在用例与外部格式之间转换
- 框架与驱动:UI、数据库、外部服务——全部位于最外层环
关键原则:
- 依赖只向内指向;内层对外层一无所知
- 业务逻辑独立于框架、数据库和交付机制
- 每个层边界都通过抽象接口跨越
- 无需 UI、数据库或外部服务即可测试
2. 六边形架构(端口与适配器)
组件:
- 领域核心:业务逻辑所在之处,不依赖框架
- 端口:定义核心如何与外部世界交互的抽象接口(驱动端口与被驱动端口)
- 适配器:端口的具体实现(PostgreSQL 适配器、Stripe 适配器、REST 适配器)
优势:
- 无需触碰核心即可替换实现(例如,将 PostgreSQL 替换为 DynamoDB)
- 在测试中使用内存适配器——无需 Docker
- 技术决策推迟到边缘
3. 领域驱动设计(DDD)
战略模式:
- 限界上下文:为某个子域隔离出一致的模型;避免在整个系统中共享单一模型
- 上下文映射:定义上下文之间的关系(防腐层、共享内核、开放主机服务)
- 通用语言:代码中的每个术语都与领域专家使用的术语一致
战术模式:
- 实体:具有稳定标识、随时间变化的对象
- 值对象:由其属性标识的不可变对象(Email、Money、Address)
- 聚合:一致性边界;只有根可从外部访问
- 仓储:持久化和重建聚合;抽象存储机制
- 领域事件:捕获领域内发生的事件;用于跨聚合协调
详细模式与实例
详细的模式文档位于 references/details.md。当上述导航层级不够用时,请阅读该文件。
测试——内存适配器
正确应用整洁架构的标志是:每个用例都可以在普通单元测试中执行,无需真实数据库、无需 Docker、无需网络:
# tests/unit/test_create_user.py
import asyncio
from typing import Dict, Optional
from domain.entities.user import User
from domain.interfaces.user_repository import IUserRepository
from use_cases.create_user import CreateUserUseCase, CreateUserRequest
class InMemoryUserRepository(IUserRepository):
def __init__(self):
self._store: Dict[str, User] = {}
async def find_by_id(self, user_id: str) -> Optional[User]:
return self._store.get(user_id)
async def find_by_email(self, email: str) -> Optional[User]:
return next((u for u in self._store.values() if u.email == email), None)
async def save(self, user: User) -> User:
self._store[user.id] = user
return user
async def delete(self, user_id: str) -> bool:
return self._store.pop(user_id, None) is not None
async def test_create_user_succeeds():
repo = InMemoryUserRepository()
use_case = CreateUserUseCase(user_repository=repo)
response = await use_case.execute(CreateUserRequest(email="[email protected]", name="Alice"))
assert response.success
assert response.user.email == "[email protected]"
assert response.user.id is not None
async def test_duplicate_email_rejected():
repo = InMemoryUserRepository()
use_case = CreateUserUseCase(user_repository=repo)
await use_case.execute(CreateUserRequest(email="[email protected]", name="Alice"))
response = await use_case.execute(CreateUserRequest(email="[email protected]", name="Alice2"))
assert not response.success
assert "already exists" in response.error
故障排查
用例测试需要运行中的数据库
业务逻辑已泄漏到基础设施层。将所有数据库调用移到 IRepository 接口之后,并在测试中注入内存实现(参见上文测试部分)。用例构造函数必须接受抽象端口,而非具体类。
层之间的循环导入
一个常见症状是在 use_cases 和 adapters 之间出现 ImportError: cannot import name X。当用例导入具体的适配器类而不是抽象端口时,就会发生这种情况。强制执行该规则:use_cases/ 只能从 domain/(实体和接口)导入。它绝不能从 adapters/ 或 infrastructure/ 导入。
框架装饰器出现在领域实体中
如果 SQLAlchemy Column() 或 Pydantic Field() 注解出现在领域实体上,那么该实体就不再是纯净的。在 adapters/repositories/ 中创建一个单独的 ORM 模型,并在仓储的 _to_entity() 方法中与领域实体相互映射。
所有逻辑最终都落在控制器中
当控制器超出 HTTP 解析和响应格式化的范围时,将逻辑提取到用例类中。控制器方法应该只做三件事:解析请求、调用用例、映射响应。
值对象过晚抛出错误
在 __post_init__(Python)或构造函数中验证不变量,这样根本无法构造无效的 Email 或 Money。这会在边界处暴露坏数据,而不是深入业务逻辑内部。
跨限界上下文的情境泄漏
如果 Order 上下文从 Identity 上下文导入 User 实体,则引入防腐层。Order 上下文应持有自己的轻量级 CustomerId 值对象,并且只通过显式接口调用 Identity 上下文。
高级模式
有关详细的 DDD 限界上下文映射、完整的多服务项目树、防腐层实现以及洋葱架构对比,请参见:
相关技能
microservices-patterns— 在将单体拆解为服务时应用这些架构模式cqrs-implementation— 将整洁架构作为 CQRS 命令/查询分离的结构基础saga-orchestration— Saga 需要定义良好的聚合边界,而 DDD 战术模式可提供这些边界event-store-design— 聚合产生的领域事件直接馈入事件存储