打開 DDD 相關的書籍,你會被一系列生硬、高深的概念充斥,拜讀完畢,滿頭霧水。這不是你的問題,而是 DDD 自己的問題,表現形式太概念化。學習它的內核,就不要被它給出的概念所迷惑,而要去思索這些概念背後所蘊含的設計原則,多問一些爲何,本質無外乎是 SOLID。最重要的,要學會運用設計原則去解決問題,而非所謂的 「設計規範」。web
本文將會以系列解答的方式展開,由淺入深,篇幅不長,無妨一看。緩存
本質上是一種方法論,提供了一套系統開發的設計方法。面對須要解決的問題,從複雜的現實中抽象出業務模型的思惟方式與實踐技巧。初衷是清晰設計思路,規範設計過程。架構
DDD 強調是說得先把 「領域」 中涉及到的數據、流程、規則等都弄明白了,而後以面向對象的觀點爲其創建一個模型(即領域模型),而這個模型,決定了你將用什麼技術、什麼架構、什麼平臺來實現這個系統。因此技術在這個過程當中是 「被動的」,是被 「選來」 實現 「領域模型」 的。對於項目的成敗,技術不是決定性因素,領域模型是否符合事物的本質纔是關鍵。app
能夠看出,領域驅動設計的出發點是業務導向,技術服務於業務。運維
學習 DDD 有一些常見的誤區。第一個要避免的就是,你必需要清楚,DDD 對於工程領域沒有提出多麼創新的技法,它更可能是把前人在生產系統中用慣的技法概括,總結,包裝了一下 ——dom
你可能認爲 DDD 是一把 「瑞士軍刀」,可以解決全部的設計問題,而實際上 「DDD 只是一把錘子」,有個諺語叫作 「若是你手裏有一把錘子,那麼全部的問題都變成了釘子」,若是你拿着 DDD 這把錘子處處去敲,要麼東西被敲壞,要麼就不起做用。curl
爲何說 DDD 只是一把錘子呢?做者明確指出,DDD 只適合業務複雜度很大的場景,不適用於技術複雜性很大但業務領域複雜性很低的場景。能夠看出,DDD 只專一一個領域,高複雜業務 —— 經過它能夠爲你的系統創建一個核心、穩定的領域模型,靈活、可擴展的架構。分佈式
DDD 是擁抱複雜的,擁抱變化的,但自己也是有成本有前提的;簡單系統,不必搞這麼複雜,強上 DDD 就是一種反模式了。因此,當你遇到一個問題就想到 DDD 的時候,必定要注意 「DDD 只是一把錘子」,不要拿着這把錘子處處去敲!工具
如何判斷業務是否複雜,判斷依據不勝繁數。在我看來,沒那麼複雜,就兩個:單元測試
只要知足其中一個,我認爲就是複雜的。
DDD 並不是 「銀彈」,天然也不是解決全部疑難雜症的 「靈丹妙藥」。在我看來,它只解決一個問題:過分耦合。
業務初期,系統功能大都很是簡單,CRUD 就能知足,此時的系統是清晰的。隨着業務的不斷演化,系統的頻繁迭代,代碼邏輯變得愈來愈複雜,系統也愈來愈冗餘。模塊彼此關聯,誰都很難說清模塊的具體功能意圖是啥;修改一個功能,每每光回溯該功能須要的修改點就須要很長時間,更別提修改帶來的不可預知的影響面。
歸根到底在於系統架構不清晰,劃分出來的模塊內聚度低、高耦合,致使代碼很差複用、很差擴展、很差運維。
第一種解決方案:按照演進式設計的理論,讓系統的設計隨着業務的演進而增加。這固然是可行的,工程實踐中的重構、持續集成就能夠對付各類混亂問題。問題在於,只是沒有章法的、小範圍的代碼重構,很難具有通用型,容易變成了重構者的自娛自樂,代碼繼續腐敗,從新重構…… 無休止的循環
第二種解決方案:從新抽象,如何抽象,DDD 建議咱們左右開弓:一、分治(實體對象、值對象、聚合根);二、分層(展現、應用、領域、通用)
核心概念三句話:
核心關係一句話:
talk is cheap,show me the code
//聚合根 class Order { public String id;//訂單ID,全局惟一 public Address customerAddress;//配送地址 public List<Item> items;//商品信息 public Pay pay;//支付信息 public LogisticsDetail logisticsDetail;//物流信息 public Pingjia pingjia;//評價信息 } //實體 class Item { public Long id; //商品ID,實體主鍵,Order內惟一 public String name;//商品名 public float price;//價格 public int count;//數量 } //實體 class Pay { public Long id; //支付ID,實體主鍵,Pay內惟一 public String source;//支付方式 public int currency;//幣種 public float total;//價格 } //實體 class LogisticsDetail { public Long id; //物流ID,實體主鍵,LogisticsDetail內惟一 public int cpCode;//物流公司 public String mailNo;//物流單 public float status;//當前狀態 } //實體 class Pingjia { public Long id; //評價ID,實體主鍵,Pingjia內惟一 public String desc;//描述 public byte[] image;//圖片 } //值對象 class Address{ public String province;//省 public String city;//市 public String county;//區 }
能夠看到,經過業務限界,DDD 將大型複雜的 DO 分解爲若干簡單的 DO,從而保證 DO 的擴展性、靈活性。具體到 DB 層面,須要五張表:
但若是業務沒有這麼複雜,我依然推薦 DO 的 4 要素設計:基礎要素、核心要素、擴展要素以及冗餘要素。就拿 open 店面綁定的 *App 設計舉例:
於此,咱們能夠概括出領域模型設計的通常步驟:
這裏多說一句,若是你讀過金字塔原理的話,會發現思惟方式分爲兩種:概括性思惟和演繹性思惟。人的原始思惟方式是演繹性的,這就決定了咱們常常基於流程去思考問題,而面向對象建模偏偏須要的是概括性思惟。這也是着重須要自我訓練的地方。
首先須要劃分模塊。模塊(Module)是 DDD 中明確提到的分層前置手段,在工程實踐中,較爲常見的模塊策略有兩種:
技術職責分包:
業務職責分包:(就拿開放平臺 open 舉例)
複雜系統建議採用技術分包策略,提升模塊代碼的複用性。
而後咱們再來說模塊內的分層,這裏特指 core 模塊的內部分層。DDD 描述了幾個分層概念,分別是:防腐層、服務層、資源庫、領域對象、基礎層。
如代碼中所示,通常的工程中包的組織方式爲 {com. 公司名。組織架構。業務。上下文.*},這樣的組織結構可以明確的將一個上下文限定在包的內部。
這裏着重釋義一下防腐層,該層主要是將外部系統 DO 轉義成本系統 DO,避免外部 DO 一旦發生變化,本系統改動範圍過大的狀況,收斂影響面。
//接口層 public class Consume { public BaseResultDTO consumeMessage(OrderDetailMessage orderDetailMessage ) throws Exception, Throwable{ OpenOrderDO openOrderDO = transferAdapter.orderDetailMessage2openOrderDO(orderDetailMessage);//調用防腐層 openOrderService.handleOrderMeasage(openOrderDO);//調用服務層 } } //防腐層 public class TransferAdapter { public OpenOrderDO orderDetailMessage2openOrderDO(OrderDetailMessage orderDetailMessage){//轉換DO OpenOrderDO openOrderDO = new OpenOrderDO(); openOrderDO.setTradeId(orderDetailMessage.getTradeId()); openOrderDO.setOrderSource(orderDetailMessage.getOrderSource()); openOrderDO.setBuyerUid(orderDetailMessage.getBuyerId()); openOrderDO.setSellerUid(orderDetailMessage.getSellerId()); return openOrderDO; } }
落地就是模型到代碼的轉換,核心是保證模型和代碼的一致性。實際狀況下,因爲沒有好的保持模型和代碼一致的辦法,不少系統每每開始搞的不錯,慢慢就不一致了,也就逐漸爛掉了。
落地的方式,理論上有三種:
1 不可控,2 不現實。推薦 3 的作法:架構師給出設計方案,並給出骨幹實現,開發人員有了可類比的代碼,就可以比較準確的去作功能開發。這比空講要有效的多。
此外,落地中還要注意三點:
DDD 實際上是面向對象方法論的一個昇華。咱們回頭來看它,無外乎是經過劃分領域(聚合根、實體、值對象)、領域行爲封裝到領域對象(充血模式)、內外交互封裝到防腐層、職責封裝到對應的模塊和分層,從而實現了高內聚低耦合 —— 這也是它最精華的部分。
那麼 DDD 的各個概念重要麼?並不重要。概念掌握確實有助於提升咱們的業務思考能力,但 DDD 強調的是理論結合實踐,沒有通過實戰考驗,都是紙上談兵。經驗豐富的開發人員,即使沒有據說過 DDD,其思想也每每與 DDD 相符。我更建議的是:不要受限於對概念的掌握,而要透過層層表象思考它的本質,追求設計原則與實踐方法的融匯貫通。只有如此,才能針對不一樣的場景靈活地運用 DDD,而非生搬硬套。畢竟,設計老是如此,即便前人已經總結了許多的方法,也不能像數學公式那樣套用獲得準確無誤的結果。它不存在惟一的答案。
經驗有限,我對 DDD 的理解不免會有不足之處,歡迎你們共同探討,共同提升。
一、領域驅動設計精簡版二、實現領域驅動設計