淺談 DDD 及 CQRS 軟體開發思想 —— 優雅的後端工程師系列(一) === 最近專注於研究各種 clean code 概念,起因大概是看到公司老專案的程式碼寫的結構比較亂 🤣,整個專案上充滿非常亂的邏輯導致難以維護,學習了一段時間後小有心得,決定開一個 「**優雅的後端工程師**」 系列,分享自己在研究 clean code 中的所學。 What is DDD? --- DDD 為 **Domain-Driven Design** 的縮寫,以中文來說也可以翻譯成 「**領域驅動設計**」,聽起來很像是某個很酷炫的招式,領域啟動? 這樣的架構來源於工程師與業務夥伴溝通時,可以透過這個架構盡可能不涉及任何專業的技術語言,只需要專注於業務需求的實現。 DDD 只是一種方法論,也就是一種開發的想法,不是什麼實際的套件引用之後就可以實現 DDD。我想最簡單的解釋方法就是:「用最優雅的方法解決一個實際問題」。 在學習 DDD 之前我想應該多少了解過一點 SOLID 設計原則,他可以使軟體開發架構進行解耦,避免整體程式結構相互依賴。其中最後一項原則 Dependency inversion(依賴倒置)也被廣泛的應用在 DDD 的設計中,另外,在 DDD 開發中一定要時刻想到的概念可以被總結為以下幾點: * 如前所述的依賴倒置,在 DDD 架構中 Domain 通常只依賴於接口,而不依賴於具體實現,Domain 並不需要感知到資料庫的存在,也不需要知道具體資料庫是如何拿到資料的,只需要知道我用這個方法就可以拿到我想要的資料。 * 所有的接口設計以及 handler 都應該設計成做的是「什麼行為」而不是傳統的 CRUD,也就是應該設計成**行為導向**,而不是 GetList、UpdateList......等等方法 * 到了整個架構的最高層 Domain 層時,他不應該感受到任何的技術實現,或者數據轉換、數據獲取的邏輯 整體架構大概可以參考下面的流程,從最低層到最高層依序為: ``` mermaid graph LR; Adapter-->Ports-->Application-->Domain; ``` Adapter 為具體實現資料操作的層級,也就是實際跟資料庫溝通的一層 Ports 為處理外界資料傳入內部的層級,用比較好理解的說法就是以前的 controller Application 為整體應用層,不關注具體怎麼與外界溝通或者具體如何實現資料操作,只封裝 Domain 的業務需求方法 Domain 更像是整個軟體要實現的業務邏輯核心,他關注的點只在業務需求是什麼,而不關心需求內部的邏輯實現,例如調用資料庫找資料等。這裡的程式更像是用程式說人話。 為什麼我們需要使用 DDD? --- 實際在實現 DDD 架構開發會發現,前期需要做很多無用的架構編寫,各種依賴來依賴去,如果只是寫一個函式很快就可以解決現在這個需求了,對於前期來說好像很浪費時間,但隨著整個項目逐漸向前迭代,DDD 的優勢就逐漸顯現出來。 舉個例子,如果只是寫一個函式實現業務邏輯,那麼所有的邏輯判斷、各種 if 都會寫在同一個函式內,先不論這個函式接下來有沒有要接手給其他的工程師,哪怕是自己開發的,過了一段時間之後可能都會忘記當初這段邏輯在做什麼判斷,那麼當業務需求有變動的時候就會很想哭,邏輯一改結果到處壞掉,重寫的心都有了。 然而用上 DDD 就不一樣了,由於整體都沒有任何的耦合,當業務需求變動的時候,只需要再重新開發一個小單元,然後應用在 domain 層,修改的程式碼幾乎對原本的程式碼沒有任何侵入性,也就不會造成改這裡壞另一個奇怪的地方的問題。 DDD 的開發也許在前期是笨重且緩慢的,但長期的開發速度一定是比扁平化架構更快的。 What is CQRS? --- CQRS 為 Command Query Responsibility Segregation 的縮寫,也是個很簡單的概念,其實就只是在軟體設計層面實現讀寫分離(不是資料庫的讀寫分離嗷) CQRS 的中心思想很簡單,在後端開發的時候我們一般使用的是一個 model,裡面包含所有的讀寫操作,而 CQRS 的思想就是分開兩個獨立的 model,一個負責寫入,一個負責讀取。 要實現這樣的架構也就引入了兩個概念,Command 和 Query,對應的服務層也就被拆開成 CommandHandler 及 QueryHandler。 所以最簡單的來說,Command 執行所有資料的變更操作,並且不返回任何東西,Query 不對資料做任何修改,只返回查詢的資料。 可以來看一下傳統架構的資料流及經過 CQRS 修改過後的架構,首先未拆分的資料流如下:  傳統的軟體架構 application 層及 model 層是沒有分開的,現在再看一下經過 CQRS 設計的資料流:  可以看到原本的 Application Service 被拆成了 Command 及 Query 層,Model 也被拆分成 Write Model 及 Read Model。 我剛開始學習這個架構的時候有個疑問,為什麼需要這樣拆分,感覺很多此一舉?但實際上最直觀的感受就是,同一區塊的程式碼量因為這樣的架構拆分變少了,在閱讀程式碼時就可以理解的更快,至於其他的好處就留給程式碼演示的時候再來展示吧! >[!Note]小資訊 >下一篇文章將會根據 DDD 的設計原則給出一些 Go 語言的小程式範例哦!不過僅展示架構的思想,和實際專案架構還是有些區別的。
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up