程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Spring.NET企業架構實踐(二)

Spring.NET企業架構實踐(二)

編輯:關於.NET

Nhibernate + WCF + ASP.NET MVC + NVelocity 對PetShop4.0重構(二)——領域模型

什麼是領域模型?領域模型是對領域內的概念類或現實世界中對象的可視化表示。又稱概念模型、領域對象模型、分析對象模型。它專 注於分析問題領域本身,發掘重要的業務領域概念,並建立業務領域概念之間的關系。

當我們不再對一個新系統進行數據庫提煉時,取而代之的時面向對象的模型提煉。我們必須大刀闊斧地對業務領域進行細分,將一個復 雜的業務領域劃分為多個小的子領域,同時還必須分清重點和次要部分,抓住核心領域概念,實現重點突破。

著名建模專家Eric Evans提出了Domain Driven Design(領域驅動設)。最初層次只分為三層:表現層、業務層和持久層,DDD其實告訴 我們如何讓實現業務層。

領域模型種類

傳統模型分為兩種:實體(Entity)和值對象(Value Object),服務(Service)成為第三種模型元素。

領域驅動設計有兩種常用的模式:貧血模式和充血模式。

貧血模式:只有狀態,沒有行為。

貧血模型

public class OrderInfo
     {
         public virtual int OrderId { get; set; }
         public virtual DateTime Date { get; set; }
         public virtual string UserId { get; set; }
         public virtual decimal OrderTotal { get; set; }
         public virtual IList<LineItemInfo> LineItems { get; set; }
         public virtual int? AuthorizationNumber { get; set; }
     }
}

Service

public class OrderManager
     {
         public IOrderDao CurrentDao { get; set; }
         public object Save(OrderInfo entity)
         {
             ProcessCreditCard(entity);
             return CurrentDao.Save(entity);
         }
         private void ProcessCreditCard(OrderInfo entity)
         {
             Random rnd = new Random();
             entity.AuthorizationNumber = (int)(rnd.NextDouble() * int.MaxValue);
         }
     }

從上面貧血模式的Domain Object可看出,其類代碼中只有屬性,這種Domain Object只是單純的數據載體。雖然它的名字是Domain Object,卻沒有包含任何業務對象的相關方法。這樣,方法都寫在服務層(Service)中,會使得層次更加明顯。但隨著業務方法增多,會使 服務層中的代碼過於臃腫,從而難以維護。

充血模式:既有狀態,又有行為。

充血模型

 public class OrderInfo
     {
         public virtual int OrderId { get; set; }
         public virtual DateTime Date { get; set; }
         public virtual string UserId { get; set; }
         public virtual decimal OrderTotal { get; set; }
         public virtual IList<LineItemInfo> LineItems { get; set; }
         public virtual int? AuthorizationNumber { get; set; }
         public void ProcessCreditCard()
         {
             Random rnd = new Random();
             this.AuthorizationNumber = (int)(rnd.NextDouble() * int.MaxValue);
         }
     } 

從充血模型的代碼中可以看出,其類既有屬性又有方法,服務層(Service)的代碼比較少,相當於門面。方法和屬性的混合編碼方式, 在被多個業務類調用的情況下,代碼的內聚性比較好。假設AManager和BManager都使用到了 OrderInfo,ProcessCreditCard方法只在 OrderInfo中寫了一次就足夠了。這樣,職責性就更加明顯。

這兩種模型都是穩定的,至於用哪種模型,還是需要根據具體需求來斷定。在大多數.NET項目中使用貧血模型要比使用充血模型的情況 多。

領域模型的對應關系一般有三種:一對一,一對多(多對一),多對多。

一對多

 public class OrderInfo
     {
         public virtual int? OrderId { get; set; }
         public virtual DateTime Date { get; set; }
         public virtual string UserId { get; set; }
         public virtual decimal OrderTotal { get; set; }
         public virtual IList<LineItemInfo> LineItems { get; set; }
         public virtual int? AuthorizationNumber { get; set; }
     }
public class LineItemInfo
     {
         public virtual string Id { get; set; }
         public virtual string Name { get; set; }
         public virtual int Quantity { get; set; }
         public virtual decimal Price { get; set; }
         public virtual string ProductName { get; set; }
         public virtual string Image { get; set; }
         public virtual string CategoryId { get; set; }
         public virtual string ProductId { get; set; }
         public virtual OrderInfo Order { get; set; }
     }

OrderInfo和LineItemInfo是一對多的關系。在NHibernate的雙向外鍵中,實體之間是循環引用的,所以不能直接序列化。作為每個實 體來說他們都是POJO(脫離框架一樣能使用),在更換ORM框架的情況下一樣能夠使用他們。

“一對多(多對一)”一般用於集合外鍵的ORM描述。“多對多”的關系一般需要一個外鍵關系表。“一對一”的關系一般用於繼承映射 或者用於把一個多字段的表拆分成多個輔助表。

NHibernate的映射配置有兩種方式,一種是在單獨的.hbm.xml文件中配置映射關系,另一種是使用NHibernate.Mapping.Attributes的 方式標注映射關系。後者省略了很多繁瑣的配置文件,但需要應用了NHibernate.Mapping.Attributes程序集。前者有大量的配置文件,但 是不需要引用其他程序集,並且在脫離ORM框架下也能夠使用。所以我個人傾向於前者。

OrderInfo.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="PetShopOrder.Domain"  namespace="PetShopOrder.Domain">
   <class name="PetShopOrder.Domain.OrderInfo, PetShopOrder.Domain" table="Orders" lazy="true" >
     <id name="OrderId" column="OrderId" type="Int32" >
       <generator class="native" />
     </id>
     <property name="Date" type="DateTime">
       <column name="OrderDate" not-null="true"></column>
     </property>
     <property name="UserId" type="String">
       <column name="UserId" length="20" not-null="true"></column>
     </property>
     <property name="AuthorizationNumber" type="Int32">
       <column name="AuthorizationNumber" not-null="false"></column>
     </property>
     <property name="OrderTotal" type="Decimal">
       <column name="TotalPrice" precision="10" scale="2" not-null="true"></column>
     </property>
     <bag name="LineItems" inverse="true" lazy="true" generic="true" cascade="all" table="LineItem">
       <key column="OrderID"/>
       <one-to-many class="PetShopOrder.Domain.LineItemInfo, PetShopOrder.Domain" />
     </bag>
   </class>
</hibernate-mapping>

在持久層中每個實體模型都對應了數據庫中的一個表,每個屬性都對應了表中的一個字段,每個實體對象對應了表中的一條記錄。

在服務層中,需要得到的模型對象往往和持久層的實體模型不一致,如某個類中有屬性:數量,單價和金額,但是數據庫中只有數量和 單價。這時候需要建立一種模型——業務模型。然而Linq和匿名類的出現緩解了這一點。在門面層調用服務層返回DTO對象的過程中,通過 Linq查詢實體模型來方式返回DTO。這樣業務模型就能夠被省略(後面的文章會談到這一點)。

最後在設計領域模型中我們需要分清“主次”。當設計進銷存系統,業務類數據就數主要的,如采購,銷售,庫存信息。基礎資料數據 就是次要的,如供應商和客戶信息。當設計客戶關系管理系統時,客戶資料數據則是主要的,圍繞的客戶產生的活動,社交等數據則是輔 助數據。這就產生一個規律:主要數據的變化頻率比較高,輔助數據變化頻率比較低。在PetShop4.0中,主要數據當然是訂單。這樣我們 就需要想方設法去優化系統,以便於更好的處理訂單數據。

出處:http://www.cnblogs.com/GoodHelper/archive/2010/06/18/SpringNet_PetShop4_2.html

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved