architecture-patterns

architecture-patterns

热门

实现经过验证的后端架构模式,包括整洁架构、六边形架构和领域驱动设计。在设计新微服务的整洁架构、重构单体应用以使用限界上下文、实现六边形或洋葱架构模式,或调试应用层之间的依赖循环时,使用此技能。

3.8万Star
4097Fork
更新于 2026/7/22
SKILL.md
readonly只读
name
architecture-patterns
description

实现经过验证的后端架构模式,包括整洁架构、六边形架构和领域驱动设计。在设计新微服务的整洁架构、重构单体应用以使用限界上下文、实现六边形或洋葱架构模式,或调试应用层之间的依赖循环时,使用此技能。

架构模式

掌握经过验证的后端架构模式,包括整洁架构、六边形架构和领域驱动设计,以构建可维护、可测试和可扩展的系统。

给定: 需要架构设计的服务边界或模块。
产出: 具有清晰依赖规则、接口定义和测试边界的分层结构。

何时使用此技能

  • 从头设计新的后端服务或微服务
  • 重构单体应用,其中业务逻辑与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="alice@example.com", name="Alice"))

    assert response.success
    assert response.user.email == "alice@example.com"
    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="alice@example.com", name="Alice"))
    response = await use_case.execute(CreateUserRequest(email="alice@example.com", name="Alice2"))

    assert not response.success
    assert "already exists" in response.error

故障排除

用例测试需要运行数据库

业务逻辑已泄漏到基础设施层。将所有数据库调用移到 IRepository 接口后面,并在测试中注入内存实现(参见上面的测试部分)。用例构造函数必须接受抽象端口,而不是具体类。

层之间的循环导入

常见症状是 use_casesadapters 之间的 ImportError: cannot import name X。这是因为用例导入了具体的适配器类而不是抽象端口。强制执行规则:use_cases/ 仅从 domain/(实体和接口)导入。绝不能从 adapters/infrastructure/ 导入。

框架装饰器出现在领域实体中

如果领域实体上出现了 SQLAlchemy Column() 或 Pydantic Field() 注解,则该实体不再纯净。在 adapters/repositories/ 中创建单独的ORM模型,并在仓库的 _to_entity() 方法中与领域实体进行映射。

所有逻辑最终都放在控制器中

当控制器超出HTTP解析和响应格式化的范围时,将逻辑提取到用例类中。控制器方法应仅做三件事:解析请求、调用用例、映射响应。

值对象过晚抛出错误

__post_init__(Python)或构造函数中验证不变量,以便无法构造无效的 EmailMoney。这会在边界处暴露不良数据,而不是在业务逻辑深处。

跨限界上下文的上下文泄漏

如果 Order 上下文从 Identity 上下文导入 User 实体,则引入防腐层。Order 上下文应持有自己的轻量级 CustomerId 值对象,并仅通过显式接口调用 Identity 上下文。

高级模式

有关详细的DDD限界上下文映射、完整的多服务项目树、防腐层实现和洋葱架构比较,请参阅:

相关技能

  • microservices-patterns — 在将单体分解为服务时应用这些架构模式
  • cqrs-implementation — 使用整洁架构作为CQRS命令/查询分离的结构基础
  • saga-orchestration — Saga需要定义良好的聚合边界,DDD战术模式提供了这一点
  • event-store-design — 聚合产生的领域事件直接馈送到事件存储中