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

来源:http://www.cnblogs.com/Zachary-Fan/archive/2017/01/16/DDD_11.html
-Advertisement-
Play Games

閱讀目錄 前言 準備 實現 結語 一、前言 最近實在太忙,上周停更了一周。按流程一步一步走到現在,到達了整個下單流程的最後一公裡——結算頁的處理。從整個流程來看,這裡需要用戶填寫的信息是最多的,那麼在後端的設計中如何考慮到業務邊界的劃分,和相互之間的交互複雜度,又是我們需要考慮的地方。總體來說本篇講 ...


 閱讀目錄

 

一、前言

  最近實在太忙,上周停更了一周。按流程一步一步走到現在,到達了整個下單流程的最後一公裡——結算頁的處理。從整個流程來看,這裡需要用戶填寫的信息是最多的,那麼在後端的設計中如何考慮到業務邊界的劃分,和相互之間的交互複雜度,又是我們需要考慮的地方。總體來說本篇講述的內容在前幾篇都有涉及,所以這次一次性處理的業務比較多,已經比較熟練的看官可以跳過本篇。

 

二、準備

  主流的電商設計中結算頁包含以下5個概念:選擇收貨地址、選擇支付方式、選擇快遞、使用優惠券、使用餘額和積分。筆者認為,根據我們在本系列的第一篇博文中的上下文映射圖,這背後涉及到了多個上下文的協作:

  1.用戶上下文:包含選擇收貨地址

  2.支付上下文:包含選擇支付方式、使用餘額和積分

  3.售價上下文:使用優惠券。

  其中第“1”點我的理解是在整個大系統中,收貨地址並不是僅在購買的時候會用到,而是用戶可以直接管理的(一般主流電商都可以在《用戶中心》菜單內操作個人的收貨地址信息),在購物車中進行管理其實並不是一個必須經過的流程,大部分場景下只是在現有地址中做一個選擇,所以收貨地址更接近於用戶域而不是購買域,在購物車的管理可以理解為一個快捷方式而已。

  第“2”點,我的理解是,把支付操作相關的概念放到一起,可以做的很靈活,可以和運營打法搭配起來。如:支付方式和使用積分的聯動、像天貓那樣的紅包等促進用戶購買欲望的招式。

  第“3”點,我的理解是,優惠券也是會影響到整個商品的售價的,所以它應該屬於售價上下文,配合其它的促銷方式做出更多的打法。

  剩下的快遞我認為是本地購買上下文內的概念,因為它只服務於購買的流程之中。

 

三、實現

  根據服務能力來編寫ApplicationService,那麼這裡總共是提供了3種服務能力,所以定義了3個ApplicationService來提供這些功能:

  1.IDeliveryService:其中包含選擇收貨地址和選擇快遞

  2.IPaymentService:其中包含選擇支付方式、使用餘額和積分

  3.ICouponService:包含選擇禮券。

  好了接下來就是其中涉及到的領域模型的設計,這裡需要糾正一個之前的錯誤,在之前的設計中把餘額直接放到了User這個值對象中,並且是從用戶上下文獲取的,現在看看當初的設計不是很妥當。因為餘額並不是用戶與生俱來的東西,就好比我要認識一個人,並不一定要知道他有多少錢,但是必然需要知道姓名、年齡等。所以餘額與用戶之間並不是一個強依賴關係。而且分屬於2個不同的領域聚合、甚至是上下文。這裡涉及的所有領域模型的UML圖如下圖1所示:

                          【圖1】

  其中的值對象都是從遠程上下文獲取的,所以這裡在購買上下文里只是使用了其的一個副本。在購買上下文的3個ApplicationService如下:

    public interface IDeliveryService
    {
        List<ShippingAddressDTO> GetAllShippingAddresses(string userId);

        Result AddNewShippingAddress(string userId, DeliveryAddNewShippingAddressRequest request);

        Result EditShippingAddress(string userId, DeliveryEditShippingAddressRequest request);

        Result DeleteShippingAddress(string id);

        List<ExpressDTO> GetAllCanUseExpresses();
    }
    public interface IPaymentService
    {
        List<PaymentMethodDTO> GetAllCanUsePaymentMethods();

        WalletDTO GetUserWallet(string userId);
    }
    public interface ICouponService
    {
        List<CouponDTO> GetAllCoupons(string userId);
    }

  這裡介面定義思路是把界面上的操作記錄全部由UI程式做本地緩存/Cookie等,減少服務端的處理壓力,所以介面看上去比較簡單,沒有那些使用禮券,修改使用的收貨地址這類的介面。

  另外提一下,在當前的解決方案中的售價上下文中的處理中,增加了2個聚合來處理優惠券相關的業務。

    public class Coupon : AggregateRoot
    {
        public string Name { get; private set; }

        public decimal Value { get; private set; }

        public DateTime ExpiryDate { get; private set; }

        public List<string> ContainsProductIds { get; private set; }

        public Coupon(string name, decimal value, DateTime expiryDate, IEnumerable<string> containsProductIds)
        {
            if (string.IsNullOrWhiteSpace(name))
                throw new ArgumentNullException("name");

            if (value <= 0)
                throw new ArgumentException("value不能小於等於0", "value");

            if (expiryDate == default(DateTime))
                throw new ArgumentException("請傳入正確的expiryDate", "expiryDate");

            if (containsProductIds == null)
                throw new ArgumentNullException("containsProductIds");

            this.Name = name;
            this.Value = value;
            this.ExpiryDate = expiryDate;
            this.ContainsProductIds = containsProductIds.ToList();
        }
    }
    public class CouponNo : AggregateRoot
    {
        public string CouponId { get; private set; }

        public DateTime UsedTime { get; private set; }

        public bool IsUsed
        {
            get { return UsedTime != default(DateTime) && UsedTime < DateTime.Now; }
        }

        public string UserId { get; private set; }

        public CouponNo(string couponId, DateTime usedTime, string userId)
        {
            if (string.IsNullOrWhiteSpace(couponId))
                throw new ArgumentNullException("couponId");

            if (string.IsNullOrWhiteSpace(userId))
                throw new ArgumentNullException("userId");

            this.CouponId = couponId;
            this.UsedTime = usedTime;
            this.UserId = userId;
        }

        public void BeUsed()
        {
            this.UsedTime = DateTime.Now;
        }
    }

  其中CouponNo中的CouponId是保持了一個對Coupon聚合ID的引用,在需要的時候從Repository中取出Coupon的信息。部分代碼如下:

            var couponNos = DomainRegistry.CouponNoRepository().GetNotUsedByUserId(cart.UserId);

            var buyProductIds = cart.CartItems.Select(ent => ent.ProductId);
            List<CouponDTO> couponDtos = new List<CouponDTO>();
            foreach (var couponNo in couponNos)
            {
                if (couponNo.IsUsed)
                    continue;

                var coupon = DomainRegistry.CouponRepository().GetByIdentity(couponNo.CouponId);

                if (coupon.ContainsProductIds.Count == 0 || coupon.ContainsProductIds.Any(ent => buyProductIds.Any(e => e == ent)))
                {
                    couponDtos.Add(new CouponDTO
                    {
                        CanUse = couponNo.IsUsed,
                        ExpiryDate = coupon.ExpiryDate,
                        ID = couponNo.ID,
                        Name = coupon.Name,
                        Value = coupon.Value
                    });
                }
            }

 

 

四、結語

  本篇比較簡單不多述了,下麵源碼奉上,有興趣的同學自行下載查看全部源碼。

 

 

 

本文的源碼地址:https://github.com/ZacharyFan/DDDDemo/tree/Demo11

 

作者:Zachary_Fan
出處:http://www.cnblogs.com/Zachary-Fan/p/DDD_11.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 預設MVC的 View頁面 不參與編譯,當更改view對應model後,view編譯也能通過,或者頁面有錯誤的服務端代碼時也不會報錯。 那麼如何在編譯的時候能讓View中的錯誤也不能通過呢。經過查找找到了方法,本機MVC5.0 適用,其他版本未試。 方法: 一、修改 .csproj 工程文件,用tx ...
  • 1.添加編輯按鈕 打開文件Index.js 【..\MyCompanyName.AbpZeroTemplate.Web\Areas\Mpa\Views\Category\Index.js】 在actions中添加如下代碼: actions: { title: app.localize('Action ...
  • 今天我們來講一下原型模式。老樣子,我們先舉一個案例: 一、案例 我們找工作,需要投簡歷,一份簡歷是不夠的,我們需要多複製幾分簡歷。 好,我們用簡單的控制台程式來完成上述的需求。 二、演繹 1、第一步演繹 客戶端調用: OK,我們實現了上述的功能,搞了三份簡歷出來。我們可以通過複製粘貼,複製更多分簡歷 ...
  • 雖然已經可以添加商品分類,但還需進行優化,比如:用戶是否輸入、輸入字元串是否有格式限制等等。 打開添加分類按鈕,名稱不輸入任何字元,直接保存,會發現列表添加一條空記錄。在實際項目中,這是不允許出現的事情,我必須對分類名稱進行必填限制,但用戶沒填寫時,給予提示信息。 數據驗證就涉及客戶端和服務端,建議 ...
  • 1.打開Index視圖 頁面中添加一個按鈕,代碼如下: <div class="row margin-bottom-5"> <div class="col-xs-6"> <div class="page-head"> <div class="page-title"> <h1> <span>分類</s ...
  • 今天來講一下工廠方法模式。 大家可能聽著這個模式有點耳熟,是的,前面第一篇博文,我們講到了簡單工廠模式。嗯,他們有的確非常相似,今天我們就拿簡單工廠模式中的案例舉例子即可。 學會了簡單工廠模式,對於工廠方法模式也就自然而然的會了。 大家知道,簡單工廠有個很明顯的缺點,就案例來說,我增加一種演算法,則需 ...
  • IT生涯將近10年,一直對於軟體架構還是將懂非懂,因為每一個團隊的經歷不一樣,所以達到的高度也不一樣,所以經歷決定一個架構師的水平能力(騰訊的架構也不一定適用中小型,做ERP的不一定適合互聯網),也由於行業的特性,所以架構也隨環境,行業特性,團隊水平性而產生裂變,所以說團隊的技術水平決定架構的層次, ...
  • 前面談到了系統的文件,從文件結構中我們也可以看出Nop的層次劃分還是非常清晰,下麵我將介紹下Nop的架構層次,並對每個層做簡要的介紹,先看我畫的層次圖。 這個系統基本上按照了ddd的形式做了劃分,我本身對ddd也是新手,可能對更深的研究就比較困難,但是大家可以看到,數據層被做了很嚴格的隔離,充分的從 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...