天天看點

走向.NET架構設計—第四章—業務層分層架構(後篇)

走向.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幾個概念:

  分層架構

  實體

  值對象

  服務

  子產品

  聚合

  工廠

走向.NET架構設計—第四章—業務層分層架構(後篇)

  當我們建立一個軟體應用時,這個應用的很大一部分是不能直接跟領域關聯的,但它們是基礎設施的一部分或者是為軟體服務的。最好能讓應用中的領域部分盡可能少地和其他的部分摻雜在一起,因為一個典型的應用包含了很多和資料庫通路,檔案或網絡通路以及使用者界面等相關的代碼。

  在一個面向對象的程式中,使用者界面、資料庫以及其他支援性代碼經常被直接寫到業務對象中。附加的業務邏輯被嵌入到UI 元件和資料庫腳本的行為中。之是以這樣做的某些原因是這樣可以很容易地讓事情快速工作起來。

  但是,當領域相關的代碼被混入到其他層時,要閱讀和思考它也變得極其困難。表面看上去是對UI 的修改,卻變成了對業務邏輯的修改。對業務規則的變更可能需要謹慎跟蹤使用者界面層代碼、資料庫代碼以及其他程式元素。實作粘連在了一起,模型驅動對象于是變得不再可行。也很難使用自動化測試。對于每個活動中涉及到的技術和邏輯,程式必須保持簡單,否則就會變得很難了解。是以,将一個複雜的程式切分成層。開發每一個層中内聚的設計,讓每個層僅依賴于它底下的那層。遵照标準的架構模式以提供層的低耦合。将領域模型相關的代碼集中到一個層中,把它從使用者界面、應用和基礎設施代碼中分隔開來。釋放領域對象的顯示自己、儲存自己、管理應用任務等職責,讓它專注于展現領域模型。這會讓一個模型進一步富含知識,更清晰地捕獲基礎的業務知識,讓它們正常工作。

  一個通用領域驅動設計的架構性解決方案包含4 個概念層:

走向.NET架構設計—第四章—業務層分層架構(後篇)

  将應用劃分成分離的層并建立層間的交換規則很重要。如果代碼沒有被清晰隔離到某層中,它會迅即混亂,因為它變得非常難以管理變更。在某處對代碼的一個簡單修改會對其他地方的代碼造成不可估量的結果。領域層應該關注核心的領域問題。它應該不涉及基礎設施類的活動。使用者界面既不跟業務邏輯緊密捆綁也不包含通常屬于基礎設施層的任務。在很多情況下應用層是必要的。它會成為業務邏輯之上的管理者,用來監督和協調應用的整個活動。

  例如,對一個典型的互動型應用,領域和基礎設施層看上去會這樣:使用者希望預定一個飛行路線,要求用一個應用層中的應用服務來完成。應用依次從基礎設施中取得相關的領域對象,調用它們的相關方法,比如檢查與另一個已經被預定的飛行線路的安全邊界。當領域對象執行完所有的檢查并修改了它們的狀态決定後,應用服

務将對象持久化到基礎設施中。 

  有一類對象看上去好像擁有辨別符,它的辨別符在曆經軟體的各種狀态後仍能保持一緻。對這些對象來講這已經不再是它們關心的屬性,這意味着能夠跨越系統的生命周期甚至能超越軟體系統的一系列的延續性和辨別符。我們把這樣的對象稱為實體。

  OOP 語言會把對象的執行個體放于記憶體,它們對每個對象會保持一個對像引用或者是記錄一個對象位址。在給定的某個時刻,這種引用對每一個對象而言是唯一的,但是很難保證在不确定的某個時間段它也是如此。實際上恰恰相反。對象經常被移出或者移回記憶體,它被序列化後在網絡上傳輸,然後在另一端被重建立立,或者它們都被消除。在程式的運作環境中,那個看起來像辨別符的引用關系其實并不是我們在談論的辨別符。

  如果有一個存放了天氣資訊(如溫度)的類,很容易産生同一個類的不同執行個體,這兩個執行個體都包含了同樣的值,這兩個對象是完全相當的,可以用其中一個跟另一個交換,但它們擁有不同的引用,它們不是實體。如果我們要用軟體程式實作一個“人”的概念,我們可能會建立一個Person 類,這個類會帶有一系列的屬性,如:名稱,出生日期,出生地等。這些屬性中有哪個可以作為Person 的辨別符嗎?名字不可以作為辨別符,因為可能有很多人擁有同一個名字。如果我們隻

考慮兩個人的名字的話,我們不能使用同一個名字來區分他們兩個。我們也不能使用出生日期作為辨別符,因為會有很多人出在同一天出生。同樣也不能用出生地作為辨別符。一個對象必須與其他的對象區分開來,即使是它們擁有着相同的屬性。錯誤的辨別符可能會導緻資料混亂。

  考慮一下一個銀行會計系統。每一個賬戶擁有它自己的數字碼。每一個賬戶可以用它的數字碼來精确辨別。這個數字碼在系統的生命周期中會保持不變,并保證延續性。賬戶碼可以作為一個對象存在于記憶體中,也可以被在記憶體中銷毀,發送到資料庫中。當這個賬戶被關閉時,它還可以被歸檔,隻要還有人對它感興趣,它就依然在某處存在。不論它的表現形式如何,數字碼會保持一緻。是以,在軟體中實作實體意味着建立辨別符。對一個人而言,其辨別符可能是屬性的組合:名稱,出生日期,出生地,父母名稱、目前位址。在美國,社會保險号也會用來建立辨別符。對一個銀行賬戶來說,賬号看上去已經足可以作為辨別符了。通常辨別符或是對

象的一個屬性(或屬性的組合),一個專門為儲存和表現辨別符而建立的屬性,也或是一種行為。對兩個擁有不同辨別符的對象來說,能用系統輕易地把它們區分開來,或者兩個使用了相同辨別符對象能被系統看成是相同的,這些都是非常重要的。如果不能滿足這個條件,整個系統可能是有問題的。

  有很多不同的方式來為每一個對象建立一個唯一的辨別符:可能由一個模型來自動産生ID,在軟體中内部使用,不會讓它對使用者可見;它可能是資料庫表的一個主鍵,會被保證在資料庫中是唯一的。隻要對象從資料庫中被檢索,它的ID 就會被檢索出并在記憶體中被重建;ID 也可能由使用者建立,例如每個機場會有一個關聯的代

碼。每個機場擁有一個唯一的字元串ID,這個字元串是在世界範圍内通用的,被世界上的每一個旅行代理使用以辨別它們的旅行計劃中涉及的機場。另一種解決方案是使用對象的屬性來建立辨別符,當這個屬性不足以代表辨別符時,另一個屬性就會被加入以幫助确定每一個對象。

  當一個對象可以用其辨別符而不是它的屬性來區分時,可以将它作為模型中的主要定義。保證類定義簡潔并關注生命周期的延續性和可辨別性。對每個對象定義一個有意義的區分,而不管它的形式或者曆史。警惕要求使用屬性比對對象的需求。定義一個可以保證對每一個對象産生一個唯一的結果的操作,這個過程可能需要某個符号以保證唯一性。這意味着辨別可以來自外部,或者它可以是由系統産生、使用任意的辨別符,但它必須符合模型中的身份差别。模型必須定義哪些被看作同一事物。

  實體是領域模型中非常重要的對象,并且它們應該在模組化過程開始時就被考慮。決定一個對象是否需要成為一個實體也很重要,這會在下一個模型中被讨論。

   

  由于篇幅的原因,這裡也不過多的寫了,大家可以先下載下傳DDD的精簡版看看,需要的話,留下Email,我給出DDD的完整版!

  今天就到這裡了,還是希望多多見諒,支援!謝謝啊!

  • 打賞
  • 收藏
  • 評論
  • 分享

    微網誌

    QQ

    微信

    走向.NET架構設計—第四章—業務層分層架構(後篇)
  • 舉報

繼續閱讀