Anemic Domain Model

Martin Fowler

營養不良的領域模型

翻譯:Areca Chen  校稿:趙光正、何明遠

長久以來,這就是一種反樣式(anti-patterns)的情況,而此時更是風起雲湧。當我與Eric Evans聊及這種情況,我倆也同時注意到這種情況,是愈來愈普遍。身為正統領域模型的倡導者而言,我們都認為這不是一件好事。

營養不良的領域模型,其最基本的病徵是,乍看之下,還真有這麼一回事。在這這個領域空間裡面,有物件,一堆以名詞命名的物件。同樣的,這些物件 也使用豐富的關連及結構連接起來,就如同真實的領域模型該有的一般。但是,如果你檢視領域中的行為,你會為之鎮攝,你會瞭解物件中所擁有的行為太少了。確實,這些模型經常附帶著一些設計規則,教唆你不要在領域物件中放置任何領域邏輯 (domain logic) 相反地,他們用一組服務物件(service objects)來補捉領域邏輯。這些服務物件是放在領域模型的上層,然後透過領域模型存取資料。

這種反樣式最根本的錯亂,就是違反物件導向設計的初衷;也就是將資料及處理程序結合在一起。營養不良的領域模型實際上是一種程序性風格( procedural style)的設計。這正好是像我(包括Eric也是)一般,對物件死忠的人,在早期Smalltalk的時代,就已經開始奮戰的事。更慘的是,有許多人錯認這種營養不良的物件,就是實際的物件;因而完全忽略物件導向設計的重點。

目前的純粹物件導向主義都非常好,但我覺得我需要更基本的論證,來改正這種營養不良的情況。基本上,營養不良領域模型的問題是它擔負領域模型會有的所有成本,卻沒有產出任何的利益。其主要成本, 是因為無法靈活地對應到資料庫;這種情況會導致將物件與資料間的作完整對應。而這 層的對應唯有在你使用功能強大的物件導向技術,組織複雜的邏輯時,才有其真實的價值。將所有的行為拉出放到服務物件上,基本上,你只是在使用交易腳本(Transaction Scripts),而遺漏了領域模型可以帶來的優勢。

這種混淆的根源之一,主要自許多物件導向專家,建議將程序性的服務層,放在領域模型的上層。而形成一個服務層(service layer)。但這並不意味我們要拿掉領域模型中的行為。事實上服務層 的擁護者,是使用一個服務層連結一個有豐富行為的領域模型,。

Eric Evans在他傑出的《領域模型設計( Domain Driven Design)》一書中,對這幾層有下列說明

應用層(application layer)(這是他對服務層的用語):定義軟體要作的工作,並且指引具有豐富內涵的領域物件解決問題。應用層的所負的任務,對商業事務,或者其他需要與應用層互動的系統, 具有豐富的意涵。應用層必須盡量保持輕薄。不要包含商業規則或知識;只需要包含合作的事項,而且將工作委託給合作的下一層領域物件。應用層不用反應商業處境的狀態,但可以反應使用者或程式的進度狀態。

領域層(或模型層(model layer)):負責呈現商業的概念、商業處境的資訊、及商業規則。對反映商業處境的狀態來說,雖然我們會將儲存狀態的技術細節交給系統的基礎架構(infrastructure)來做,不過這些狀態卻必須在這一層中控制及使用。這一層是商業軟體的核心。

這裡的關鍵在於,服務層是輕薄的--所有的關鍵邏輯是放在領域層。他在其服務樣式(service pattern)中再次指出:

目前,最普遍的錯誤就是,輕易地放棄將行為放在適當的物件上,因而逐漸地漂移到程序性的程式寫作。

我不知道為何這種反樣式的情況如此普遍。我懷疑那是因為有許多人,並不是真正的運用適當的領域物件,尤其是那些來自資料(庫)背景的人。有些技術助長了這種情況,如J2EE的Entity Beans,這正是為何我偏好使用『樸實的Java物件』 ,而非Entity Beans 的原因之一。

一般而言,你在服務層中找到愈多的行為,愈代表你從領域模型中喪失更多的利益。如果你的邏輯全部放在服務層,那你就是淪落為盲人了。


Domain Model

領域模型

An object model of the domain that incorporates both behavior and data.

一個領域的物件模型,這個模型將行為及資料兩者結合在一起。

完整的描述請參閱《Patterns of Enterprise Application Architecture》這本書第116頁。

在最差的情況下,商業邏輯可能非成複雜。我們透過規則及邏輯,描述許多不同的案例及行為觀點。由於這種複雜性,我們便設計物件來處理這些商業規則及邏輯。領域模型建構的是物件互動的網絡,其中,每一個物件代表某些富含 意義的個體,其所代表的可能大到代表一家公司,小到一個訂單畫面的單一行資料。


Transaction Script

交易腳本

Organizes business logic by procedures where each procedure handles a single request from the presentation.

使用程序組織商業邏輯,其中每一個程序處理陳述中的單一需求。

完整的描述請參閱《Patterns of Enterprise Application Architecture》這本書第110頁。

許多商業應用可以想像成是一系列的交易(transactions)。一個交易可以視為將某些資訊以特定的方式組織起來,或資訊的變動。客戶端系統與伺服端系統的每一個互動,都包含一些邏輯。在某些情況中,這些邏輯只是簡單的顯示資料庫中的資訊。或者,牽涉驗證及計算的許多步驟。

一個交易腳本,主要是將這些邏輯組織起來,成為一個單一的程序;直接呼叫資料庫,或穿過資料庫輕薄的外層。雖然可以將共通的次作業切割成更小的次程序,但每一個交易都有其個別的交易腳本。


Service Layer

服務層

作者:Randy Stafford

Defines an application's boundary with a layer of services that establishes a set of available operations and coordinates the application's response in each operation.

使用一個服務層,定義一個應用程式的邊界。這個服務層在每一個操作中,制訂一組可用的操作,及協調應用程式的回應。

完整的描述請參閱《Patterns of Enterprise Application Architecture》這本書第133頁。

企業應用程式對於他們所儲存的資料及實作的邏輯,一般需要不同的介面: 如資料儲存(data loaders)、使用者介面(user interfaces)、整合通路(integration gateway)等等。雖然有用途不同,這些介面往往需要共通地與應用程式互動,以便存取與操作資料,及啟動其商業邏輯。這些互動可能很複雜;其中包括跨越多個資源的交易,及針對一項活動結合多個回應。在每一個介面中,將互動的邏輯個別的編碼(encoging),會導致許多重複的結構。

服務層定義應用程式的邊界(boundary)[Cockburn PloP],並且從客戶層次的觀點,定義應用程式可用的一組作業。服務層將應用程式的商業邏輯,控制交易,並結合回應,封裝於其作業的實作中。


POJO

樸實的Java物件

POJO: Plain Old Java Object.

這個名詞是我(Martin Fowler)與Rebbecca Parsons, Josh MacKenzie在2000年準備在一個研討會中演講時,所創造出來的。在這個演講中,我們指出,將商業邏輯編碼成標準(regular)的Java物件,比使用Entity Beans,可以提供更多的利益。我們非常懷疑,為什麼人們抗拒在他們的系統中使用標準物件;我們的結論是,那是因為簡單的物件缺乏超炫的名稱。因此我們給他一個超炫的名稱,而且這個名稱非常貼切。