architecture-patterns

architecture-patterns

熱門

實作經過驗證的後端架構模式,包括 Clean Architecture、Hexagonal Architecture 與 Domain-Driven Design。在為新的微服務設計乾淨架構、重構單體應用以使用限界上下文、實作六邊形或洋蔥架構模式,或除錯應用層之間的依賴循環時,使用此技能。

3.8萬星標
4097分支
更新於 2026/7/22
SKILL.md
readonlyread-only
name
architecture-patterns
description

實作經過驗證的後端架構模式,包括 Clean Architecture、Hexagonal Architecture 與 Domain-Driven Design。在為新的微服務設計乾淨架構、重構單體應用以使用限界上下文、實作六邊形或洋蔥架構模式,或除錯應用層之間的依賴循環時,使用此技能。

架構模式

掌握經過驗證的後端架構模式,包括 Clean Architecture、Hexagonal Architecture 與 Domain-Driven Design,以建構可維護、可測試且可擴展的系統。

給定: 一個待架構的服務邊界或模組。
產出: 具有明確依賴規則、介面定義與測試邊界的分層結構。

何時使用此技能

  • 從頭設計新的後端服務或微服務
  • 重構業務邏輯與 ORM 模型或 HTTP 關注點糾纏不清的單體應用
  • 在將系統拆分為服務之前建立限界上下文
  • 除錯基礎設施程式碼滲入領域層的依賴循環
  • 建立可測試的程式碼庫,讓使用案例測試無需執行資料庫
  • 實作領域驅動設計的戰術模式(聚合、值物件、領域事件)

核心概念

1. Clean Architecture(Uncle Bob)

層級(依賴向內流動):

  • Entities:核心業務模型,無框架匯入
  • Use Cases:應用程式業務規則,協調實體
  • Interface Adapters:控制器、呈現器、閘道器——在使用案例與外部格式之間轉換
  • Frameworks & Drivers:UI、資料庫、外部服務——全部在最外層

關鍵原則:

  • 依賴僅向內指向;內層對外層一無所知
  • 業務邏輯獨立於框架、資料庫與傳遞機制
  • 每個層級邊界透過抽象介面跨越
  • 可在沒有 UI、資料庫或外部服務的情況下測試

2. Hexagonal Architecture(Ports and Adapters)

元件:

  • Domain Core:業務邏輯在此,無框架
  • Ports:定義核心如何與外部世界互動的抽象介面(驅動與被驅動)
  • Adapters:連接埠的具體實作(PostgreSQL 適配器、Stripe 適配器、REST 適配器)

優點:

  • 無需觸及核心即可更換實作(例如將 PostgreSQL 替換為 DynamoDB)
  • 在測試中使用記憶體適配器——無需 Docker
  • 技術決策延遲到邊緣

3. Domain-Driven Design(DDD)

戰略模式:

  • Bounded Contexts:為一個子領域隔離一致的模型;避免在整個系統中共享單一模型
  • Context Mapping:定義上下文之間的關係(防腐層、共享核心、開放主機服務)
  • Ubiquitous Language:程式碼中的每個術語都與領域專家使用的術語一致

戰術模式:

  • Entities:具有穩定身份且隨時間變化的物件
  • Value Objects:由其屬性識別的不可變物件(Email、Money、Address)
  • Aggregates:一致性邊界;只有根可從外部存取
  • Repositories:持久化與重建聚合;抽象化儲存機制
  • Domain Events:捕捉領域內發生的事件;用於跨聚合協調

詳細模式與實作範例

詳細的模式文件位於 references/details.md。當上述導航層級不足時,請閱讀該檔案。

測試——記憶體適配器

正確應用 Clean Architecture 的標誌是,每個使用案例都可以在純單元測試中執行,無需真實資料庫、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 — 使用 Clean Architecture 作為 CQRS 命令/查詢分離的結構基礎
  • saga-orchestration — Saga 需要明確定義的聚合邊界,而 DDD 戰術模式提供了這一點
  • event-store-design — 聚合產生的領域事件直接饋入事件儲存