(6) 基於領域分析設計的架構規範 - 關於重構與落地

本系列目錄:架構

  1. 改變與優點
  2. 領域分析基礎
  3. 讀寫隔離
  4. 充血模型之實體
  5. 充血模型之Service
  6. 關於重構與落地

不論你們是否定可我在這裏提到的這一套小規範,既然你能看到這裏,仍是由衷表示感謝。核心內容已經介紹得差很少了,接下來仍是看兩個很實際得話題。框架

DDD落地之殤

DDD這類架構真正落地困難,我以爲,有幾個緣由:分佈式

  1. 規範多,尤爲是充血模型,須要對業務有更清晰的認識
  2. 代碼結構相比無狀態扁平化開發,層次深,模塊劃分更嚴格,因此,至少從文件數上來講,是更多的(代碼行數卻是不必定多)
  3. DDD並非一個「剛性」需求,若是不按這個規範來作,系統同樣能夠跑起來,因此,不少規範處在一個「模糊」的界定上,讓開發人員沒法肯定到底該如何規劃代碼。

正由於如此,即便團隊技術Leader瞭解DDD,卻在項目一開始的時候,因爲考慮到DDD的規範過多,不便於協做,爲了快速開發迭代,每每繼續沿用最通用的寫法。而一旦到了中期,業務複雜度上來了,當以爲領域分析有用武之地的時候,低頭一看,系統的複雜度已經到了難以輕鬆駕馭的程度,更別說來一個180度的大轉變了。測試

真的,這很現實,就是一個惡性循環。優化

因此,咱們須要找到一個破冰的辦法:編碼

  • Leader足夠強,團隊成員實力不錯,也給予Leader充分信任,那麼在一開始就採用DDD
  • 在一開始依舊採用非DDD架構模式,但利用迭代的方式,將代碼逐漸變成DDD

這二者沒有絕對好壞,雖然說第一種不常見,但依舊是我挺期待和欣賞的作法。.net

而可能更實用的是第二種,但新的問題又來了,如何逐步迭代改進成DDD,從哪裏入手?設計

從現有代碼迭代改進

咱們不強求一步到位,但求一步步作出一些恰當地整理code

如下給出一個按部就班的參考方案:對象

1. 讀寫隔離

這個過程幾乎不與業務掛鉤,咱們將OrderService,ProductService中的查詢方法剝離出來,放在諸如OrderFinder,ProductFinder之中,具體編碼細則規範參見以前的章節。 整個過程,通常來講,只要編譯能經過,代碼就不會有什麼問題。

2. 提取Factory

在查詢方法被剝離以後,就只剩下增/改/刪了,其中又是最特別的,前面在講工廠的時候提到過,因此將一個實體的建立行爲提取到一個XXXFactory中,也不困難,稍微留意一下,也不容易引起業務問題。特別是在一個已經相對複雜的系統中,若是無法一會兒分析透徹聚合根的歸屬問題,那麼能夠每個實體都加一個Factory,以後再逐步合併,雖然說工做量稍大一點,但對於已經有點棘手的項目來講,也算是一個快刀斬亂麻的辦法。

3. 提取{Action}Service

也就是提取咱們前文所提到的,跨多個聚合的一個事務操做 到一個獨立的Service中。 或者還能夠將要求下降一點,只要以爲代碼量特別大(我我的以爲150行以上就很大了,況且有些還帶了私有方法)的一個方法,都遷移到一個獨立的Service中,並採用前文提到的{具體操做}+Service的命名方式,一個Service只負責一個複雜的方法。雖然說不會很是精確,但重構後的效果仍是會很明顯的。

4. 創建領域聚合體系

經果上面幾步操做,我相信你的代碼裏,曾經那些OrderService,UserService,已經只剩下相對簡單的一些改/刪操做了,其實,某種程度上,他們已經逐步出現了領域聚合對象的樣子了。因此,若是你作到這一步,暫時中止了,也已經值得慶祝了!

那麼若是還要繼續前進一步呢?小項目還好,對於大項目,會有些難了。我相信這時候OrderUser頗有多是被POJO/DTO存在的,爲了保證平滑過分,咱們確定只能新建領域模型,那麼可能會要採用諸如OrderDomain來表示了。而後再將以前OrderService裏的方法遷移過來。

但若是你用的是Spring或者相似Spring的IOC框架,會有一個很現實的問題。因爲實體是被ORM框架new出來的對象,是脫離IOC容器管控的,因此若是你要在一個實體中使用一些IOC容器裏的組件,可能得要本身去經過IOC容器的上下文來手動獲取,好比經過ApplicationContext來獲取,而不能經過@Autowired來自動注入了。

目前我在工做中,借用版本迭代的機會,逐步執行第一步第二步,確實也是因爲項目太大太雜,即便這兩步走起來工做量也不小,還要和同事協調好這種新作法溝通協做問題,確實這也是挺現實的一個問題~

關於版本迭代

惟一不變的就是變化。

咱們常常得要「笑着」對着甲方或老闆說:「咱們會擁抱變換」,呵呵~ 很苦,但得要面對啊。

因此,假如咱們有一個領域實體爲訂單明細,你們能夠很容猜到,它也是屬於訂單聚合中的一部分,而非聚合根。訂單明細的變更,都來源於訂單的操做,好比訂單編輯,order.modify()

某一日,產品需求發生變化,說:

  • 咱們的訂單明細要單獨展現一個管理頁面,而不僅是像之前那樣,只能從某一個訂單點擊查看「詳情」才能看到
  • 同時,咱們要能對訂單明細進行修改,而且將這些明細的訂單綁定關係進行修改

這時,你會很明確的發現,曾經的訂單明細已經開始「昂首擡頭作主人」了,再也不「寄人籬下」了,由於它的查看入口再也不附屬再其餘實體上,並且咱們還得精確找到某一個明細進行修改。 獨立修改

產品既然這樣設計,意味着訂單明細在整個業務系統中的身份已經發生變化,它的獨立性也將愈來愈強,將來不少功能頗有可能將圍繞它而展開。因此,訂單明細將成爲一個獨立的聚合,進而咱們會作出一些變化:

  1. 訂單明細將有本身的 OrderDetailFactory
  2. 訂單明細將逐步創建本身的方法,出現如 orderDetail.modify()
  3. 曾經訂單聚合中的一些操做,要遷移到新的Service中,由於他們是不一樣的聚合了,有些操做已經屬於跨聚合操做

其中第(3)步,是難度最大的,由於要改動之前的代碼,甚至要動到業務,不只有開發難度,還有測試難度。

可是很遺憾的是,任何重構都是這樣的。不論用什麼框架,到必定階段,咱們都會面對這個問題。不在開發期間進行優化,那技術債務,只會變成成千上萬的BUG來給咱們上課。有的時候咱們總吐槽本身又拿到的一個前人的項目,說代碼多麼爛。可是反思下來,如今本身手頭的代碼,重構過嗎?是否是也會成爲將來人的笑柄呢?

其實這個將來人可能很近,可能就是一個月後的你本身。(神情複雜的笑容)

關於下一階段的內容

目前我更多的以單計應用的架構爲例,還未說起太多分佈式相關的內容。

分佈式是一個雙刃劍,與這個架構的協調,我還在梳理中,等好了,我會第一時間發出來。

其實我不在意閱讀量有多大~ 我把這個看成一個我本身的工做記錄,就行了~ 謝謝你們~

相關文章
相關標籤/搜索