走向.NET架構設計—第四章—業務層分層架構(後篇)
前言: 在上一篇文章中,我們討論了組織商務邏輯的模式:Transaction Script和Active Record,Domain Model。在本篇中開始講述Anemic Model。
今天的內容比較簡單,也是本章的一個收尾!
本篇議題如下:
Transaction Scrip(前篇)
Active Record前篇)
Domain Model(中篇)
Anemic Model(後篇)
DDD(後篇)
Anemic Domain Model:
初看起來,貧血型領域模型和Domain Model模式很像。但是它們確實是不同的:Domain Model的領域類中包含了自身的商務邏輯和資料,以及對象之前的關係;Anemic Domain Model的領域類將與自身相關的業務處理邏輯全部轉移到了模型之外——有專門的商務規則類,這使領域類成為了一個簡單的資料對象。
這種模式的缺點就是:領網域服務類中的代碼更加結構化了,和Transaction Script模式很像,這也就會帶來和Transaction Script模式一樣的問題。例如,其中一個就是違背了“Tell, Don’t Ask”原則:業務對象原本應該告訴客戶代碼它們是否能執行某個操作,而不是讓客戶代碼根據業務對象的狀態來自己判斷是否該執行,現在因為所有邏輯已經從業務類中移出了,業務類已經沒有 “自主能力”了。
如果採用Anemic Domain Model來實現前面的訂單處理相關例子,Order業務類的代碼則如下所示:
public class Order
{
public string OrderNo { get; set; }
public OrderStatus Status { get; set; }
public List<OrderItem> Items { get; set; }
}
Order的商務規則會放在特定的規則類中,如下所示:
public class PorcessStatusSpecification
{
public bool IsSatisfiedBy(Order order)
{
return order.Status != OrderStatus.Processed;
}
}
現在OrderService領網域服務類的方法如下所示:
public bool OrderProcess(Order requestOrder)
{
bool result = false;
var order = requestOrder;
ProductService productService = null;
if (order != null)
{
PorcessStatusSpecification specification = new PorcessStatusSpecification();
if (specification.IsSatisfiedBy(order))
{
productService = new ProductService();
var hasInventory = productService.CheckInventory(order);
if (hasInventory)
{
order.Status = OrderStatus.Processed;
//...
}
}
}
return result;
}
從上面的代碼可以看出:OrderService完全取代了原本的Order,而且還會為Order修改狀態。隨著邏輯的複雜性增強,在服務類中會出現很多的輔助方法,這會導致服務類最後和Transaction Script一樣變得無法維護。
到這裡為止,四種組織商務邏輯的模式就講述完了,每一種都有自己的用途,無所謂“一定用,或者一定不用”。到底是用哪種,都是根據項目和經驗而定。
DDD:
下面我們就來進入DDD,這裡只是講述了一下DDD中的一些基本概念,至於具體的講述DDD:
1. 後面的章節會陸續的介紹
2. 閱讀《領域驅動設計.軟體核心複雜性應對之道》,如果朋友們有需要,留下自己的Email,我會發送給大家。
下面的一些的文字都是摘自一些書籍。目的只是一個為了讓大家快速的瞭解一下DDD。
DDD幾個概念:
分層架構
實體
值對象
服務
模組
彙總
工廠
分層架構
當我們建立一個軟體應用時,這個應用的很大一部分是不能直接跟領域關聯的,但它們是基礎設施的一部分或者是為軟體服務的。最好能讓應用中的領域部分儘可能少地和其他的部分摻雜在一起,因為一個典型的應用程式套件含了很多和資料庫訪問,檔案或網路訪問以及使用者介面等相關的代碼。
在一個物件導向的程式中,使用者介面、資料庫以及其他支援性代碼經常被直接寫到業務對象中。附加的商務邏輯被嵌入到UI 組件和資料庫指令碼的行為中。之所以這樣做的某些原因是這樣可以很容易地讓事情快速工作起來。
但是,當領域相關的代碼被混入到其他層時,要閱讀和思考它也變得極其困難。表面看上去是對UI 的修改,卻變成了對商務邏輯的修改。對商務規則的變更可能需要謹慎跟蹤使用者介面層代碼、資料庫代碼以及其他程式元素。實現粘連在了一起,模型驅動對象於是變得不再可行。也很難使用自動化測試。對於每個活動中涉及到的技術和邏輯,程式必須保持簡單,否則就會變得很難理解。因此,將一個複雜的程式切分成層。開發每一個層中內聚的設計,讓每個層僅依賴於它底下的那層。遵照標準的架構模式以提供層的低耦合。將領域模型相關的代碼集中到一個層中,把它從使用者介面、應用和基礎設施代碼中分隔開來。釋放領域對象的顯示自己、儲存自己、管理應用任務等職責,讓它專註於展現領域模型。這會讓一個模型進一步富含知識,更清晰地捕獲基礎的業務知識,讓它們正常工作。
一個通用領域驅動設計的架構性方案套件含4 個概念層:
將應用劃分成分離的層並建立層間的交換規則很重要。如果代碼沒有被清晰隔離到某層中,它會迅即混亂,因為它變得非常難以管理變更。在某處對代碼的一個簡單修改會對其他地方的代碼造成不可估量的結果。領域層應該關注核心的領域問題。它應該不涉及基礎設施類的活動。使用者介面既不跟商務邏輯緊密捆綁也不包含通常屬於基礎設施層的任務。在很多情況下應用程式層是必要的。它會成為商務邏輯之上的管理者,用來監督和協調應用的整個活動。
例如,對一個典型的互動型應用,領域和基礎設施層看上去會這樣:使用者希望預定一個飛行路線,要求用一個應用程式層中的應用服務來完成。應用依次從基礎設施中取得相關的領域對象,調用它們的相關方法,比如檢查與另一個已經被預定的飛行線路的安全邊界。當領域對象執行完所有的檢查並修改了它們的狀態決定後,應用服
務將對象持久化到基礎設施中。
實體
有一類對象看上去好像擁有標識符,它的標識符在曆經軟體的各種狀態後仍能保持一致。對這些對象來講這已經不再是它們關心的屬性,這意味著能夠跨越系統的生命週期甚至能超越軟體系統的一系列的延續性和標識符。我們把這樣的對象稱為實體。
OOP 語言會把對象的執行個體放於記憶體,它們對每個對象會保持一個對像引用或者是記錄一個對象地址。在給定的某個時刻,這種引用對每一個對象而言是唯一的,但是很難保證在不確定的某個時間段它也是如此。實際上恰恰相反。對象經常被移出或者移回記憶體,它被序列化後在網路上傳輸,然後在另一端被重建立立,或者它們都被消除。在程式的運行環境中,那個看起來像標識符的參考關聯性其實並不是我們在談論的標識符。
如果有一個存放了天氣資訊(如溫度)的類,很容易產生同一個類的不同執行個體,這兩個執行個體都包含了同樣的值,這兩個對象是完全相當的,可以用其中一個跟另一個交換,但它們擁有不同的引用,它們不是實體。如果我們要用軟體程式實現一個“人”的概念,我們可能會建立一個Person 類,這個類會帶有一系列的屬性,如:名稱,出生日期,出生地等。這些屬性中有哪個可以作為Person 的標識符嗎?名字不可以作為標識符,因為可能有很多人擁有同一個名字。如果我們只
考慮兩個人的名字的話,我們不能使用同一個名字來區分他們兩個。我們也不能使用出生日期作為標識符,因為會有很多人出在同一天出生。同樣也不能用出生地作為標識符。一個對象必須與其他的對象區分開來,即使是它們擁有著相同的屬性。錯誤的標識符可能會導致資料混亂。
考慮一下一個銀行會計系統。每一個賬戶擁有它自己的數字碼。每一個賬戶可以用它的數字碼來精確標識。這個數字碼在系統的生命週期中會保持不變,並保證延續性。賬戶碼可以作為一個對象存在於記憶體中,也可以被在記憶體中銷毀,發送到資料庫中。當這個賬戶被關閉時,它還可以被歸檔,只要還有人對它感興趣,它就依然在某處存在。不論它的表現形式如何,數字碼會保持一致。因此,在軟體中實現實體意味著建立標識符。對一個人而言,其標識符可能是屬性的組合:名稱,出生日期,出生地,父母名稱、當前地址。在美國,社會保險號也會用來建立標識符。對一個銀行賬戶來說,帳號看上去已經足可以作為標識符了。通常標識符或是對
象的一個屬性(或屬性的組合),一個專門為儲存和表現標識符而建立的屬性,也或是一種行為。對兩個擁有不同標識符的對象來說,能用系統輕易地把它們區分開來,或者兩個使用了相同標識符對象能被系統看成是相同的,這些都是非常重要的。如果不能滿足這個條件,整個系統可能是有問題的。
有很多不同的方式來為每一個對象建立一個唯一的標識符:可能由一個模型來自動產生ID,在軟體中內部使用,不會讓它對使用者可見;它可能是資料庫表的一個主鍵,會被保證在資料庫中是唯一的。只要對象從資料庫中被檢索,它的ID 就會被檢索出並在記憶體中被重建;ID 也可能由使用者建立,例如每個機場會有一個關聯的代
碼。每個機場擁有一個唯一的字串ID,這個字串是在世界範圍內通用的,被世界上的每一個旅行代理使用以標識它們的旅行計劃中涉及的機場。另一種解決方案是使用對象的屬性來建立標識符,當這個屬性不足以代表標識符時,另一個屬性就會被加入以協助確定每一個對象。
當一個對象可以用其標識符而不是它的屬性來區分時,可以將它作為模型中的主要定義。保證類定義簡潔並關注生命週期的延續性和可標識性。對每個對象定義一個有意義的區分,而不管它的形式或者曆史。警惕要求使用屬性匹配對象的需求。定義一個可以保證對每一個對象產生一個唯一的結果的操作,這個過程可能需要某個符號以保證唯一性。這意味著標識可以來自外部,或者它可以是由系統產生、使用任意的標識符,但它必須符合模型中的身份差別。模型必須定義哪些被看作同一事物。
實體是領域模型中非常重要的對象,並且它們應該在建模過程開始時就被考慮。決定一個對象是否需要成為一個實體也很重要,這會在下一個模型中被討論。
由於篇幅的原因,這裡也不過多的寫了,大家可以先下載DDD的精簡版看看,需要的話,留下Email,我給出DDD的完整版!
今天就到這裡了,還是希望多多見諒,支援!謝謝啊!