.net架構設計讀書筆記--第三章 第9節 域模型實現(ImplementingDomain Model)

    咱們長時間爭論什麼方案是實現域業務領域層架構的最佳方法。最後,咱們用一個在線商店案例來講明,其中忽略了許多以前遇到的一些場景。在線商店對不少人來講更容易理解。 前端

1、在線商店項目簡介 web

1. 用例選擇 數據庫

Use-case後端

Description架構

Registers to the sitemvc

The user fills in the application form and becomes an official customer of the I-Buy-Stuff site.app

Log in to start using the siteasp.net

The user enters credentials and logs in. Credentials can be entered directly to the site or via a couple of social networks.ide

Subscribe to the newsletter函數

The user adds an email address to receive the newsletter.

Search an order

The user indicates the number of one of her orders and reviews details such as estimated delivery date, items, and costs.

Place an order

The user fills a shopping cart with a list of products and then proceeds with

making payment and entering the details related to delivery

2. 方法選擇

    在線商城的需求,幾乎全部的開發人員都或多或少的瞭解。在須要分析後和一般語言的定義下,將在線商城清晰的定義爲一些實體。能夠將項目中的類分開建立爲類庫。域模型將定義爲如下域實體:Admin, Customer, Order, OrderItem, Product, Subscriber, 和FidelityCard。

 

3. 設計在線商城項目結構

    前面的章節中專一於探討分層結構在DDD中的做用,如今看下實際項目中是如何使用的。下圖是VS2013中的項目結構

    圖中能夠的地到Demain Layer,Infrastructure Layer, 和site。IBuyStuff解決方案中,site目錄中實際上就是一個asp.net mvc應用程序。從邏輯上劃分,能夠將系統分爲前端和後端兩部分。Demain Layer 和Infrastructure Layer是後端的部分,後端儘量多的在同一個上下文中處理前端的動做。

 

4. 技術選擇

    IBuyStuff解決方案使用了一系列.net技術,大部分經過NuGet包引入。前面已經提到,前端是使用了ASP.NET Identity 的asp.net mvc5的應用程序。Twitter Bootstrap技術也用戶界中使用。針對於移動視圖,使用界面使用了WURFL來感知終端設備,Asp.net經過路由來展示View。

    後端的持久化使用了Entity Framework Code First實現。在示例代碼中本地的SqlServer實例在web站點的App_Data目錄下。最後,項目中全部的依賴注入都是使用Microsoft Patterns and Practices Unity。

5. 邊界上下文設計

    I-Buy-Stuff在線商城是一個刻意簡單小型、上下文邊界中業務不須要面對額外的需求。演示一個廣義的DDD架構它可能不是最佳的例子,但別一方面,一個太複雜的例子又太難講解。

在域上下文中分解業務,一個合理簡單的方案,將業務分爲如下幾個邊界上下文:

  • MembershipBC
  • OrderingBC
  • CatalogBC

MembershipBC用於身份認證和用戶帳戶管理。OrderingBC用於管理購物車和訂單處理,CatalogBC用於後臺管理,如產品更新、添加、刪除,更新庫存等。

 

OrderingBC最多是一個Asp.net Mvc應用程序,如在線商城。Membership在同一個應用中做爲不一樣的功能模塊獨立存在。CatalogBC是管理系統,更可能是數據庫的CRUD操做,沒在必要使用DDD設計。

6. 邊界上下文間的通訊

    Product的增刪改查什麼時候通知OrderingBC上下文邊界?OrderingBC要對數據儘量頻繁讀取來獲取CatalogBC對數據庫的更新是不太可能的,別外一種方法,當CatalogBC更新時,同時調用一個OrderingBC的RPC。在大型複雜的系統中,你甚至會用Service bus將架構中許多小模塊鏈接起來。

7. 添加其它的上下文邊界

    隨着商城的複雜度成長,你要添加其它的上下文,好比:ShippingBC,PricingBC,PurchaseBC等。你能夠將他們放到同下big上下文,能夠將他們分割成多個小的上下文。這兒沒有什麼硬性指標來決策怎麼作。

8. I-Buy-Stuff解決方案中的上下文圖表

    以下圖所示,I-Buy-Stuff上下文是一個ASP.NET MVC應用程序,它要在域模型架構上運行,是一個至關大的上下文,國爲它包含了會員、訂購邏輯、甚至是包含了購買上下文。I-Buy-Stuff上下文承載着大部分業務邏輯,Shipping和Purchase被實現爲服務形式。

 

 

 

2、高大尚的域模型建模

    現代軟件的根本問題是,開發都經常寧願關注代碼而不肯意麪對模型設計,這種方法自己沒有錯,可是你將面對的是難以管理的複雜度。若是你沒有進行域模型設計的狀況下寫出了成功的代碼,這樣作有錯嗎?固然沒錯,但你有可能在複雜度增加時暴露出問題。它可能出如今下一個項目或同一個項目中的需求增加和變動。

1. anemic model 弱模型

    DDD權威指出沒有模型沒有必要是弱模型。軟件設計中的面向對象指出,一個類是對數據和行爲的封裝,而一個模型只是用於映射數據庫,基本上沒有行爲方法,這樣的類被稱之爲弱類 anemic model。

    業務邏輯的位置

實際上,面向行爲本質上是找出業務邏輯模塊的位置。

class Invoice

{

public string Number {get; private set;}

public DateTime Date {get; private set;}

public Customer Customer {get; private set;}

public string PayableOrder {get; private set;}

public ICollection<InvoiceLine> Lines {get; private set;}

...

}

上面的Invoice類中沒有方法,是錯誤的寫法?仍是弱模型的一部分?若是對象僅沒有方法,它也不必定是一個弱模型。弱模型是指將業務邏輯放到了模型外的地方實現。

2. Entities scaffolding

    一個DDD實體應該是一個同時包含數據和行爲的類。一個實體可能有公有屬性,但他不會被用做爲一個數據容器。下面是DDD實體的特性

  • 良好的惟一性定義
  • 行爲經過方法來表示,不論是公有方法仍是非公有方法
  • 經過只讀屬性公開狀態
  • 用值對象代替不多用的基元類型
  • 使用工廠方法來代替多個構造函數

 

3.Value objects scaffolding

    與entity相似,value object是由一系列數據組成的類。行爲對值對象來講是不須要的。值對象也能夠有方法,可是這些方法都是一些輔助類方法。於entity不一樣,value object不須要惟一性,它們沒有可變狀態,它們只用於存儲數據。如DateTime類型的構造函數。

4. 明確合集

將話題轉到I-Buy-Stuff這個示例應用程序上,I-Buy-Stuff是一個簡單的在線商城,下圖反應出應用中實體的關係。在站點中,咱們認爲只有少許的用例,如:搜索,下單。Customer、Order、Product是合集,Customer和Produc是單件實體合集。

  •  Customer aggregate

    Customer的特色是有一些我的數據,如name,address,username,或payment details。咱們明確假設I-Buy-Stuff系統中只有一種類型用戶,不用作任何區分。如:法人客戶可能和我的客戶,法人客戶下可能包含多個我的客戶。I-Buy-Stuff選擇第3種表示方法,不區分。

Customer定義

public class Customer : IAggregateRoot

{

    public static Customer CreateNew(Gender gender, string id,string firstname, string lastname, string email)

    {

var customer = new Customer {

CustomerId = id,

Address = Address.Create(),

Payment = NullCreditCard.Instance,

Email = email,

FirstName = firstname,

LastName = lastname,

Gender = gender

};

return customer;

}

public string CustomerId { get; private set; }

public string PasswordHash { get; private set; }

public string FirstName { get; private set; }

public string LastName { get; private set; }

public string Email { get; private set; }

public Gender Gender { get; private set; }

public string Avatar { get; private set; }

public Address Address { get; private set; }

public CreditCard Payment { get; private set; }

public ICollection<Order> Orders { get; private set; }

// More methods here such as SetDefaultPaymentMode.

// A customer may have a default payment mode (e.g., credit card), but

// different orders can be paid in different ways.

...

}

其它幾個合集定義……

 

5. 實體持久化 Persisting the model

    域模型要支持持久化,常見的是用O/RM來實現,如Entity Framework Code-First 方法實現。本質上Entity Framework有兩種實現風格:Database First 與Code First。另外還有第三種風格,Model First,它是一種混合風格。Database-First方法中Entity Framework讀取數據庫結構,生成和一系列partial類型的弱類(anemic class),可使用partial來擴展類。Code-First風格是生成一系列類,如I-Buy-Stuff定義的合集,而後添加一個額外的層來映射到數據庫表。這個映射層告訴O/RM關於數據庫信息,及如何CRUD數據。I-Buy-Stuff示例中使用的是Code-First風格。

Code-First經過繼承DbContext類爲中心實現持久化。

public class DomainModelFacade : DbContext

{

    static DomainModelFacade()

    {

        Database.SetInitializer(new SampleAppInitializer());

    }

    public DomainModelFacade() : base("naa4e - 09")

    {

        Products = base.Set<Product>();

        Customers = base.Set<Customer>();

        Orders = base.Set<Order>();

        FidelityCards = base.Set<FidelityCard>();

    }

    public DbSet<Order> Orders { get; private set; }

    public DbSet<Customer> Customers { get; private set; }

    public DbSet<Product> Products { get; private set; }

    public DbSet<FidelityCard> FidelityCards { get; private set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)

    {

...

}

}

 

    Mapping properties to columns

    Code First中有兩種方法將properties映射到columns。能夠在demain class中使用data annotation attributes,也可使用Code-First API自帶的configuration classes來重寫OnModelCreating方法輕鬆實現。如:

modelBuilder.ComplexType<Money>();

modelBuilder.ComplexType<Address>();

modelBuilder.ComplexType<CreditCard>();

modelBuilder.Configurations.Add(new FidelityCardMap());

modelBuilder.Configurations.Add(new OrderMap());

modelBuilder.Configurations.Add(new CustomerMap());

modelBuilder.Configurations.Add(new OrderItemMap());

modelBuilder.Configurations.Add(new CurrencyMap());

 

 

3、實現業務邏輯

    不少狀況下,並不全部的業務邏輯都須要融入到域模型的類裏。而非業務邏輯的功能,如持久化功能也要添加到域模型中。在I-Buy-Stuff項目中有兩個主要的業務邏輯:查詢訂單和訂單下單。

1.查詢訂單

    下面的代碼中Controller接受一個查找訂單的用戶請求,controller方法中使用一個Service來獲取訂單數據。

public ActionResult SearchResults(int id)

{

var model = _service.FindOrder(id, User.Identity.Name);

return View(model);

}

public SearchOrderViewModel FindOrder(int orderId, string customerId)

{

    var order = _orderRepository.FindByCustomerAndId(orderId, customerId);

    if (order is NullOrder)

        return new SearchOrderViewModel();

    return SearchOrderViewModel.CreateFromOrder(order);

}

application service中FindOrder方法調用域服務處倉儲服務來獲取訂單。

 

2. 訂單下單

    當用戶在站點上點擊購買時,商城系統反回一個用戶界面,用於建立訂單,另外,還有添加到購物車等方法

public ActionResult New()

{

    var customerId = User.Identity.Name;

    var shoppingCartModel = _service.CreateShoppingCartForCustomer(customerId);

    shoppingCartModel.EnableEditOnShoppingCart = true;

    SaveCurrentShoppingCart(shoppingCartModel);

    return View("shoppingcart", shoppingCartModel);

}

 

    //添加到購物車

public ActionResult AddToShoppingCartCommand(int productId, int quantity = 1)

{

    var cartModel = RetrieveCurrentShoppingCart();

    cartModel = _service.AddProductToShoppingCart(cart, productId, quantity);

    SaveCurrentShoppingCart(cartModel);

    return RedirectToAction("AddTo");

}

 

public ShoppingCartViewModel AddProductToShoppingCart(ShoppingCartViewModel cart, int productId, int quantity)

{

    var product = (from p in cart.Products where p.Id == productId select p).Single();

    cart.OrderRequest.AddItem(quantity, product);

    return cart;

}

相關文章
相關標籤/搜索