根據 2024 年 vFunction 調查,超過 50% 的公司眼看著超過四分之一的 IT 預算消失在技術債務中。
這不是因為開發人員不擅長撰寫程式碼,而是因為我們的架構邊界正在侵蝕。我們讓商業規則滲透到資料庫模型中,讓 HTTP 控制器滲透到核心邏輯中。
主要禍首是什麼?現代框架行銷以及「教學文化」,它們主導了我們學習程式設計的方式。
以下深入探討為什麼你的架構正在流血、我們正在違反哪些工程原則,以及六邊形隔離如何提供結構性解決方案——附帶完整的 TypeScript 程式碼庫 FlowBank,讓你能實際看到它的應用,而不只是紙上談兵。
1. 陷阱:「Hello-World 到達時間」與架構債務
大多數開發人員透過 15 分鐘的影片教學或框架快速入門指南來學習建構應用程式。為了贏得採用,框架會針對快速上手進行最佳化——讓你只需三個步驟就能使用「魔法」捷徑建構一個待辦事項應用程式。
這會造成三個架構陷阱:
- 主動記錄陷阱 — ORM 將資料庫結構直接耦合到商業實體。變更你的資料表,你的商業核心模型就會壞掉。
- 控制器到資料庫捷徑 — 框架樣板程式鼓勵將驗證、商業規則和 SQL 直接寫在 HTTP 處理器中。
- 「魔法」註解滲透 — 特定框架的裝飾器直接注入到領域物件中,讓整個應用程式受制於該廠商。
長期遵循這些捷徑會累積 架構技術債務 (ATD)。三年後,升級框架或切換資料庫提供者就意味著必須重寫公司的基礎商業規則。
2. 解決方案:什麼是六邊形隔離?
由 Alistair Cockburn 最初提出,稱為 連接埠 與 轉接器,六邊形架構透過強制嚴格的結構隔離來解決層級滲透問題。
[External World] ----> ( Port / Interface ) ----> [Core Business Logic]
(HTTP/DB/CLI) (Strict Abstraction) (Pure Domain Rules)
Enter fullscreen mode Exit fullscreen mode
- 核心領域 — 只包含純粹的商業邏輯。對網頁、資料庫、檔案系統或框架一無所知。
- 連接埠(抽象介面) — 六邊形的邊緣完全由抽象介面構成。核心只與這些介面溝通。
- 轉接器(具體實作) — Postgres、REST API、GraphQL、S3 — 全部位於六邊形之外,實作連接埠以傳遞資料進出。
透過抽象隔離核心,你就能獲得真正的即插即用架構。在不觸碰任何一行核心應用程式規則的情況下,將 MongoDB 替換為 PostgreSQL。
最小 TypeScript 範例
這是 FlowBank 所使用的模式精簡版本,FlowBank 是一個小型開源銀行 API,專門用來展示這種架構的完整實作——轉帳、存款、通知和支付處理,所有核心都與 Postgres、Stripe 和 Express 完全隔離。
// core/ports/user-repository.port.ts
// The PORT — an abstract interface the core depends on, not a concrete DB.
export interface UserRepository {
findById(id: string): Promise<User | null>;
save(user: User): Promise<void>;
}
// core/domain/user.ts
// The CORE — pure business logic, no framework or DB imports.
export class User {
constructor(public readonly id: string, private email: string) {}
changeEmail(newEmail: string) {
if (!newEmail.includes("@")) {
throw new Error("Invalid email");
}
this.email = newEmail;
}
}
// adapters/postgres-user-repository.ts
// The ADAPTER — concrete implementation, swappable without touching the core.
import { UserRepository } from "../core/ports/user-repository.port";
import { User } from "../core/domain/user";
import { db } from "./db-client";
export class PostgresUserRepository implements UserRepository {
async findById(id: string): Promise<User | null> {
const row = await db.query("SELECT * FROM users WHERE id = $1", [id]);
return row ? new User(row.id, row.email) : null;
}
async save(user: User): Promise<void> {
await db.query("UPDATE users SET email = $1 WHERE id = $2", [
user, // simplified for brevity
user,
]);
}
}
Enter fullscreen mode Exit fullscreen mode
User 類別及其商業規則從不匯入 db-client、Express 或任何與 Postgres 相關的東西。明天將 PostgresUserRepository 替換為 MongoUserRepository 或 InMemoryUserRepository(用於測試)時,不需要對核心做任何更動。
3. 理論支柱:SOLID 與 DDD
六邊形隔離不是隨意的設計選擇,而是基礎電腦科學原則的具體實作。
SOLID 矩陣:
- 依賴反轉原則 (DIP) — 高階商業邏輯不得依賴低階框架;兩者都依賴抽象。六邊形架構強制所有原始碼依賴都指向內部,朝向核心。
- 單一職責原則 (SRP) — 領域模型獲得單一變更理由:商業需求變更。它們完全沒有框架管線。
- 開閉原則 (OCP) — 對擴展開放,對修改關閉。需要新的 SMS 提供者?撰寫新的轉接器。核心保持不變。
領域驅動設計與元件耦合:
六邊形隔離與 Clean Architecture(Robert C. Martin)及 Onion Architecture(Jeffrey Palermo)屬於同一家族。它強制遵循無循環依賴原則 (ADP),消除循環參考,並遵循穩定依賴原則 (SDP),讓最穩定的元件——你的商業規則——成為所有依賴的目標。
4. 工程回報
實作六邊形隔離意味著前期需要更多樣板程式,但長期投資報酬率相當可觀:
- 無懈可擊的可測試性 — 由於核心只依賴抽象連接埠,你可以立即模擬資料庫和外部 API。在毫秒內測試整個商業邏輯,不需要 Docker 容器或實際伺服器。
- 框架無關性 — 你的框架成為傳遞機制,而非應用程式的定義。
- 未來證明演進 — 當更快的資料庫或更便宜的雲端提供者出現時,遷移成本從全面重寫降至建置單一新轉接器。
5. 在實際程式碼庫中查看:FlowBank
閱讀模式與看到它在實際依賴圖中運作是兩種不同的體驗。這就是為什麼本文附帶 FlowBank——一個開源 TypeScript 銀行 API,透過實際的資金移動工作流程展示六邊形架構:轉帳、存款、通知和支付處理。
src/
core/ <- the hexagon's interior, zero framework imports allowed
domain/ Account, Money, Transaction — pure business rules
ports/ interfaces the core depends on, never concretes
use-cases/ application logic, orchestrates domain + ports
errors/ domain-specific error types
adapters/ <- everything outside the hexagon
postgres/ real DB implementation of the repository ports
stripe/ real payment gateway implementation
email/ real notification implementation
http/ Express router — a driving adapter, calls IN to use cases
in-memory/ fakes used by tests, swap-in for Postgres/Stripe/email
main.ts <- composition root: the ONLY file allowed to wire
concrete adapters into the use cases
Enter fullscreen mode Exit fullscreen mode
有幾件事值得實際嘗試,而不只是閱讀:
-
npm test執行核心的商業規則測試,無需資料庫、無需網路呼叫、無需 Docker 容器——只需記憶體內轉接器代替 Postgres 和 Stripe。 - 在程式碼庫中搜尋證據而非承諾:
grep -rl "from.*adapters" src/core不會傳回任何結果。本文前面圖表中的依賴箭頭不是空想——它們是由實際匯入的內容強制執行的。 - 嘗試將
StripePaymentGateway替換為假設的PaystackPaymentGateway,或將PostgresAccountRepository替換為 Mongo 對等項目。這兩者都不會觸碰Account、Money或 use cases——這正是本文論點的具體化。
複製它、破壞它、擴充它。這個儲存庫證明了這不僅僅是一張圖表——它是一個可以發佈、快速測試、並在提供者交換時存活的程式碼庫。
結論
技術債務通常不是由單一錯誤決策造成的——它是一次又一次的框架捷徑累積而成,直到商業邏輯與傳遞機制糾纏到無法改變其中一方而不破壞另一方。六邊形隔離是對此的直接結構性答案:將所有框架、資料庫和廠商 SDK 推到邊緣,並保留一個小型、無依賴的核心,它只知道它所服務的業務。
回報不是理論上的。它表現為測試以毫秒而非分鐘為單位執行,遷移只需一個衝刺而非一個季度,以及新工程師能夠真正理解的程式碼庫,因為商業規則沒有埋藏在 ORM 鉤子和路由處理器中。
不要再像 15 分鐘教學一樣建構軟體。隔離你的核心,保護你的邊界,複製 FlowBank 親自查看這個模式,並取回你的工程預算。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.