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

1、前言

    上一篇咱們講了DDD的核心概念(附上連接),而且設計了咱們的上下文映射圖,那麼接下來就準備開始立項了,本篇文章的部分知識點可能對一部分人來講比較基礎,能夠選擇性的閱讀。

    在這以前咱們日常用的最多的應該就是3層架構了,這裏也不展開描述了,你們都是在3層的陪伴下一路走來的~

    DDD所使用的傳統分層架構是鬆散分層,也就是上層能夠訪問任意層級的下層,而不是僅限於當前層的下一層,這是有別於3層架構的。以下面2張圖的區別圖:

      

                        【圖1】

  

                        【圖2】

Application:這層的職責是對接收到的數據作一些非業務性驗證,事務的控制,最重要的是協調多個聚合之間的操做。這裏應該能夠清晰的表達出整個操做所作的事情,而且與通用語言是一致的。

Domain:這一層是DDD設計的核心,這裏不但須要精確合理的表達出通用語言的每個細節,另外如何把對象合理的定義爲聚合、實體、值對象也是重中之重。這裏不但關係着整個項目的複雜度,也是戰術建模的體現,任何的一行代碼都是對業務的準肯定義,應該是恰到好處。一個清晰簡潔的戰術建模才能夠應對後續的快速變化。

Infrastructure:這裏是輔助性的一層,也是整個項目的基礎。比如這裏存放着一磚一瓦,最終建造什麼模樣的高樓在於用它的地方。主要包括,倉儲的實現(咱們存放數據的地方)、一些通用的支撐性類庫。

 

2、六邊形架構

    在[Vaughn Vernon]《實現領域驅動設計》一書中屢次提到對DDD主張六邊形架構的概念,六邊形架構對於保證限界上下文內的領域概念的清晰性有着重要的做用,那麼什麼是六邊形架構,以下圖3(摘自[Vaughn Vernon]《實現領域驅動設計》一書)。

  

                      【圖3】

    在當今愈來愈提倡開放合做的大環境下,引用的多樣化的Service,和在自身系統達到必定規模以後的分佈式治理,愈來愈須要經過協做進行工做,那麼如何提高協做的效率變得愈來愈重要。提升各個應用程序的自治性,是一種有效提高協做能力的手段。從上圖中看出爲了保證領域模型所在的應用程序的乾淨簡潔和自治性,各類適配器做爲"防腐層(在上篇中有提到)"在整個程序的最外層保護着當前的「界限上下文(在上篇中有提到)」不受外部入侵。

    因此在咱們的整個設計中須要注意對涉及到外部系統交互的地方的抽象,經過面向接口、依賴注入等方式作到外部的變化對自身系統的影響最小化。

 

3、終於開始建項目了

    按照以前的這些描述,咱們終於初步創建了咱們的解決方案。以下圖4:

  

                【圖4】

這裏把每一個項目的職責大體說一下。

Mall:負責咱們的電商網站的界面處理和用戶的數據錄入

Mall.Application:按模塊分別定義不一樣的ApplicationService來說述每個操做下的「故事」。

Mall.Application.DomainEventSubscribers:全部的領域事件訂閱者。

Mall.Domain:這裏存放着戰術建模的結晶,Entity、Aggregate、ValueObject。(下面會具體講述下)

Mall.Domain.Events:全部的領域事件,這層也能夠合併到Domain中,給它新建一個文件夾。

Mall.Domain.IRepositories:全部的倉儲(資源庫)接口。相似於三層中的IDAL。

Mall.DomainService:領域服務,存放着那些不適合放在聚合/值對象上的無狀態的操做方法,用於實現特定某個領域的任務。

Mall.Infrastructure:存放着一些通用類庫

Mall.Infrastructure.Repositories:全部倉儲(資源庫)的實現。

Mall.Infrastructure.Translators:翻譯層,也就是與外部系統溝通的橋樑,主要的職責就是作好「反腐層」的重任。

 

4、DDD中的3個臭皮匠

    這裏的3個臭皮匠其實就是:Entity、ValueObject、Aggregate。咱們要提煉出業務中的精華,合理的抽象爲這3個概念,而且這種抽象是需隨着領域裏的概念變化而變化的。這3者的結合運用會讓咱們的項目活起來,這是DDD的核心。這裏再把這3個概念從新梳理一下。

    Entity(實體): 每一個實體是惟一的,而且能夠至關長的一段時間內持續地變化。咱們能夠對實體作屢次修改,故一個實體對象可能和它先前的狀態大不相同。可是,因爲它們擁有相同的身份標識,他們依然是同一個實體。

    ValueObject(值對象):值對象用於度量和描述事物,當你只關心某個對象的屬性時,該對象即可做爲一個值對象。實體與值對象的區別在於惟一的身份標識和可變性。

    Aggregate(聚合):聚合類是實體的升級,是由一組與生俱來就密切相關實體和值對象組合而成的,這整個組合的最上層實體就是聚合。

 

5、CQRS(Command Query Responsibility Segregation)

    說到DDD必然要提一下CQRS,我認爲CQRS和DDD的關係就像咖啡和牛奶,給大型系統的構建提供了一劑良藥,它生於讀寫分離,具備高吞吐量、高伸縮性等特色,值得咱們爲之付出一些代價。可是CQRS的使用會使整個數據持久化和查詢的鏈路拉長,而且工做量也會比簡單的讀寫一體化大的多,因此須要對項目作出合理的考量來決定是否使用。

    當咱們須要把某個複雜的聚合修改以後寫入到數據庫的時候,要保證N張表的數據被同時修改爲功,整個事務的週期必然會加長。並且當咱們須要顯示來自不一樣聚合類型與實例的數據時,咱們的SQL必然包含N多的join。領域越複雜這種狀況愈加常見。

    CQRS須要和事件源結合使用,對數據的修改操做只是往事件源裏增長一條修改後的結果記錄(相似於咱們的源碼控制軟件的log),並不會直接把修改後的對象持久化到數據庫。這樣可以大大提升數據修改的速度,而且對於查詢操做的實現方式就比較多樣化了。大體列舉了如下4種方式:

    1.仍是使用單個數據庫,每次領域對象的獲取都須要根據事件源中的事件集合作重建,獲得當前的最新的數據返回。這只是編碼設計上的讀寫分離

    2.拆分爲讀庫和寫庫,實現方式同1

    3.拆分爲讀庫和寫庫,而且針對讀庫作專門的查詢數據冗餘,異步的經過事件源來修改查詢數據,能夠結合merge commit。

    4.在3的基礎上作讀庫的負載均衡

    這4種方式複雜度各不相同,能夠結合實際項目的複雜度擇優選擇,其中最關鍵的一條即是是否存在大量的列表類數據展現,若是是那麼1和2便就不適合了。在以上的方式以外能夠結合其餘的數據存儲一塊兒使用,如緩存,NoSql,然而這隻須要訂閱全部的命令事件便可實現。

 

6、結語

    本篇主要介紹了項目的分層架構、每層的職責和裏面存放什麼樣子的類。限於非咱們這個系列的核心主題,因此都沒有發散出去作更加具體形象的描述,但願你們能夠邊結合[Vaughn Vernon]《實現領域驅動設計》一書的閱讀跟着我作實際的編碼來加深對DDD的理解。跳出根深蒂固的三層思想是痛苦的,可是我認爲只要堅持下去,DDD會讓你看見一片世外桃源,到那時會以爲咱們的付出都是值得的。而且DDD思想的運用可大可小,小到類的設計,大到複雜項目之間的構架,都會給你提供幫助。

 

做者:Zachary
出處:https://zacharyfan.com/archives/114.html

 

 

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

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

 

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

若是你是運營,面對不斷變化的市場一籌莫展。又或者想了解主流的運營策略,以豐富本身的「倉庫」。歡迎關注個人公衆號「跨界架構師」,回覆「運營」,送你一份我長期收集和整理的思惟導圖。

相關文章
相關標籤/搜索