組織 Domain Logic 是企業應用架構中最核心的決策之一。Fowler 將 Domain Logic 的組織方式分為三種主要模式:Transaction ScriptDomain ModelTable Module

三種主要模式#

Transaction Script#

Transaction Script 是最直觀的做法——為每個使用者操作(或商業交易)撰寫一個程序。

本質上,它是一個接收 Presentation 層輸入的程序,經過驗證和計算後,將資料存入資料庫,再將結果回傳給 Presentation 層。每個操作對應一個程序,程序可以被拆分為子程序供不同 Transaction Script 共用。

優點:

  • 簡單的程序式模型,大多數開發者都能理解
  • 與簡單的 Data Source 層(Row Data Gateway 或 Table Data Gateway)配合良好
  • 交易邊界容易設定——開始一個交易,結束時關閉

缺點:

  • 隨著 Domain Logic 複雜度增加,重複程式碼越來越多
  • 即使抽取共用子程序,重複仍然難以發現和消除
  • 最終會變成一團沒有清晰結構的混亂程序

Figure 2.1: A Transaction Script's way of calculating revenue recognitions

Domain Model#

Domain Model 是物件導向的方式——圍繞領域中的名詞(如租約、資產、出貨等)建構物件模型。每個物件包含相關的邏輯,而非將所有邏輯放在一個程序中。

優點:

  • 一旦習慣後,可以優雅地處理越來越複雜的邏輯
  • 透過新增策略物件等 OO 技術來擴展功能
  • 許多開發者一旦使用 Domain Model 就「終身受用」

缺點:

  • 學習曲線陡峭——開發團隊可能需要數月才能適應範式轉換
  • Domain Model 越豐富,與關聯式資料庫的映射就越複雜(通常需要 Data Mapper)
  • 對不熟悉 OO 的開發者來說,找到行為散佈在哪些物件中是一大挑戰

Figure 2.2: A Domain Model's way of calculating revenue recognitions

Table Module#

Table Module 是 Transaction Script 和 Domain Model 之間的折衷方案。它為資料庫中的每個 table 建立一個類別(而非每筆 row),以 Record Set 作為資料載體。

優點:

  • 比 Transaction Script 更有結構,減少重複
  • 與 Record Set 基礎的 GUI 環境完美搭配(如 .NET 與 Visual Studio)
  • 在有 Record Set 工具支援的平台上非常有吸引力

缺點:

  • 無法使用 Domain Model 中的細緻 OO 技術(如繼承、策略模式)
  • 在沒有 Record Set 支援的環境中,不值得使用

Figure 2.3: Calculating revenue recognitions with a Table Module

如何選擇#

選擇哪種模式取決於 Domain Logic 的複雜度。隨著複雜度增加,Transaction Script 和 Table Module 的維護成本會指數性上升,而 Domain Model 的維護成本則相對線性。

影響選擇的因素:

  • 團隊熟悉度:熟悉 Domain Model 的團隊會降低其初始成本
  • 平台工具支援:有 Record Set 工具的環境讓 Table Module 更有吸引力
  • 這三種模式並非互斥——可以同時用 Transaction Script 處理部分邏輯,Domain Model 處理其餘部分

Figure 2.4: Relationships between complexity and effort for different domain logic styles

如果選錯了方向:從 Transaction Script 重構到 Domain Model 是值得的;但從 Domain Model 回退到 Transaction Script 通常意義不大(除非你能因此簡化 Data Source 層)。

Service Layer#

Service Layer 是一種常見的做法——在底層的 Domain Model 或 Table Module 之上放置一層服務。

Service Layer 的角色#

  • 提供一組清晰的 API,作為應用程式的對外介面
  • 適合放置交易控制安全性邏輯
  • Presentation 層透過 Service Layer 與 Domain 互動

該放多少邏輯在 Service Layer#

Service Layer 的「厚薄」是一個關鍵決策:

  • 最薄的 Service Layer:純粹的 Facade,所有業務邏輯在底層物件中
  • 最厚的 Service Layer:大部分邏輯以 Transaction Script 形式放在 Service Layer 中,底層物件很簡單
  • 中間路線(Controller-Entity 風格):每個 use case 的特定邏輯放在 Service Layer,通用邏輯放在 Domain 物件中

作者偏好最薄的 Service Layer——只在需要時才加入,而且傾向讓它只做 Facade。不過,許多優秀的設計者會在 Service Layer 中放入較多邏輯,這也是可行的做法。