1.引言
在開始之前,我想我們有必要先了解以下DDD的主要參與者。因為畢竟語言是人說的嗎,就像我們面向對象編程一樣,那通用語言面向的是?
DDD的主要參與者:領域專家+開發人員
領域專家:精通業務的任何人。
開發人員:開發+測試。
領域專家擅長某個領域的知識,專注于交付的業務價值。
開發人員則注重于技術實現。
開發人員總是想著類、接口、方法、設計模式、架構等。以面向對象的編程思想進行思考,思考如何進行抽象、封裝、繼承、多態等。而領域專家對軟件中的框架、持久化、數據庫等沒有概念,而這也就導致了他們之間交流的困難性。
那怎么解決交流障礙這個問題呢?
2. 通用語言
拋開DDD而言,單從字面意思來理解,我們首先肯定先聯想到作為世界使用最多的語言--英語。
英語之所以能成為通用語言,我想無非是以下幾方面:
- 簡單易學
- 使用率高
- 國際通用
那DDD中通用語言又是怎樣呢?
首先它也同樣要擁有【簡單】的特性,這樣才便于理解和傳播。
其次,它也要有【通用】、【使用率高】的特性,因為只有在軟件開發的過程中,團隊范圍內所有的參與人員廣泛使用,才能準確傳遞業務規則。
通用語言是團隊交流的基礎上建立起來的,代碼則是基于通用語言來進行業務表達的。
3. 舉個例子
項目經理安排了一項任務給我:
圣杰這個補丁處理下【可銷控制】這個需求。
聽后,真是一臉懵逼,【可銷控制】是什么鬼?
這明顯是專業術語,我不懂的專業術語。
按照DDD對通用語言的定義,【可銷控制】就不算通用語言。
因為只有領域專家知道它的含義,開發人員一臉茫然。
但通用語言是領域專家和開發人員一起創建的,所以我們開個需求會議,來梳理下專業術語背后的含義。
可銷控制的應用場景是:
在ERP中,在做單時對銷售員負責的客戶進行范圍控制。比如,A客戶是銷售員小李發展過來的,小李希望僅能自己負責對A客戶的業務。
這么一解釋,是不是明白不少了。但是不是還有幾點疑惑?
- 可銷范圍如何配置?
- 做什么單據時進行可銷范圍控制?
- 如何進行可銷控制?
- 可銷控制需不需要參數控制?
經過討論,得出以下結論:
- 提供專門的【銷售員-客戶可銷控制】界面進行銷售員-客戶映射數據配置。
- 做銷售訂單單據時進行可銷范圍控制
- 在錄入銷售員后,做單員在選擇客戶列表時,客戶列表中僅顯示可銷范圍配置的客戶數據。
- 提供【銷售員-客戶可銷范圍控制】系統參數,勾選后才進行可銷范圍控制。
好了,這下需求理解個七八九了。
原來可銷控制就是銷售員-客戶銷售范圍控制。
咱們用一句話來精簡下這個需求:
可銷控制是指銷售員所負責客戶的范圍控制,當勾選了【銷售員-客戶可銷控制】參數后,在做銷售訂單錄入客戶數據時,客戶列表僅能選擇到在【可銷控制】列表中為該銷售員配置的客戶。
經過這么一說明,我們是不是已經理清了【可銷控制】這個需求點。
那這種通過團隊交流達成共識的能夠簡單清晰準確傳遞業務規則的語言(可以是文字、圖片等)即可稱為通用語言。
在通用語言的術語中,名詞用于給概念命名,形容詞用于描述這些概念,而動詞則表示可以完成的操作。
4. 通用語言的價值
就像上面所說的那樣,通用語言的最大價值是解決了交流障礙問題,使領域專家和開發人員能夠協同合作,從而能夠確保業務需求的正確表達。
通用語言包含術語和用例場景,且能夠直接反映在代碼中。
基于通用語言,開發人員能夠開發出可讀性更好的代碼,從而將業務需求準確轉化為代碼設計。達到DDD的目標代碼即設計,設計即代碼。通俗的講,也就是開發人員寫的代碼領域專家也能看懂。
5.通用語言的代碼表達
既然開發人員也要基于通用語言進行代碼開發,那代碼如何體現通用語言呢?
在《實現領域驅動設計》書中有一個簡單的例子(P238),我們一起來看一下:
- 系統必須對User進行認證,并且只有當Tenant(租戶)處于激活狀態時才能對User進行認證。
上面這個用例就是基于通用語言的用例,簡單清楚的說明了業務規則。
我們先看第一種代碼實現:
bool anthentic = false;
User user = _userRepository().FindUserByTenantIdAndUserName(tenantId, userName);
if(user!=null)
{
authentic = user.IsAuthentic(password);
}
return authentic;
這段代碼完全不能反應通用語言,主要存在以下問題:
- 這段代碼先查找user,再對user進行密碼匹配來完成認證。其中
user.IsAuthentic(password);
表示的是“用戶是否被認證”的意思,而沒有表達出“認證”這個過程,即“對用戶進行認證”。 - 未體現“檢查Tenant是否處于激活狀態”這個前提條件。
知道問題后,我們可以講代碼略做改動:
bool anthentic = false;
Tenant tenant = _tenantRepository.FindTenantById(tenantId);
//檢查租戶是否激活
if(tenant!=null&&tenant.IsActive){
User user = _userRepository.FindUserByTenantIdAndUserName(tenantId, userName);
if(user!=null)
{
authentic = tenant.Authenticate(user, password);//租戶對用戶進行認證
}
}
return authentic;
以上代碼雖然也不是最終結果,但至少對通用語言進行了體現。通讀代碼,就能明白業務用例,體現代碼即設計這一思想。