前言¶
後端代碼寫久了最常見的問題不是性能,而是分層退化:domain 裏直接 import 了 ORM,use case 裏塞滿了 HTTP 狀態碼,測試必須連數據庫才能跑。架構規則寫在文檔裏沒人看,寫在代碼裏又沒人維護。下面介紹的 architecture-patterns 是一個提示詞型 Skill,作用是把這些規則帶進對話流程,讓模型在設計或重構時按同一套分層約束產出代碼。
這是什麼¶
architecture-patterns 是一個通用 SKILL.md 格式的後端架構模式 Skill,座標 @clawhub_wpank/architecture-patterns,版本 1.0.0,發佈者 wpank,來源 clawhub,分類 dev-programming。它覆蓋清潔架構(Clean Architecture)、六邊形架構(Hexagonal / Ports & Adapters)與領域驅動設計(DDD),用於構建可維護、可測試、可擴展的系統,並給出清晰的分層結構和依賴方向約束。
在 SkillHub 上的頁面標註爲「Architecture Patterns」,屬於 dev-programming 分類。
核心功能¶
下面介紹的都是 SKILL.md 中實際寫明的能力。
1、一個選型決策表,按場景推薦模式。簡單 CRUD 應用建議不加架構(避免過度設計),中等複雜度且需要團隊統一標準時用清潔架構,外部集成多且變化頻繁時用六邊形架構,業務規則複雜的領域用 DDD,多團隊的大規模系統用 DDD + 限界上下文。
2、清潔架構的分層圖與依賴規則。四層分別是 Entities、Use Cases、Interface Adapters、Frameworks & Drivers,依賴只能由外向內,內層不得 import 外層。
3、六邊形架構的 Port 與 Adapter 圖示,端口定義領域需要什麼,適配器是可替換的實現,測試時可換掉。
4、一套目錄結構約定,app/domain 放實體與業務規則(內層),use_cases 放應用邏輯,adapters 放接口實現(倉儲、控制器),infrastructure 放框架與外部關注點。
5、可直接參考的代碼骨架,包括實體定義、抽象端口(IUserRepository)、用例編排(CreateUserUseCase)、Postgres 適配器、領域服務與適配器替換示例、不可變值對象(Email、Money)、聚合根與不變量校驗。
6、要點標註:實體內不寫框架依賴,業務規則放在實體和聚合裏,端口定義契約而不給實現,適配器可互換以便測試。
安裝與啓用¶
安裝命令來自官方頁面:
$ openclaw skills install @wpank/architecture-patterns
頁面同時給出另一種途徑,用 npx 通過 skills CLI:
npx skills
安裝後在對話中觸發即可,觸發條件寫得很明確:從零設計後端系統、爲可維護性重構單體、爲團隊建立架構標準、創建可測試可 mock 的代碼庫、規劃微服務拆分。
當前資料裏沒有列出 Cursor / Codex CLI / Claude Code 的差異化安裝方式,這裏不做推測。
典型用法¶
下面的例子都來自 SKILL.md,可直接對照。
先看目錄結構,把分層落到文件上:
app/
├── domain/ # 實體與業務規則(最內層)
│ ├── entities/
│ │ └── user.py
│ ├── value_objects/
│ │ └── email.py
│ └── interfaces/ # 端口
│ └── user_repository.py
├── use_cases/ # 應用業務規則
│ └── create_user.py
├── adapters/ # 接口實現
│ ├── repositories/
│ │ └── postgres_user_repository.py
│ └── controllers/
│ └── user_controller.py
└── infrastructure/ # 框架與外部關注點
├── database.py
└── config.py
實體層不引入框架,業務規則寫在實體方法裏:
from dataclasses import dataclass
from datetime import datetime
@dataclass
class User:
"""Core entity - NO framework dependencies."""
id: str
email: str
name: str
created_at: datetime
is_active: bool = True
def deactivate(self):
"""Business rule in entity."""
self.is_active = False
def can_place_order(self) -> bool:
return self.is_active
端口只定義契約,不給實現:
from abc import ABC, abstractmethod
from typing import Optional
class IUserRepository(ABC):
"""Port: defines contract, no implementation."""
@abstractmethod
async def find_by_id(self, user_id: str) -> Optional[User]:
pass
@abstractmethod
async def save(self, user: User) -> User:
pass
用例負責編排,依賴通過構造函數注入:
@dataclass
class CreateUserRequest:
email: str
name: str
@dataclass
class CreateUserResponse:
user: Optional[User]
success: bool
error: Optional[str] = None
class CreateUserUseCase:
"""Use case: orchestrates business logic."""
def __init__(self, user_repository: IUserRepository):
self.user_repository = user_repository # Injected dependency
async def execute(self, request: CreateUserRequest) -> CreateUserResponse:
existing = await self.user_repository.find_by_email(request.email)
if existing:
return CreateUserResponse(user=None, success=False, error="Email exists")
user = User(
id=str(uuid.uuid4()),
email=request.email,
name=request.name,
created_at=datetime.now()
)
saved = await self.user_repository.save(user)
return CreateUserResponse(user=saved, success=True)
適配器實現端口,可以換成 Postgres、內存實現或其他:
class PostgresUserRepository(IUserRepository):
"""Adapter: PostgreSQL implementation of the port."""
def __init__(self, pool: asyncpg.Pool):
self.pool = pool
async def find_by_id(self, user_id: str) -> Optional[User]:
async with self.pool.acquire() as conn:
row = await conn.fetchrow(
"SELECT * FROM users WHERE id = $1", user_id
)
return self._to_entity(row) if row else None
async def save(self, user: User) -> User:
async with self.pool.acquire() as conn:
await conn.execute(
"""INSERT INTO users (id, email, name, created_at, is_active)
VALUES ($1, $2, $3, $4, $5)
ON CONFLICT (id) DO UPDATE SET email=$2, name=$3, is_active=$5""",
user.id, user.email, user.name, user.created_at, user.is_active
)
return user
六邊形架構的用法是給領域服務注入多個端口,測試時替換爲 mock 適配器:
class OrderService:
def __init__(
self,
order_repo: OrderRepositoryPort, # Port
payment: PaymentGatewayPort, # Port
notifications: NotificationPort # Port
):
self.orders = order_repo
self.payments = payment
self.notifications = notifications
async def place_order(self, order: Order) -> OrderResult:
if not order.is_valid():
return OrderResult(success=False, error="Invalid order")
payment = await self.payments.charge(order.total, order.customer_id)
if not payment.success:
return OrderResult(success=False, error="Payment failed")
order.mark_as_paid()
saved = await self.orders.save(order)
await self.notifications.send(order.customer_email, "Order confirmed")
return OrderResult(success=True, order=saved)
class StripePaymentAdapter(PaymentGatewayPort):
async def charge(self, amount: Money, customer: str) -> PaymentResult:
# Real Stripe implementation
...
class MockPaymentAdapter(PaymentGatewayPort):
async def charge(self, amount: Money, customer: str) -> PaymentResult:
return PaymentResult(success=True, transaction_id="mock-123")
DDD 部分給出不可變值對象與聚合根。值對象用 frozen=True 保證不可變,在 __post_init__ 裏校驗:
@dataclass(frozen=True)
class Email:
"""Value object: validated, immutable."""
value: str
def __post_init__(self):
if "@" not in self.value:
raise ValueError("Invalid email")
@dataclass(frozen=True)
class Money:
amount: int # cents
currency: str
def add(self, other: "Money") -> "Money":
if self.currency != other.currency:
raise ValueError("Currency mismatch")
return Money(self.amount + other.amount, self.currency)
聚合根承擔一致性邊界,在方法裏強制不變量:
class Order:
"""Aggregate root: enforces invariants."""
def __init__(self, id: str, customer: Customer):
self.id = id
self.customer = customer
self.items: List[OrderItem] = []
self.status = OrderStatus.PENDING
self._events: List[DomainEvent] = []
def add_item(self, product: Product, quantity: int):
"""Business logic in aggregate."""
if quantity > product.max_quantity:
raise ValueError(f"Max {product.max_quantity} allowed")
item = OrderItem(product, quantity)
self.items.append(item)
self._events.append(ItemAddedEvent(self.id, item))
def submit(self):
"""State transition with invariant enforcement."""
if not self.items:
raise ValueError("Cannot submit empty order")
if self.status != OrderStatus.PENDING:
raise ValueError("Order already submitted")
self.status = OrderStatus.SUBMITTED
self._events.append(OrderSubmittedEvent(self.id))
單元測試部分,資料中的要點是:領域層與用例層只依賴端口,測試時注入內存適配器(in-memory adapter)即可,不需要真實數據庫。上面 MockPaymentAdapter 的寫法就是同一思路。
適用場景與注意¶
適合這些情況:新後端服務要定分層標準,單體要按限界上下文拆,外部集成多且可能更換供應商,業務規則複雜需要落在聚合與值對象裏,或者正在排查應用層之間的循環依賴。
要注意幾點。第一,SKILL.md 明確寫了簡單 CRUD 應用不需要上這些模式,強行套用屬於過度設計,選型表裏這是第一行。第二,這套內容是架構約束與代碼骨架,不包含運行時框架,落地時需要自己選語言對應的 ORM、HTTP 庫和測試框架,示例代碼是 Python 風格(async、asyncpg、dataclass),直接抄到別的語言需要改寫。第三,示例中的 find_by_email 只出現在 CreateUserUseCase 裏,端口定義中並沒有聲明,按示例跑之前先確認端口補全,否則會運行期報錯。第四,資料中未給出運行時的調用示例或配置項,接入方式只有安裝命令和觸發條件。
結尾¶
architecture-patterns 的價值在於把「分層」和「依賴方向」從口頭約定變成對話裏可執行的約束,同時給出目錄結構、端口寫法和測試替換方式,適合在設計期和重構期使用。它不做框架集成,也不生成項目腳手架,需要與具體技術棧配合。
官方地址:https://api.skillhub.cn/user_692c63a9/architecture-patterns
上游倉庫:https://clawhub.ai/wpank/architecture-patterns
SkillHub 頁面:https://www.skillhub.cn/skills/clawhub_wpank/architecture-patterns