如何一步一步用DDD設計一個電商網站(十)—— 一個完整的購物車


本系列所有文章

如何一步一步用DDD設計一個電商網站(一)—— 先理解核心概念

如何一步一步用DDD設計一個電商網站(二)—— 項目架構

如何一步一步用DDD設計一個電商網站(三)—— 初涉核心域

如何一步一步用DDD設計一個電商網站(四)—— 把商品賣給用戶

如何一步一步用DDD設計一個電商網站(五)—— 停下腳步,重新出發

如何一步一步用DDD設計一個電商網站(六)—— 給購物車加點料,集成售價上下文

如何一步一步用DDD設計一個電商網站(七)—— 實現售價上下文

如何一步一步用DDD設計一個電商網站(八)—— 會員價的集成

如何一步一步用DDD設計一個電商網站(九)—— 小心陷入值對象持久化的坑

如何一步一步用DDD設計一個電商網站(十)—— 一個完整的購物車

如何一步一步用DDD設計一個電商網站(十一)—— 最后的准備

如何一步一步用DDD設計一個電商網站(十二)—— 提交並生成訂單

如何一步一步用DDD設計一個電商網站(十三)—— 領域事件擴展

 

 

 

閱讀目錄

 

一、前言

  之前的文章中已經涉及到了購買商品加入購物車,購物車內購物項的金額計算等功能。本篇准備把剩下的購物車的基本概念一次處理完。

 

二、回顧

  在動手之前我對之前的購買上下文內對象做了一次回顧。先梳理一下已經在上下文內出現的領域對象,如圖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
出處:https://zacharyfan.com/archives/180.html

 

 

▶關於作者:張帆(Zachary,個人微信號:Zachary-ZF)。堅持用心打磨每一篇高質量原創。歡迎掃描右側的二維碼~。

定期發表原創內容:架構設計丨分布式系統丨產品丨運營丨一些思考。

 

如果你是初級程序員,想提升但不知道如何下手。又或者做程序員多年,陷入了一些瓶頸想拓寬一下視野。歡迎關注我的公眾號「跨界架構師」,回復「技術」,送你一份我長期收集和整理的思維導圖。

如果你是運營,面對不斷變化的市場束手無策。又或者想了解主流的運營策略,以豐富自己的“倉庫”。歡迎關注我的公眾號「跨界架構師」,回復「運營」,送你一份我長期收集和整理的思維導圖。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM