干货 | DDD实战:基于洋葱模型的分层代码架构设计
點擊上方“中興開發者社區”,關注我們
每天讀一篇一線開發者原創好文
▎作者簡介
作者馮丹是一名非常有激情的一線程序員,喜歡java強大的面向對象能力,scala簡潔的函數式編程范式以及Akka這種優秀的響應式編程框架。今天的文章可以讓讀者了解DDD落地的一種具體的措施。
領域驅動設計DDD(Domain Driven Design)的主旨思想就是不再把需求分析和代碼實現分解為兩個獨立的過程,代碼即方案,這對于代碼的設計提出了更高的要求。要求即使是非開發人員也能非常容易的了解到他想要了解到的東西。
這就要求我們的代碼必須是分層設計的,層次逐層遞進,若要了解大概流程,則通過閱讀userInterface層和Application層的代碼就能夠知道整個業務的流程是怎么樣的,并不需要知道業務邏輯具體是怎么實現的,并不需要知道數據庫到底使用的是mysql還是cassandra。
另一方面我們想要管理不確定性,擁抱變化,同時不會因為外部頻繁變化而導致我們的核心業務也跟著頻繁的改動,這對于我們的代碼有提出了另一個要求:必須要有一個穩定的核心,它不依賴任務外部的東西。——洋蔥模型應運而生。
本次實踐緊貼業務(輸出即為最近迭代開發的系統監控微服務),通過基于洋蔥模型的代碼分層設計,讓代碼清晰易懂,而且不會再出現這樣的對話了:
前端開發:“這個接口里面的字段我要改一下,你也跟著改一下吧”
后端開發:“這個字段不能改啊,你改了,我要改好大一串代碼”
代碼分層理念:
基于上圖的原理再結合我們具體業務,我們把代碼按照如下目錄進行拆分:
為了和外部交互我們需要一個存放和外部交互的接口的包,我們把它叫做“api”或者“apiserver”
為了使我們的核心模型不隨著外部接口的變化而變化,我們需要一個存放外部接口相關的數據模型的包,我們把它叫做“dto”(data transaction object 數據傳輸對象),這個包中可以包含把dto對象轉為model對象的方法。這樣做的好處是dto向model依賴,而不是model依賴dto, 這樣的話如果外部模型發生了變化,我們修改dto包中的代碼就已經足夠了,model并不感知這個變化。
為了更加靈活的應對外部的變化,我們需要區分核心業務和非核心業務,把變化頻繁的業務歸到非核心業務層中,放非核心業務的包把它叫做“app”,通常app層就是通過調用不同的核心業務層開出來的各種方法來實現業務,同時把核心業務層的模型轉換為外部接口需要的模型。
為了使我們的業務邏輯盡量穩定,我們需要一個不依賴任務外部包(或者說外部實現)的核心業務包,我們把它叫做“model”,我們把本屬于domain層的東西model、repository、領域服務、領域事件等都放在這個包中,這么做的好處是和其他的包是同一抽象層次,想看核心業務打開model包就足夠了。本身model中的業務需要的持久化等功能是基礎設施層提供的,也就是說model需要依賴基礎設施層,但是我們為了讓model層足夠的穩定,我們需要用依賴注入的方式讓依賴倒置,讓provider依賴model層。model提供了可供編排的和領域核心模型強相關的各種服務,model層中都是核心業務的直接表達,如果需要用到基礎設施則全部使用抽象代替,具體的基礎設施在外部(通常是main函數)注入。
為了使我們的業務系統有操作數據庫、文件系統或者其他第三方軟件的能力,我們還需要一個存放和這些基礎設施強相關的包,我們把它叫做“provider”,provider包中理論上就是一些獨立的基礎設施操作類,他們依賴于model,把model層的數據持久化,或者是發送給kafka等。通常各個單獨的provider實例在系統上電的時候注入到model層中,model層用一個公共的父類類型變量來接收這個provider實例,這樣就做到了依賴倒置。如果業務發生變化,動態或者靜態地修改model層中這個公共父類類型的變量對于的值就行了,通常這個注入的動作也不會放在model層中,所以model是穩定的。
收益:
把代碼結構按照上述原則進行拆分之后邏輯變得更加清晰了,比如想要看一個rest接口是個什么流程,只需要打開api包查看url是什么,然后打開dto包查看接口中的數據模型是什么樣的,再打開app包查看具體的業務流程即可。 至于具體實現關心它們的人才需要進一步了解。也就不會出現這種情況了:想要了解某個業務流程,把整個工程代碼都翻遍了,都沒理清楚。
比如原來使用的數據庫是mysql,現在想要替換成cassandra,只需要實現一套cassandra操作的provider,然后在main函數把這個provider注入到model層即可。model層毫無感知。
比如前端要求改一個接口字段,只需要在dto中把修改相應字段,然后映射到相同的model模型字段中即可,model層也毫無感知。
比如前端一個查詢數據的接口,原本只返回了部分數據,現在想要讓這個接口返回全量數據,只需要在app包中重新編排這個接口的邏輯,先獲取partI再獲取partII即可(假定model中已經有了獲取partI和partII的服務)。model層也是毫無感知。
通常情況下model是穩定的,除非真的是核心業務發生變化才需要去動model的東西。
所以變化并不可怕,因為我們的代碼又靈活又穩定。
下圖是真實的基于這個理念開發的監控微服務的包和包依賴關系圖:
其中大致分了兩條線,左邊一條線:描述了從外部rest接口到核心領域模型model的依賴
右邊一條線描述了基礎設施對于model的依賴,基礎設施在main函數中注入到model中。
作者的其他文章
干貨|JVM內存模型和常規問題定位手段
總結
以上是生活随笔為你收集整理的干货 | DDD实战:基于洋葱模型的分层代码架构设计的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: torch.mul、matmul、mm、
- 下一篇: canvas下雪效果(原生js)