領域驅動設計:軟件核心復雜性應對之道_人人都可以領域驅動設計(一)

cd81d8e93378cac1c81eaca923caa964.png

最近幾年,領域驅動設計(Domain-Driven Design,DDD)這個術語越來越多地出現在軟件工程師的視野里。對DDD不熟悉的人可能會覺得它是軟件領域里的一個新的概念,但是實際上,Eric Evans在十幾年前就已經提出了這個概念。這個“古老”的概念在之所以能夠重煥新生,很大程度上是因為遇上了“微服務”這個浪潮。如果把DDD里面的理念拿去和微服務架構做對比,你會發現它們有著高度的相似性——DDD里的限界上下文不正是微服務架構中的微服務嗎?于是,各大公司紛紛運用DDD的方法論來構建自己的產品架構。有些團隊成功地將DDD結合到了產品架構中,產生了許多優秀的實踐;也有些團隊反映DDD太過復雜,很難落地。那么DDD究竟是個什么樣的理念?為什么大家都爭先恐后地使用它,彷佛不加點DDD都不好意思說自己是微服務架構?然而又為什么那么多團隊說DDD難以落地?本文將會結合簡單的代碼實現來談談筆者對DDD的理解。

什么是領域驅動設計?

軟件的核心是其為用戶解決領域相關的問題的能力。

軟件就是為了解決某一領域相關問題而存在的,比如一個普通的計算器軟件,就是為了滿足我們進行簡單的加減乘除運算而存在。對于計算器這種小而簡單的軟件,一個普通的軟件工程師可能花上幾天就能過設計開發出來,而且基本不會出現Bug。但是對于一些大型而且復雜的系統,一個團隊都得花上很長的時間去設計整個架構,然后經過n輪迭代才能開發出可用的版本,而且后面還有各種Bug要去處理。比如證券交易系統,里面就包括了用戶系統、賬戶系統、訂單系統、撮合系統等一系列的子系統,而且其中的調用關系和業務都非常復雜。像這樣一個龐大的系統,怎樣才能把它設計好呢?這正是DDD要回答的問題。

領域驅動設計(DDD)是一種軟件開發的方法論,旨在幫助我們設計出高質量的軟件模型

在軟件領域,解決復雜問題的方法不外乎是“分治”和“抽象”,DDD也是基于這兩個理念建立起一套方法論。其中將一個系統劃分成多個限界上下文,限界上下文中劃分出多個子域,這是分治;然后在分別對各個子域進行領域建模,這是抽象。當你在設計一個業務復雜的系統卻無從下手時,嘗試一下DDD,說不定困難就會迎刃而解了。DDD中最核心的理念就是領域建模,可以說它提供的各種方法都是為了幫助我們設計出更能準確傳達業務規則的領域模型。一個好的領域模型可以讓一個系統更加健壯,可以讓一個框架易用性更加好,可以讓一段代碼更加好維護。那么,什么樣的模型才是好的領域模型?下面,我們通過一個例子來簡單說明下。

什么是領域模型?
領域模型是關于某個特定業務領域的軟件模型。通常,領域模型通過對象模型來實現,這些對象同事包含了數據和行為,并且表達了準確的業務含義。

日期和時間領域模型

如何設計一個日期和時間API?

首先需要對日期和時間的概念進行建模,從直覺上,我們可以將日期和時間抽象成一個對象Date。另外,時間和日期經常都需要進行格式化輸出,因此我們還需要一個用于表示時間格式的對象DateFormat。為了更好地表示年月周日等概念,再抽象出一個表示日歷的Calendar對象,以及表示時區的TimeZone對象。

cf641c7293aaba7a2c3170891cf5744d.png

相信到這里大家都已經知道,這正是JDK 1.1版本的日期時間API,下面我們先回顧一下它的用法:

public class TestOldDate {public static void main(String[] args) {// 獲取表示當前時刻的Date對象Date date1 = new Date();// 通過Calendar等到指定日期時間的Date對象,采用當前的系統時區Calendar calendar = Calendar.getInstance(TimeZone.getDefault());calendar.set(2020, 2, 10, 0, 0, 0);Date date2 = calendar.getTime();// 進行時間比較System.out.println("date1 is after date2: " + date1.after(date2));// 進行時間的加減法,如獲得昨天的這個時刻:calendar.setTime(date1);calendar.set(Calendar.DAY_OF_MONTH, calendar.get(Calendar.DAY_OF_MONTH) - 1);Date date3 = calendar.getTime();// 對日期格式化輸出DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println("date3: " + df.format(date3));}
}
/* Output:
date1 is after date2: false
date3: 2020-02-07 23:55:39
*/

如果你用習慣了JDK 1.1版本的日期時間API,可能會覺得上述例子中的用法也沒有多大的問題。但是,仔細思考一下就會發現這其中的邏輯跟我們人類對時間的處理邏輯不太像,比如要對時間做加法,首先要將需要操作的Date對象設置到Calendar,然后對Calendar做加法,最后調用Calendar的接口得到結果。時間的加法難道不應該直接對Date對象加上一個時間段就行了嗎?

相信很多小伙伴都會遇到這種情況,自己寫出來的代碼可讀性不夠好。這其中原因可能是對領域的理解不夠深,設計出來的領域模型沒能準確的表達業務邏輯(如JDK 1.1的日期時間模型),或者開發前根本就沒有進行領域建模。這樣容易導致采用了一種“機器思維”去進行開發,而不是按照我們平常思考問題的思維去開發。

JDK 1.8引入了全新的日期和時間API來解決老版本的API所存在的種種問題,下面,我們來看一下比之前更準確地表達日期和時間的領域模型:

99a886debe62164774ef3e327943e5c6.png

JDK 1.8的日期和時間領域模型中的領域知識明顯比老版本的領域模型要豐富很多,而且模型更加符合人類思考日期和時間的思維。下面,我們看下新的日期和時間API的用法:

public class TestNewDate {public static void main(String[] args) {// 獲取表示當前時刻的LocalDateTime對象LocalDateTime date1 = LocalDateTime.now(ZoneId.systemDefault());// 獲取指定時間的LocalDateTime對象LocalDateTime date2 = LocalDateTime.of(LocalDate.of(2020, 2, 10),LocalTime.of(0, 0, 0));// 進行是時間比較System.out.println("date1 is after date2: " + date1.isAfter(date2));// 進行時間的加減法,如獲得昨天的這個時刻:LocalDateTime date3 = date1.minus(Period.ofDays(1));// 對時間進行格式化輸出DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");System.out.println("date3: " + df.format(date3));}
}
/* Output:
date1 is after date2: false
date3: 2020-02-07 23:56:51
*/

實現同樣的功能,JDK 1.8版本的日期和時間API明顯更加簡潔,而且代碼的邏輯更加符合人的思維,可讀性更好(比如使用.now()函數創建當前時刻的LocalDateTime對象,代碼閱讀起來就跟人類的自然語言一樣)。由此可見,設計出一個高質量的領域模型對于軟件系統是多么的重要。

在這個例子中,JDK并沒有顯式地使用DDD提供的戰術建模手段對日期和時間API進行設計,但是從JDK 1.1到JDK 1.8版本中的變化,其中就蘊含著DDD最核心的內容:設計出更符合業務規則和人類思維的領域模型。從這個例子中我們也能看出,DDD并沒有傳說中的那么神秘,也未必一定要運用在復雜的系統,即使是一個簡單的日期和時間API,其中也可以看到它的身影。

如果讓你對JDK 1.1的日期和時間API進行優化,相信很多人都設計不出像JDK 1.8版本的這樣優秀的API,不管在經驗,還是在方法上我們都欠點火候。簡單的API如此,更別說設計復雜的大型系統了。這時,我們需要一些具體的建模方法來指導設計。

DDD的建模方法

DDD主要提出了兩種建模方法來幫助我們設計出高質量的領域模型:戰略建模和戰術建模。

戰略建模根據領域知識對系統進行限界上下文和子域的劃分,戰術建模具體為每個限界上下文設計出領域模型。而這兩者中又內涵很多知識點,光看下面的這張DDD的概念圖,你可能會覺得DDD太過復雜了。

18c450de9c13448dc619c723a8fa96c6.png

確實,DDD的學習曲線比較陡,特別是第一次看Eric Evans所著的《領域驅動設計——軟件核心復雜性應對之道》時,會有種不知所云的感覺。再看Vaughn Vernon所著的《實現領域驅動設計》可能會好點了,但是里面提到的各種概念還是沒能很清晰地理解。所謂“實踐出真知”,只有通過不斷地實踐,才能學習到DDD的精髓,體會到它的魔力。下一篇文章,我們將開始通過實踐一個簡單的業務功能著手介紹DDD的各種理論知識。

http://weixin.qq.com/r/Tx2zqxDE602UrVTI90hd (二維碼自動識別)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457817.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457817.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457817.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

linux 進程通信 消息隊列

詳解linux進程間通信-消息隊列 前言:前面討論了信號、管道的進程間通信方式,接下來將討論消息隊列。 一、系統V IPC 三種系統V IPC:消息隊列、信號量以及共享內存(共享存儲器)之間有很多相似之處。 每個內核中的 I P …

laravel框架——composer導入laravel

第一種:  composer create-project --prefer-dist laravel/laravel projectName "5.2.*"第二種:  composer global require "laravel/installer"  laravel new 名稱轉載于:https://www.cnblogs.com/xj76149095/p/5951822.html…

第七章 心得體會

通過第七章的學習,使自己對驅動程序的認識更加深刻,LED燈的驅動程序幫我我學到很多,還學會了驅動的移植。 學到的知識: 一、編寫LED驅動 1、創建LED驅動的設備文件 第一步:使用cdev_init函數初始化cdev leds_cdev.owne…

wx.checkjsapi是寫在config里面嗎_用Python寫一個程序,解密游戲內抽獎的秘密

前言本文的文字及圖片來源于網絡,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯系我們以作處理。作者: 極客挖掘機PS:如有需要Python學習資料的小伙伴可以加點擊下方鏈接自行獲取http://t.cn/A6Zvjdun分析需求我們先整理下思…

Dev C++安裝第三方庫boost

Dev_C安裝第三方庫boost 安裝步驟 準備工作下載boost庫,下載地址https://sourceforge.net/projects/boost/1. 設置GCC的環境變量PATH 設置環境變量path,在其中加上DEV-C編譯器的路徑(gcc.exe所在路徑),如C:\Program Files (x86)…

bash的一些小技巧

1、從輸入讀入變量 eg:read -ep "input yes or no: " flag 用e選項表示編輯,可以使用backspace刪除 2、數組 a、索引數組 declare -a arr(var1 var2 var3) 用空格分割,如果直接訪問變量$arr, 則獲取的是數組的第一個元素&#xff0…

golang switch_為什么程序員都不喜歡使用 switch ,而是大量的 if……else if ?

點擊上方“我要學編程”,選擇“置頂/星標公眾號”福利干貨,第一時間送達!來自 | C語言Plus請用5秒鐘的時間查看下面的代碼是否存在bug。OK,熟練的程序猿應該已經發現Bug所在了,在第13行下面我沒有添加關鍵字break; 這就…

RabbitMQ 安裝與簡單使用

在企業應用系統領域,會面對不同系統之間的通信、集成與整合,尤其當面臨異構系統時,這種分布式的調用與通信變得越發重要。其次,系統中一般會有很多對實時性要求不高的但是執行起來比較較耗時的地方,比如發送短信&#…

數據庫函數依賴及范式

一、基礎概念   要理解范式,首先必須對知道什么是關系數據庫,如果你不知道,我可以簡單的不能再簡單的說一下:關系數據庫就是用二維表來保存數據。表和表之間可以……(省略10W字)。   然后你應該理解以下…

windows svn

windows svn 1.1Svn和VisualSvn介紹 VisualSvn Server2.5.6(版本控制服務器)免費開源軟件 是基于Windows平臺上的Subversion服務器,它是免費的 官方下載: http://www.visualsvn.com/files/VisualSVN-Server-2.5.6.msi TortoiseSvn…

信息摘要技術及算法介紹

數據摘要算法是密碼學算法中非常重要的一個分支,它通過對所有數據提取指紋信息以實現數據簽名、數據完整性校驗等功能,由于其不可逆性,有時候會被用做敏感信息的加密。 數據摘要算法也被稱為哈希(Hash)算法、散列算法…

AutoLayout的那些事兒

AutoLayout非常強大也非常易用,可讀性也很強,加上各種第三方AutoLayout庫,讓你布起局來猶如繃掉鏈子的狗!根本停不下來!以前的 1label.frame.origin.y label.frame.size.height 10如今只用: 123button.sn…

docker-compose下載慢_編寫Docker Compose時要注意的五大常見錯誤

在構建容器化的應用時,開發人員往往需要某種方法來引導啟動目標容器,以對其進行代碼級別的測試。盡管業界有許多方法可以實現該目的,但Docker Compose是目前最受歡迎的一種方法。它能夠讓如下兩個方面變得容易實現:指定在開發過程…

前端測試利器--Browser-Sync啟動命令

使用browser-sync啟動命令cmd切換到項目的根目錄下**1.browser-sync start --server --files "css/*.css"----------**使用兩個*檢測所有的目錄**轉載于:https://blog.51cto.com/1888512/1862054

VMware實現Android x86 8.1 從安裝到使用

VMware實現Android x86 8.1 從安裝到使用 虛擬機--Android 安裝 Android系統配置 安裝軟件 個性化設計 托坑指南 一些終端模擬器的指令 虛擬機–Android 發現現在安卓虛擬機已經到了8.1,我就試試能不能安裝并正常使用。由于版本過新,網上也沒有一些系統的…

frame越過另一個frame_擁抱swoole(三)之用php實現一個混合服務器

混合服務器,就是可以同時支持http,websocket,tcp等的服務器,用swoole就是這么簡單,分分鐘,就可以愉快地搞物聯網開發了,啥都支持,我采用官方的例子,創建一個混合服務器&a…

Hibernate學習系列————注解一對多單向實例

2019獨角獸企業重金招聘Python工程師標準>>> 開發環境:MysqlEclipse 一對多單向的列子原理:一個班級,多個學生,學生端為多的一端,他們擁有一個外鍵指向相同的班級。 項目結構 需要的jar包 hibernate.cfg.xm…

Spring學習筆記--自動裝配Bean屬性

Spring提供了四種類型的自動裝配策略: byName – 把與Bean的屬性具有相同名字(或者ID)的其他Bean自動裝配到Bean的對應屬性中。byType – 把與Bean的屬性具有相同類型的其他Bean自動裝配到Bean的對應屬性中。constructor – 把與Bean的構造器入參具有相同類型的其他…

sudo apt-get nmap 報錯鎖占用

在Ubuntu中用apt-get命令安裝軟件是出現如下錯誤: 網上搜了一下原因,說是有另外一個程序在運行,導致鎖不可用,原因可能是賞析運行更新或安裝沒有正常完成。這是因為上次更新或者安裝沒有正常完成。 網上的兩種解決方法&#xff1…

python逐行讀取txt寫入excel_用python從符合一定格式的txt文檔中逐行讀取數據并按一定規則寫入excel(openpyxl支持Excel 2007 .xlsx格式)...

前幾天接到一個任務,從gerrit上通過ssh命令獲取一些commit相關的數據到文本文檔中,隨后將這些數據存入Excel中。數據格式如下圖所示觀察上圖可知,存在文本文檔中的數據符合一定的格式,通過python讀取、正則表達式處理并寫入Excel文…