六角形架構 Hexagonal

多租戶 SaaS 後端 · FastAPI ── src/shared 橫切基板 × src/modules 特徵模組。依賴一律向內、邊界由 import-linter 機械強制。

草稿 · Draft 對內:工程 / PM
2026-07

The Hexagon · 依賴反轉

一個模組 = 三個同心環

內圈是純業務、外圈是技術細節。箭頭永遠由外向內 —— adapters → application → domain。

Adapters inbound · outbound Application ports · use_cases Domain frozen dataclass HTTP / Broker ↦ ↦ DB / HTTP / S3 use case 只依賴 Port(Protocol),adapter 反過來實作它
Domain
entities · exceptions · lifecycle
零框架 · 不 import 任何外環
Application
Port = Protocol 契約
UseCase = 一個業務流程
Inbound Adapter
FastAPI router · FastStream 訂閱者
驅動 use case
Outbound Adapter
SQLAlchemy repo · httpx · S3
實作 Port
R1 · Domain purity ── application / domain 不得 import fastapi · sqlalchemy · pydantic · shared.infrastructure

Request journey · api 行程

一個 HTTP 請求的旅程

綠點 = 請求,由外層中介一路穿到 Postgres,再沿 session_context commit。

HTTP Client
中介層 MiddlewareCORS → RedMetrics → TenantActive → Idempotency
Inbound Routeradapters/inbound/api · FastAPI
AuthorizedUseCasePDP 權限檢查 · 拒絕 → 403 + 稽核
AuditedUseCase成功 → 同交易寫 audit_log
Use Caseapplication/use_cases · 一個流程
PortProtocol — 依賴反轉點
SqlAlchemy Repositoryadapters/outbound · RLS set_local app.tenant_id
PostgreSQLvia PgBouncer · transaction pool

src/shared · 橫切基板

shared 提供什麼共享能力

通用能力集中一處,模組往內依賴即可 —— 但方向嚴格單向。

Ports & Adapters(通用)

ClockIdGenerator · uuid7Encryptor · AES-GCM OutboundHttpClient · httpxObjectStorage · S3 DomainEventPublisherBackgroundJobSubmitter · outboxDistributedLock · Garnet

Use-case 裝飾器

AuditedUseCaseAuthorizedUseCase

Infrastructure & 統一信封

session_context / get_dbRLSCacheService post-commit bufferDomainError + ErrorCodes{data, messages, status_code}
R3d · 黃金鐵律 ── src.shared 永不 import src.modules

SPI Seam · 反轉跨模組依賴

shared 需要 module 時怎麼辦

shared 開「插槽」、模組在組合根把「插頭」插進去。以 PDP 權限判定為例。

shared · get_pdp()placeholder → raise NotImplementedError
啟動時以 dependency_overrides 覆寫
組合根 · main.pywire_pdp(app, build_pdp_dependency)
注入 permission 的真實實作
permission · SqlAlchemyPdp無狀態 PDP · 展開 token roles claim 純比對

同型接縫:wire_tenant_context · wire_entitlement_service · MODULE_REGISTRY

src/modules · 分層與邊界

模組分層與匯入方向

Feature 可依賴 Platform,反之不行;同層 sibling 互相 import 也禁止。

Feature tier · 特徵模組選配 / 可訂閱
llmstorage projectdirectorycontract
↓ 可 import ✓ ↑ 反向 import ✗
Platform / core tier · 平台層禁撤銷
identitytenantpermission
跨模組協作 ── 只經 __init__.py 契約;真正協作用「消費者自持 Port + 組合根注入」,模組彼此零直接 import(例:tenant 的 OwnerInvitationIssuer ← identity)

Boundaries are mechanical

邊界如何被機械強制

import-linter14 條結構契約 · 0 個 ignore_imports
PreToolUse寫入當下擋下違規 Python · never lands
arch_validatorAST 規則 · PS01 PgBouncer 安全 · TR02 …
doc-hygienetracker 引用 ratchet · CI-gated

Process model

四行程 · 同一 image

apimain.py · FastAPI · 處理 HTTP、發布訊息
workerworker.py · FastStream · 執行背景工作
dispatcherdispatcher.py · 輪詢 outbox → RabbitMQ
schedulerscheduler.py · 輪詢到期排程 → outbox
共用:PostgreSQL · PgBouncer · RabbitMQ · Garnet (+ migrate 一次性)

Hexagonal Architecture · FastAPI · PostgreSQL · RabbitMQ · Garnet · 嚴格六角形 + 共享基板 + 交易外箱