標籤:cts 業務 rtt 資料類型 處理 物理 emd 合作 font
閱讀目錄
- 前言
- 如何在一個項目中實現多個內容相關的業務
- 售價上下文與購買內容相關的整合
- 結語
一、前言
前幾篇已經實現了一個最簡單的購買過程,這次開始往這個過程中增加一些東西。比如促銷、會員價等,在我們的第一篇文章(如何一步一步用DDD設計一個電商網站(一)—— 先理解核心概念)中規劃的上下文映射圖可以看到,這些都屬於一個獨立的上下文(售價上下文)。
二、如何在一個項目中實現多個內容相關的業務
一般情況下,為了更好的分而治之,把不同的上下文作為單獨的service,然後通過rpc架構(如WCF)來對其訪問是個比較常見的做法。但是在一些小型團隊中,雖然劃分出了不同上下文,但是我們的Team Dev還是同一個。在這種情況下,我個人一般的做法是直接在同一個解決方案中建立不同的項目去做,但是這裡需要在解決方案中明確的劃分好不同上下文之間的邊界,通過代碼審核等手段管理好這個邊界不被破壞。
【圖1】
增加的幾個項目1所示。
三、售價上下文與購買內容相關的整合
根據我們第一篇如何一步一步用DDD設計一個電商網站(一)—— 先理解核心概念所定義的上下文映射圖和9種整合模式可以看出,這2個上下文在同一個子域中,並且在我們實際業務情境中,這2者又是相輔相成,所以售價上下文和購買上下文是一種合作關係。確立這個關係之後,那麼這個促銷的計算邏輯到底是放到哪個上下文種做更合適呢?我們先整理一下幾種可能的方式:
1.購買上下文把購物車中的商品資訊丟給售價上下文 --> 售價上下文進行計算 --> 把結果再返回給購買上下文。
2.購買上下文從銷價上下文擷取相關會員價和促銷資訊 --> 再本地的購物車對象基礎上進行運算,並直接可運用結果。
3.再抽出一個專門的計算服務(隸屬於售價上下文),去做這個計算的動作。購買上下文把購物車中的商品資訊丟給計算服務 --> 計算上下文從銷價上下文擷取到相關會員價和促銷資訊 --> 計算 --> 返回結果給購買上下文
我相信1和2是比較主流的2個方式。但是方式2是把售價上下文僅作為一種資料的提供方,這就把合作關係變成了一個上下遊的關係,並且這種方式使得促銷規則和購物車強耦合到了一起,不利於促銷規則的變化。在這裡售價上下文只起了一個簡單的資料維護作用,無法完全控制“售價”的定義,沒有很好的做到職責分離。方式1和3對購買上下文來說其實是沒有區別的,只是方式3讓整個資料互動的鏈路多了一層,會產生額外的開銷,好處是服務的粒度更細了,需要結合實際情況權衡一下得失。這裡我選擇1方式來實現,因為我們在項目初期,還是儘可能的減少非業務目的的拆分導致的額外成本。
好了,確定了整合方式之後,先把2個上下文之間用於資料互動的DTO模型定一下,如2(售價內容相關的DTO模型),圖3(購買上下文中與前者對應的值對象)。
【圖2】
【圖3】
另外在圖3中可以發現增加了一個ISellingPriceService,抽象了與售價內容相關的互動。那麼我們在Mall.Infrastructure.Translators項目中增加對這個內容相關的防腐層處理,老3樣SellingPriceAdapter(發起上下文資料請求的適配器)、SellingPriceService(實現ISellingPriceService)、SellingPriceTranslator(把遠端資料物件轉換成本地的值對象),代碼很簡單大家可以在源碼中查看。需要注意的是,這裡的Mall.Infrastructure.Translators項目僅增加了對Mall.Application.SellingPrice項目的引用,類似於把它當作一個遠端資源來對待(按上面所說,如果實際由不同的團隊負責可以物理上的分離到2個解決方案中)。
最後建立一個CartService,裡面的GetCart()方法——擷取購物車資訊,來作為調用發起方。這其中的實現使用了最簡單的方式,本地不做任何的資料冗餘,代碼如下:
public class CartService { private readonly static ConfirmUserCartExistedDomainService _confirmUserCartExistedDomainService = new ConfirmUserCartExistedDomainService(); public CartDTO GetCart(string userId) { var cart = _confirmUserCartExistedDomainService.GetUserCart(userId); if (cart.IsEmpty()) { return null; } var sellingPriceCart = DomainRegistry.SellingPriceService().Calculate(cart); return ConvertToCart(cart, sellingPriceCart); } private CartDTO ConvertToCart(Cart cart, SellingPriceCart sellingPriceCart) { return new CartDTO { CartItemGroups = sellingPriceCart.CalculatedFullGroups.Select(ent => new CartItemGroupDTO { CartItems = ent.CalculatedCartItems.Select(e => ConvertToCartItem(e, cart.GetCartItem(e.ProductId))).ToArray(), ReducePrice = ent.ReducePrice }).ToArray(), CartItems = sellingPriceCart.CalculatedCartItems.Select(ent => ConvertToCartItem(ent, cart.GetCartItem(ent.ProductId))).ToArray() }; } private CartItemDTO ConvertToCartItem(SellingPriceCartItem sellingPriceCartItem, CartItem cartItem) { var product = DomainRegistry.ProductService().GetProduct(cartItem.ProductId); return new CartItemDTO { ProductId = cartItem.ProductId, ProductName = product == null ? "商品已失效" : product.SaleName, ReducePrice = sellingPriceCartItem.ReducePrice, SalePrice = cartItem.Price }; } }
四、結語
這次有個全域改動這裡提一下,我在本次編碼中把之前所有的Guid標識全部改為了string類型,弱化了對唯一標識的資料類型約束,提高可擴充性(如自增欄位、其它自訂的唯一標識等),另外還把購物項中的Price改為了UnitPrice,讓語義更加清晰。本篇內容比較粗,歡迎大家探討。
本文的源碼地址:https://github.com/ZacharyFan/DDDDemo/tree/Demo6。
Zachary_Fan
出處:http://www.cnblogs.com/Zachary-Fan/p/6087752.html
如何一步一步用DDD設計一個電商網站(六)—— 給購物車加點料,整合售價上下文