本系列所有文章
如何一步一步用DDD設計一個電商網站(一)—— 先理解核心概念
如何一步一步用DDD設計一個電商網站(四)—— 把商品賣給用戶
如何一步一步用DDD設計一個電商網站(五)—— 停下腳步,重新出發
如何一步一步用DDD設計一個電商網站(六)—— 給購物車加點料,集成售價上下文
如何一步一步用DDD設計一個電商網站(七)—— 實現售價上下文
如何一步一步用DDD設計一個電商網站(八)—— 會員價的集成
如何一步一步用DDD設計一個電商網站(九)—— 小心陷入值對象持久化的坑
如何一步一步用DDD設計一個電商網站(十)—— 一個完整的購物車
如何一步一步用DDD設計一個電商網站(十一)—— 最后的准備
如何一步一步用DDD設計一個電商網站(十二)—— 提交並生成訂單
如何一步一步用DDD設計一個電商網站(十三)—— 領域事件擴展
閱讀目錄
一、前言
結合我們本次系列的第一篇博文中提到的上下文映射圖(傳送門:如何一步一步用DDD設計一個電商網站(一)—— 先理解核心概念),得知我們這個電商網站的核心域就是銷售子域。因為電子商務是以信息網絡技術為手段,以商品交換為中心的商務活動,一個好的核心域設計可以大大提升企業的競爭力和對市場變化的相應速度。
那么我們開始設計領域對象。對於設計領域對象的基本概念不了解的可以先閱讀我的該系列第二篇文章(傳送門:如何一步一步用DDD設計一個電商網站(二)—— 項目架構)。
二、定義幾個基類
我相信我們大部分人會以如下的方式去存放我們定義的基類,見圖1。
【圖1】
這是一種比較常規的技術分層思維方式產生的結果,在某些項目文件中或多或少有那么幾個"Base"、"Core"、"Common"等的文件夾存放着一些通用的類,它們起着對當前項目中類的抽象、實現通用性支撐性功能的作用。然而在DDD中這些都應屬於基礎設施層的事情,這樣能夠保證其他層專注於自身的職責,不會把本應內聚的東西泄露到這些類中。如我們當前的領域層就專注於領域建模,里面的概念全部與通用語言相關。說干就干,搬到基礎設施層去,再取個能表達出一致概念的名字的模塊存放,如圖2。
【圖2】
三、核心域(銷售子域)中有什么
“銷售”用通俗的話講就是“把商品賣給用戶”,這幾個字中就已經凸顯出幾個概念:“商品”,“用戶”,“賣”。以下就是”商品“和”用戶“的代碼實現:
【圖3】
【圖4】
我相信有許多人會以圖3和圖4的方式定義我們的商品和用戶,乍一看的確符合對商品、用戶概念的獨立的理解。但在DDD中有不同的限界上下文,每個限界上下文專注處理自身的業務,多個限界上下文之間是以協作的方式工作。保證多個限界上下文之間的良好協作關系的方式是提高自治性。提高自治性的方式又有很多,技術方面如領域事件、消息隊列、事件源等,這里暫時不展開描述。從代碼層面來看,建模的時候只獲取對當前上下文業務處理剛剛好大小的數據,也可以提高當前項目的自治性。
根據我們划分的上下文映射圖,用戶和商品是屬於另外的上下文的,那么在這里我們都應該建模為值對象,因為我們是無法直接修改這些對象的內部屬性的。另外,在上面2個圖中,獲取其他上下文中的資源時,我們作為客戶方基本上是不會原封不動的消費服務方提供的數據的。比如這里面的Product.PermitNo(批文號),在大部分行業里,它與銷售商品沒什么關系。再如User.BlockedBalance(凍結余額),在銷售的過程中,只需要知道用戶有多少可用余額就好了。這個思路總結一下就是,從業務角度我們不求大而全,只求剛好滿足當前業務即可,但是當業務發生變化的時候我們的領域模型也要及時反映出調整后的通用語言概念。得到修改后的模型:
【圖5】
【圖6】
對了,不管是值對象還是實體和聚合,習慣在構造函數中的做好守衛驗證,有利於表達出什么樣的領域對象是合法的。
四、更好的存活於分布式背景下
在某些背景下一個限界上下文是作為獨立的服務對外提供API進行訪問的,特別在電商行業,分布式系統的構建是個普遍情況,方式也多元化,各種RPC框架、Restful等技術選型,SOA、微服務等實現理念層出不窮。如何最大化的降低技術變更和業務變化導致的上下文划分調整的影響,也是我們要考慮的重要問題。
對於我們.Net開發人員來說,在分布式場景下用的最多的方式無非是WebAPI和WCF了。這種方式也就是在第一篇文章中所提到的發布語言和開放主機服務,那么對於客戶端來說需要做好防腐層(第一篇文章中有提到)的工作,好避免外部上下文的概念侵入到自身的領域概念中。一個普遍的防腐層實現時序圖,其中真正負責防腐層工作的是XXXAdapter和XXXTranslator,如下(摘自[Vaughn Vernon]《實現領域驅動設計》):
【圖7】
我們這里實現的相關類如下圖所示定義:
【圖8】
其中1存放着訪問遠程資源的接口定義,2是其實現方式。這樣設計的好處是,對於領域層的建模隱藏了數據獲取的實現細節。並且當我們實際開發的時候可能由於需要配合服務方還未准備好,但是這絲毫不影響我們的開發工作,我們可以定義個Mock類來實現這里的IRemoteServices中的接口,就可以順利地進行開發工作。
其中ProductAdapter、UserAdapter分別負責請求商品上下文和用戶上下文並取得原始結果,ProductTranslator、UserTranslator則是通過解析原始結果,轉換為我方上下文中需要的領域模型概念。以下則是核心部分的實現:
【圖9】
【圖10】
五、結語
作者:Zachary
出處:https://zacharyfan.com/archives/121.html
▶關於作者:張帆(Zachary,個人微信號:Zachary-ZF)。堅持用心打磨每一篇高質量原創。歡迎掃描右側的二維碼~。
定期發表原創內容:架構設計丨分布式系統丨產品丨運營丨一些思考。
如果你是初級程序員,想提升但不知道如何下手。又或者做程序員多年,陷入了一些瓶頸想拓寬一下視野。歡迎關注我的公眾號「跨界架構師」,回復「技術」,送你一份我長期收集和整理的思維導圖。
如果你是運營,面對不斷變化的市場束手無策。又或者想了解主流的運營策略,以豐富自己的“倉庫”。歡迎關注我的公眾號「跨界架構師」,回復「運營」,送你一份我長期收集和整理的思維導圖。