走向ASP.NET架構設計——第四章:業務層分層架構(后篇)
今天的內容比較簡單,也是本章的一個收尾!
Anemic Domain Model
這種模式和之前講述的Domain Model有很多的相似的地方。在之前的Domain Model中,每個業務類都包含了自己的業務邏輯和數據,以及對象之前的關系;但是在Anemic Domain Model,每個業務類僅僅只是包含了一些保存業務數據的屬性,把相應的業務規則從原本的業務類中移到了另外的一個專門的業務規則類(Specification Pattern,我們后面的章節講述),同時把相應的業務方法移到了service類中。之前在Domain Model中,service類包含了一些粗顆粒度的業務方法,現在service類中也包含了原本在業務類中的細粒度的業務方法。
我們再來看看在這種組織方式下,我們之前的代碼如何寫:
{
public Guid Id{ get; set; }
public decimal Deposit{ get; set; }
public decimal Withdraw{ get; set; }
public string Reference{ get; set; }
public DateTime Date{ get; set; }
public Guid BankAccountId{ get; set; }
}
大家看到上面的代碼,里面已經沒有了業務邏輯的方法和一些規則,這些東西都被移到了另外的一個類中:
{
private decimal _amountToWithdraw;
public BankAccountHasEnoughFundsToWithdrawSpecification(decimal amountToWithdraw)
{
_amountToWithdraw = amountToWithdraw;
}
public bool IsSatisfiedBy(BankAccount bankAccount)
{
return bankAccount.Balance >= _amountToWithdraw;
}
}
大家在看看service類,現在service類中方法就很多了:
{
private BankAccountService _bankAccountService;
private IBankAccountRepository _bankRepository;
public ApplicationBankAccountService() :
this (new BankAccountRepository(), new BankAccountService(new BankAccountRepository()))
{ }
public ApplicationBankAccountService(IBankAccountRepository bankRepository, BankAccountService bankAccountService)
{
_bankRepository = bankRepository;
_bankAccountService = bankAccountService;
}
public ApplicationBankAccountService(BankAccountService bankAccountService, IBankAccountRepository bankRepository)
{
_bankAccountService = bankAccountService;
_bankRepository = bankRepository;
}
public BankAccountCreateResponse CreateBankAccount(BankAccountCreateRequest bankAccountCreateRequest)
{
BankAccountCreateResponse bankAccountCreateResponse = new BankAccountCreateResponse();
BankAccount bankAccount = _bankAccountService.CreateBankAccount(bankAccountCreateRequest.CustomerName);
bankAccountCreateResponse.BankAccountId = bankAccount.AccountNo;
bankAccountCreateResponse.Success = true;
return bankAccountCreateResponse;
}
public void Deposit(DepositRequest depositRequest)
{
_bankAccountService.Deposit(depositRequest.AccountId, depositRequest.Amount, "");
}
public void Withdrawal(WithdrawalRequest withdrawalRequest)
{
_bankAccountService.Withdraw(withdrawalRequest.AccountId, withdrawalRequest.Amount, "");
}
public TransferResponse Transfer(TransferRequest request)
{
TransferResponse response = new TransferResponse();
try
{
_bankAccountService.Transfer(request.AccountIdTo, request.AccountIdFrom, request.Amount);
response.Success = true;
}
catch (InsufficientFundsException)
{
response.Message = "There is not enough funds in account no: " + request.AccountIdFrom.ToString();
response.Success = false;
}
return response;
}
public FindAllBankAccountResponse GetAllBankAccounts()
{
FindAllBankAccountResponse FindAllBankAccountResponse = new FindAllBankAccountResponse();
IList<BankAccountView> bankAccountViews = new List<BankAccountView>();
FindAllBankAccountResponse.BankAccountView = bankAccountViews;
foreach (BankAccount acc in _bankRepository.FindAll())
{
bankAccountViews.Add(ViewMapper.CreateBankAccountViewFrom(acc));
}
return FindAllBankAccountResponse;
}
public FindBankAccountResponse GetBankAccountBy(Guid Id)
{
FindBankAccountResponse bankAccountResponse = new FindBankAccountResponse();
BankAccount acc = _bankRepository.FindBy(Id);
BankAccountView bankAccountView = ViewMapper.CreateBankAccountViewFrom(acc);
foreach (Transaction tran in acc.Transactions)
{
bankAccountView.Transactions.Add(ViewMapper.CreateTransactionViewFrom(tran));
}
bankAccountResponse.BankAccount = bankAccountView;
return bankAccountResponse;
}
}
到這里為止,四種組織業務邏輯的模式就講述完了,每一種都有自己的用途,無所謂“一定用,或者一定不用”。到底是用哪種,都是根據項目和經驗而定。
DDD
下面我們就來進入DDD,這里只是講述了一下DDD中的一些基本概念,至于具體的講述DDD:
1、后面的章節會陸續的介紹。
2、閱讀《領域驅動設計.軟件核心復雜性應對之道》,如果朋友們有需要,留下自己的Email,我會發送給大家。
下面的一些的文字都是摘自一些書籍。目的只是一個為了讓大家快速的了解一下DDD。
DDD幾個概念:
分層架構
實體
值對象
服務
模塊
聚合
工廠
分層架構
當我們創建一個軟件應用時,這個應用的很大一部分是不能直接跟領域關聯的,但它們是基礎設施的一部分或者是為軟件服務的。最好能讓應用中的領域部分盡可能少地和其他的部分摻雜在一起,因為一個典型的應用包含了很多和數據庫訪問,文件或網絡訪問以及用戶界面等相關的代碼。
在一個面向對象的程序中,用戶界面、數據庫以及其他支持性代碼經常被直接寫到業務對象中。附加的業務邏輯被嵌入到UI 組件和數據庫腳本的行為中。之所以這樣做的某些原因是這樣可以很容易地讓事情快速工作起來。
但是,當領域相關的代碼被混入到其他層時,要閱讀和思考它也變得極其困難。表面看上去是對UI 的修改,卻變成了對業務邏輯的修改。對業務規則的變更可能需要謹慎跟蹤用戶界面層代碼、數據庫代碼以及其他程序元素。實現粘連在了一起,模型驅動對象于是變得不再可行。也很難使用自動化測試。對于每個活動中涉及到的技術和邏輯,程序必須保持簡單,否則就會變得很難理解。因此,將一個復雜的程序切分成層。開發每一個層中內聚的設計,讓每個層僅依賴于它底下的那層。遵照標準的架構模式以提供層的低耦合。將領域模型相關的代碼集中到一個層中,把它從用戶界面、應用和基礎設施代碼中分隔開來。釋放領域對象的顯示自己、保存自己、管理應用任務等職責,讓它專注于展現領域模型。這會讓一個模型進一步富含知識,更清晰地捕獲基礎的業務知識,讓它們正常工作。
一個通用領域驅動設計的架構性解決方案包含4 個概念層:
將應用劃分成分離的層并建立層間的交換規則很重要。如果代碼沒有被清晰隔離到某層中,它會迅即混亂,因為它變得非常難以管理變更。在某處對代碼的一個簡單修改會對其他地方的代碼造成不可估量的結果。領域層應該關注核心的領域問題。它應該不涉及基礎設施類的活動。用戶界面既不跟業務邏輯緊密捆綁也不包含通常屬于基礎設施層的任務。在很多情況下應用層是必要的。它會成為業務邏輯之上的管理者,用來監督和協調應用的整個活動。
例如,對一個典型的交互型應用,領域和基礎設施層看上去會這樣:用戶希望預定一個飛行路線,要求用一個應用層中的應用服務來完成。應用依次從基礎設施中取得相關的領域對象,調用它們的相關方法,比如檢查與另一個已經被預定的飛行線路的安全邊界。當領域對象執行完所有的檢查并修改了它們的狀態決定后,應用服
務將對象持久化到基礎設施中。
實體
有一類對象看上去好像擁有標識符,它的標識符在歷經軟件的各種狀態后仍能保持一致。對這些對象來講這已經不再是它們關心的屬性,這意味著能夠跨越系統的生命周期甚至能超越軟件系統的一系列的延續性和標識符。我們把這樣的對象稱為實體。
OOP 語言會把對象的實例放于內存,它們對每個對象會保持一個對像引用或者是記錄一個對象地址。在給定的某個時刻,這種引用對每一個對象而言是唯一的,但是很難保證在不確定的某個時間段它也是如此。實際上恰恰相反。對象經常被移出或者移回內存,它被序列化后在網絡上傳輸,然后在另一端被重新建立,或者它們都被消除。在程序的運行環境中,那個看起來像標識符的引用關系其實并不是我們在談論的標識符。
如果有一個存放了天氣信息(如溫度)的類,很容易產生同一個類的不同實例,這兩個實例都包含了同樣的值,這兩個對象是完全相當的,可以用其中一個跟另一個交換,但它們擁有不同的引用,它們不是實體。如果我們要用軟件程序實現一個“人”的概念,我們可能會創建一個Person 類,這個類會帶有一系列的屬性,如:名稱,出生日期,出生地等。這些屬性中有哪個可以作為Person 的標識符嗎?名字不可以作為標識符,因為可能有很多人擁有同一個名字。如果我們只
考慮兩個人的名字的話,我們不能使用同一個名字來區分他們兩個。我們也不能使用出生日期作為標識符,因為會有很多人出在同一天出生。同樣也不能用出生地作為標識符。一個對象必須與其他的對象區分開來,即使是它們擁有著相同的屬性。錯誤的標識符可能會導致數據混亂。
考慮一下一個銀行會計系統。每一個賬戶擁有它自己的數字碼。每一個賬戶可以用它的數字碼來精確標識。這個數字碼在系統的生命周期中會保持不變,并保證延續性。賬戶碼可以作為一個對象存在于內存中,也可以被在內存中銷毀,發送到數據庫中。當這個賬戶被關閉時,它還可以被歸檔,只要還有人對它感興趣,它就依然在某處存在。不論它的表現形式如何,數字碼會保持一致。因此,在軟件中實現實體意味著創建標識符。對一個人而言,其標識符可能是屬性的組合:名稱,出生日期,出生地,父母名稱、當前地址。在美國,社會保險號也會用來創建標識符。對一個銀行賬戶來說,賬號看上去已經足可以作為標識符了。通常標識符或是對
象的一個屬性(或屬性的組合),一個專門為保存和表現標識符而創建的屬性,也或是一種行為。對兩個擁有不同標識符的對象來說,能用系統輕易地把它們區分開來,或者兩個使用了相同標識符對象能被系統看成是相同的,這些都是非常重要的。如果不能滿足這個條件,整個系統可能是有問題的。
有很多不同的方式來為每一個對象創建一個唯一的標識符:可能由一個模型來自動產生ID,在軟件中內部使用,不會讓它對用戶可見;它可能是數據庫表的一個主鍵,會被保證在數據庫中是唯一的。只要對象從數據庫中被檢索,它的ID 就會被檢索出并在內存中被重建;ID 也可能由用戶創建,例如每個機場會有一個關聯的代
碼。每個機場擁有一個唯一的字符串ID,這個字符串是在世界范圍內通用的,被世界上的每一個旅行代理使用以標識它們的旅行計劃中涉及的機場。另一種解決方案是使用對象的屬性來創建標識符,當這個屬性不足以代表標識符時,另一個屬性就會被加入以幫助確定每一個對象。
當一個對象可以用其標識符而不是它的屬性來區分時,可以將它作為模型中的主要定義。保證類定義簡潔并關注生命周期的延續性和可標識性。對每個對象定義一個有意義的區分,而不管它的形式或者歷史。警惕要求使用屬性匹配對象的需求。定義一個可以保證對每一個對象產生一個唯一的結果的操作,這個過程可能需要某個符號以保證唯一性。這意味著標識可以來自外部,或者它可以是由系統產生、使用任意的標識符,但它必須符合模型中的身份差別。模型必須定義哪些被看作同一事物。
實體是領域模型中非常重要的對象,并且它們應該在建模過程開始時就被考慮。決定一個對象是否需要成為一個實體也很重要,這會在下一個模型中被討論。
由于篇幅的原因,這里也不過多的寫了,大家可以先下載DDD的精簡版看看,需要的話,留下Email,我給出DDD的完整版!
今天就到這里了,還是希望多多見諒,支持!謝謝啊!