閱讀目錄 前言 回顧 梳理 實現 結語 一、前言 之前的文章中已經涉及到了購買商品加入購物車,購物車內購物項的金額計算等功能。本篇準備把剩下的購物車的基本概念一次處理完。 二、回顧 在動手之前我對之前的購買上下文內對象做了一次回顧。先梳理一下已經在上下文內出現的領域對象,如圖1所示: 【圖1】 在梳 ...
閱讀目錄
一、前言
之前的文章中已經涉及到了購買商品加入購物車,購物車內購物項的金額計算等功能。本篇準備把剩下的購物車的基本概念一次處理完。
二、回顧
在動手之前我對之前的購買上下文內對象做了一次回顧。先梳理一下已經在上下文內出現的領域對象,如圖1所示:
【圖1】
在梳理的過程中,我把原來Cart.AddCartItem(string productId, int quantity, decimal price)重構為了Cart.AddCartItem(Product product, int quantity),這樣的好處的是2個:
1.更清晰的表述出了在購物車中添加商品的意思。
2.約束了外部只能通過Product對象來進行商品的添加,這樣在Product構造函數中的約束在這裡無需再次驗證(如salename不能空等)。
三、梳理
目前的購物車中在操作上的方法只有一個。參照目前主流電商平臺的設計,我們需要增加:
1.修改數量
2.刪除
3.選擇參與的促銷(如果存在多個非單品級促銷)
4.收藏商品
前面3個比較簡單,都是購物車自身的概念,只有其中第四點超出了購物車自身的範疇,並且筆者認為收藏本就不是購物車特有的概念,而是在任何看得到商品的地方都可以做添加收藏的操作。那麼自然引出了一個新的概念——收藏夾。看下最新的UML圖,如圖2所示:
【圖2】
我想會有一部分同學在設計收藏夾(Favorites)的時候會以另外的方式來做,比如像下圖3這樣:
【圖3】
這裡我認為這樣考慮的原因可能是由於DBFirst的思想導致的,因為圖2中的“收藏夾”僅僅是維護了一個“用戶”與“收藏項”之間的關係,那麼只要在“收藏項”上增加一個UserId就直接可以省去了這一層關係,並且數據結構更加簡單。這時候我們就需要註意了,千萬不能有DBFirst思想去影響領域的建模,這樣的方式會把“添加購物項”這類的業務含義泄露到了Repository層或者Application層去實現,導致無法用通用語言進行完整的業務描述了。
並且在這個場景下,我個人觀點認為,收藏商品其實只是為商品的展示途徑中增加了一種途徑而已,所以它應該被設計為獨立存在的,由它自身來管理這些“被收藏的商品”,它的存在與否都不影響其它領域對象。
四、實現
要實現這4個操作,那麼需要在ICartService中增加下麵4個介面:
Result ChangeQuantity(string userId, string id, int quantity); Result DeleteCartItem(string userId, string id); Result AddToFavorites(string userId, string productId); Result ChangeMultiProductsPromotion(string userId, string productId, string selectedMultiProductsPromotionId);
其中的部分實現如下:
public Result AddToFavorites(string userId, string productId) { var cart = _confirmUserCartExistedDomainService.GetUserCart(userId); if (cart.IsEmpty()) { return Result.Fail("當前購物車中並沒有商品"); } var cartItem = cart.GetCartItem(productId); if (cartItem == null) { return Result.Fail("該購物項已不存在"); } var favorites = DomainRegistry.FavoritesRepository().GetByUserId(userId) ?? new Favorites(userId, null); favorites.AddFavoritesItem(cartItem); DomainRegistry.FavoritesRepository().Save(favorites); return Result.Success(); }
其中關於Favorites的構造函數我是這麼做的:
public Favorites(string userId, IEnumerable<FavoritesItem> favoritesItems) { if (string.IsNullOrWhiteSpace(userId)) throw new ArgumentNullException("userId"); this.UserId = userId; this._favoritesItems = new List<FavoritesItem>(); if (favoritesItems != null && favoritesItems.Any()) { foreach (var favoritesItem in favoritesItems) { AddFavoritesItem(favoritesItem); } } }
這樣可以重用AddFavoritesItem中的一些守衛操作,保證在業務產生變動之後歷史數據從DB取出來的時候經過一次最新的業務驗證,確保數據在流轉過程中的合法性。這個方式可以擇機運用在任何聚合的構造函數中。
五、結語
本篇主要的觀點還是在建模上的思維慣性,拋開DB,拋開DB,拋開DB,重要的事情說3遍。
本文的源碼地址:https://github.com/ZacharyFan/DDDDemo/tree/Demo10。
作者:Zachary_Fan
出處:http://www.cnblogs.com/Zachary-Fan/p/DDD_10.html