該重視軟件方法了,實現:利潤 = 需求 - 設計

本文篇幅有些長,但是相比閱讀各類書籍,然後理解和吸收,會大大節省很多時間,對於一些書中難以理解的部分做了改進,幫助更好的理解。可能閱讀本文需要一些軟件方法的基礎知識,才能更好理解和吸收,甚至提出反饋建議。希望文本對大家有幫助,當然這需要運用好“隻字不差閱讀”和“隻字不差理解”。

“系統性地輸出式學習”,最大的受益者是自己,其次是“隻字不差閱讀”和“隻字不差理解”的你。爲什麼:“利潤 = 需求 - 設計”

  • 先說需求:一種是“假需求”,一種是“有需求”。假需求做得多,企業成本增加。有需求並非一定是核心價值,通過軟件方法的分析思路和工具從“有需求”中找到“真需求”,提升軟件好賣帶來營收增長(價值需求帶來營收)。
  • 再說設計:指的是用一系列軟件方法分析業務流程和規則,產出需求分析,做好架構設計,提升軟件系統的維護性和擴展性,進而提升軟件生命週期各個環節的效率和質量,降低成本(好的設計降低成本)。

1.背景是什麼?

不知道你有沒有發現,當下軟件行業的工程師在業務抽象與抽象、領域建模、架構設計等能力上似乎並沒有因爲業務快速發展,及技術框架、中間件、分佈式等技術發展成熟,帶來相關的能力的積累和沉澱,反而在退步或消失,甚至很多參加工作幾年的同學都未曾使用過軟件方法中的一些方法。

而軟件方法隨着企業規模變大,重要性其實應該被逐步提升纔對,但實際恰恰相反,越來越不重視,而且需求越來越多,設計越來越丟失,系統擴展性和維護性越來越差,用戶產品越來越複雜。

記得剛參加工作頭幾年,項目一旦立項,架構設計會作爲重要且緊急的關鍵里程碑工作,經歷幾輪評審和修改才進入開發,而且在旁邊聽前輩討論和評審也是受益匪淺,濃濃的技術氛圍推着自己也去學習。但是隨着互聯網快速發展及敏捷迭代的流行,人們漸漸忽視了軟件方法(敏捷迭代並未要求忽視設計,而是在執行時因爲人的原因不再重視,慢慢地不再談及軟件方法)。而且,平時經常聽到重構系統,但是每年都在重構,這是進步,還是退步,我認爲是退步,這值得我們反思。

能力的退步、氛圍的缺失和無效的重構,是總結本文的背景,希望在不久的將來越來越多的工程師熟練掌握軟件方法中的相關方法和工具。

2.什麼是軟件方法?

來自百度百科的定義,軟件方法是指在軟件開發過程中,從軟件需求分析、軟件設計、軟件編碼、軟件維護等環節中,採用適合的方法解決軟件開發中的問題,實現高效的開發,以滿足用戶的需求。

我自己對“軟件方法”理解,從業務到軟件開發的一系列分析方法和工具,幫助我們將複雜的業務知識、架構知識轉變爲團隊中人人能夠理解和上手的統一語言,並藉助諸如UML工具產出具體的設計圖。從而更加系統化、規範化地進行軟件開發,確保軟件系統的可維護性、可靠性和可擴展性。

但是,軟件方法長成啥樣?

比如你學會了設計原則和設計模式,可以說你掌握了設計方法;比如你學會了爲業務構建領域模型,可以說你學會了領域建模方法;比如你學會了通過業務用例和業務時序定義組織提供的價值和組織內如何協同,可以說你掌握了業務建模方法。

設計方法、領域建模方法、業務建模方法等方法的融會貫通,組成一個完整的軟件方法。大部分人可以較好掌握設計方法,但是掌握業務建模和領域建模方法的人比較少。

3.爲什麼軟件方法被忽視?

因爲軟件方法中最核心的能力,比如業務建模和領域建模能力,看起來對完成代碼開發好像沒有幫助,同時掌握它又有一定的挑戰,導致沒有得到足夠多的人重視。當然還有另外2個原因:

  • 只重技術不重業務,只是用代碼實現業務系統,並未考慮業務擴展性、維護性等。
  • 業務和領域建模是種手藝,但凡手藝都需要在實踐中不斷經歷磨鍊。

4.軟件方法的重要性?

一個好的軟件產品,離不開對業務的理解和抽象,作爲技術人員,先要對業務充分理解,並將業務知識做抽象,才能將業務理解和抽象轉換成更好的軟件設計,這是技術人員能力發展非常重要的步驟及好的軟件系統的前置條件。

而且軟件方法技能的熟練度提升,會帶來團隊和個人整體交付質量和效率的提高,個人的價值和影響力也會越來越好,職業發展路徑越清晰。

本文篇幅有些長,但是相比閱讀各類書籍,然後理解和吸收,會大大節省很多時間,況且通過自己的總結輸出,對於一些書中難以理解的部分做了改進,幫助更好的理解。可能閱讀本文需要一些軟件方法的基礎知識,才能更好理解和吸收,甚至提出反饋建議。

最後,希望文本對大家有幫助,當然,若想真的有幫助,需要運用好“隻字不差閱讀”和“隻字不差理解”。若總結中有錯誤或不合理的地方,歡迎留評指出,不勝感激。

 

一、軟件方法概要

產品和研發在進入具體開發之前,我認爲需要思考清楚如下3個問題:(1)首先,需要了解需求會不會變化;(2)其次,無論需求變與不變,要知道提升“好賣”和“降本”的方法;(3)最後,實踐建模方法挖掘被研究組織提供的價值,降低實現成本。

1. 需求會不會變化

人類或組織相關需求從過去到現代大部分不會發生變化,在將來大概率也不會變化,比如存/取錢、喫飯、看病、出行、看戲等。但是隨着技術發展和資源逐漸充裕,很多需求會不斷出現或發生演進,比如買東西不要出門、看戲到看電影/短視頻、喫飯點菜時希望不要等待。無論需求變與不變,實現需求的方式會隨着技術發展而逐步變化,這是毋庸置疑的。

比如,以“取錢買商品”需求爲例,這個需求從古代到現在都沒有發生變化,只是需求的實現方式變得更高效了。10幾年前,我們要從家裏先坐車去銀行取完錢,然後坐車去商場買東西,最後從商場拿着商品坐車回家,一天下來將人肉這個大的物流不斷挪動,人累效率又低。現在可以直接在淘寶上買商品,然後網上支付,最後由物流公司把貨物寄到你家裏,人肉這個物流可以做到足不出戶。

而且,隨着技術發展,原先因爲資源和技術受限,很多需求沒法得以實現,或者需求體現方式單一。採用建模思維,可以更高效和高質量挖掘出需求的價值,進而通過付出心血和努力獲得競爭優勢和成本降低。

比如,以“餐館點菜”需求爲例,從古代到移動互聯網之前,你每次去餐館喫飯,服務員會給你拿一張菜單過來,然後你邊點他邊寫,遇到人多的時候,服務員根本來不及招呼你,你要過好久才能完成點菜,上菜就更慢了。但是隨着互聯網普及及智能手機的出現,基於二維碼掃碼點餐大大提升了餐館效率及降低了人力成本,也提升食客的消費體驗。

2. 提升“好賣”和“降本”的建模方法

任何一家企業都不得不重視的公式:“利潤 = 收入 - 成本”。作爲軟件行業的產品和技術人員來說,我們演進下公式:“利潤 = 需求 - 設計”。通過挖掘需求價值提升產品好賣,做好軟件工程的設計降低成本,進而提升利潤。

建模技能的有效運用會決定需求的價值、決定軟件工程設計的質量。但是,什麼是建模?

建模的本質:總結經驗的過程,將經驗總結爲方法和工具。

以人的大腦爲例,通過學習、做項目積累經驗、聽分享、做交流等動作完成大量的信息輸入之後,大腦會完成一輪建模,並總結出一些方法和模型,變成大腦的模型之一,模型越多個人能力越強,然後在下次自己遇到類似問題時,能夠基於模型給出更好的方法。

放在軟件開發上,建模是軟件生命週期各個階段的模型組合,包括業務建模、需求分析、領域建模和軟件設計。每一個建模技能細節的提升,能夠更好挖掘到產品價值或降低研發成本,提升產品“賣相”,及提升架構的質量屬性(如擴展性、可維護性、安全性等),這些都會帶來“利潤”提升。

提升好賣 業務建模 組織要解決什麼問題,爲其他組織提供什麼價值,提升好賣。
需求分析 爲了解決組織問題,待開發系統需要提供什麼功能。
降低成本 領域建模 爲了提供功能,系統內部應該有什麼樣的核心機制,包括隱含業務知識的領域模型,領域模型中領域對象的生命週期,系統協作流程。
軟件設計 爲了提供好賣的產品,如何用選定的技術實現,包括概要設計和詳細設計。

3. 實踐建模方法挖掘價值和降低成本

以“人”系統爲例,人會走路,喫飯,說話,跳躍,還會拉馬車、搬磚、講笑話等,但是我們人這套系統並沒有走路子系統、喫飯子系統、說話子系統、跳躍子系統。反而是呼吸子系統、消化子系統、神經子系統、血液循環子系統。人體的每個子系統互相協同完成走路、喫飯、說話、跳躍等外在功能表現,併爲其他組織提供價值,比如爲房地產提供搬磚價值,爲小朋友講三國演義(凱叔講故事)。

從上面的例子我們可以學習到,實現產品不僅僅是簡單的把外在功能表現當中需求,然後變成一個個子系統。合理的方式是:研究組織對外提供的價值、爲了實現組織對外提供的價值,組織內需要開發什麼系統、不同的系統如何協作及用什麼樣的技術實現更好的產品體驗和性能表現。

以下以銀行爲例,按順序踐行建模的4個方法,因爲無銀行經驗,銀行系統的實際情況肯定和下圖有出入。

通過表格進一步理解上圖中的業務建模、需求分析、領域建模和軟件設計的職責。

提升銷售 業務建模 組織要解決什麼問題,爲其他組織提供什麼價值,提升好賣。比如上圖,通過“銀行”組織,爲“人”這個組織提供資金存款,取款,轉賬價值,帶來資金安全、利息收入、便捷價值。但是每次取款都需要坐車去銀行、取號、等待櫃員叫號,不是特別方便和靈活(要解決的問題)。
需求分析 爲了解決組織問題,待開發系統需要提供什麼功能和性能。比如上圖,客戶去銀行櫃檯通過“櫃員”取款效率較低,爲了提升組織對外的便捷價值,需要研究組織中的人肉系統“櫃員”,創造“ATM取款機”自動化系統來替換櫃員這個人肉系統,提升取款效率。同時因爲在ATM取款機上取款,安全要求更高,相應的風控系統也是研究的系統。
降低成本 領域建模 爲了提供功能,系統內部應該有什麼樣的核心機制。比如上圖,ATM取款機要提供取款功能,通過分析領域模型(領域關係和職責)、狀態機(對象生命週期建模)和系統時序圖(描述協作)研究清楚待改進的系統。
軟件設計 爲了實現好賣的產品,如何用選定的技術實現。比如上圖,大的維度包括概要設計和詳細設計,概要設計和詳細設計內又分別包含具體的設計細節,比如概要設計中有物理架構,邏輯架構等;詳細設計中有數據庫設計,接口設計等。

按照業務建模、需求分析、領域建模和軟件設計對組織進行建模分析,可以強迫我們高質量思考,產出高質量的設計降低開發成本和維護成本,進而產出“更好賣”的產品。

二、業務建模

業務建模核心是找到“被研究的組織”向“其他組織”提供的價值,一般稱其他組織爲用戶/客戶,但用戶/客戶過於泛化。當然,在面向社媒/股東等,可以說我們有多少用戶,但是產品和技術在討論具體的需求細節時,需要從泛化到具體。

如何從泛化到具體,核心是用老大和涉衆來替代其他組織。老大是爲軟件付費的人(或是最核心的用戶),涉衆是除老大之外相關的人,或者老大就是涉衆裏最重要的人。軟件開發時優先滿足老大需求,然後是滿足涉衆需求。

比如銀行的涉衆是家裏有儲蓄的人和有貸款需求的企業,銀行通過一定的利息吸納涉衆的儲蓄,然後以更高的利息貸款給企業,這就是銀行的商業模式,各種涉衆的需求都得到了滿足。

業務建模採用最核心的2個模型,分別是業務用例和業務時序。

業務用例表示被研究組織對外提供的價值,業務時序表示被研究組織內部各個系統如何相互協作實現組織對外價值的提供。

還是以“軟件方法-概要”中的銀行爲研究對象(銀行每個人都用過,以它爲例更容易理解),來更詳細描述銀行業務用例和業務時序。

1. 業務用例

業務用例要素:業務執行者和業務用例。

業務用例畫法:用照相機對準被研究的組織,誰和這個組織有交互,誰就是業務執行者。如下示例,拿着照相機在組織邊界處拍照(比如大門),誰和組織有交互,誰“可能”是業務執行者。爲什麼說可能呢?原因有些和組織有交互並不一定是業務執行者,比如來問路的人,乘涼的人,在組織內工作的人等。

以銀行爲例,儲戶來銀行存款,企業來銀行貸款,儲戶和企業都是銀行這個組織的業務執行者。但是銀行裏的職工雖然也來銀行,但他們只是銀行這個組織裏的業務工人,並不是業務執行者。但是,如果研究的組織是銀行裏的某個子系統,比如點鈔機,這個點鈔機的業務執行者可能是銀行職員。

如下圖,圍繞錢流通的商業模式,古代和現代的需求沒有發生變化,都有存款,取款,轉賬和貸款需求。變化的地方在於名字不同,古時叫錢莊和商人,現在叫銀行和企業。

但是,隨着技術發展和資源不斷湧現,組織的價值在不斷演進,比如現在銀行相比古代的錢莊,除了基本存取款之外,還可以爲企業和個人提供諸如理財、外匯、信用卡、期權等銀行組織對外提供的價值。

2. 業務時序

業務時序畫法:業務時序中包括業務工人和業務實體,通過業務工人和業務時序的相互協同合作,完成被研究組織對外價值的提供。業務工人(圓圈中有個小人)和業務實體(圓圈+下劃線)表示方式見下圖。要特別注意,業務實體的抽象級別要一致,比如系統和表結構都出現在業務實體中,肯定出現了抽象級別不一致的情況。

以“銀行取款業務用例”爲例,先畫出銀行早期的業務時序圖,然後畫“點鈔機"出現的改進業務時序圖、最後畫"ATM取款機"出現的改進業務時序圖。通過業務時序的不斷修改(但是業務用例一直沒有改變,還是銀行組織對外提供取款價值),來感受業務時序在改善組織問題、提升用戶價值體驗研究上的價值。

2.1 櫃檯取款

小時候家境不富裕,有點錢媽媽會把錢拿到信用社存款賺點利息,信用社的櫃員拿到錢之後要反覆數幾遍,有時候會拿出一張鈔票對着燈光反覆瞧幾遍,辨別是否真假。同時家裏需要用錢的時候,媽媽又會去信用社取款,當櫃員把錢給到媽媽的時候,媽媽要反覆數幾遍,而且最擔心是怕有假鈔。

那個時候消費沒現在這麼方便,存/取款頻率也沒現在這麼高,效率並非那時候的核心問題,反而鈔票的數量和真假是那時的核心問題。

2.2 點鈔機出現

於是善於研究組織問題的人發現,鈔票數量出錯帶來的影響:要麼是儲戶損失,要麼是銀行損失。同時鈔票要是辨別真假出錯,銀行大概率會損失更嚴重,因爲假鈔會流行。

那麼,不出意外也就出意外了,有人研究出了點鈔機,一次性解決當時核心問題的:鈔票數量和鈔票真假,儲戶和銀行再也不用擔心了,銀行的價值和信任感被加強。於是業務時序圖有了如下變化。

2.3 ATM取款機出現

隨着銀行卡的普及和軟件技術的發展,基於存摺存/取款逐漸淡出了歷史舞臺,上圖中的“1:服務檯填寫取款單”和“2.5打印存摺上的取款金額”在今天已經逐漸消失,取而代之的是基於銀行卡的存取款操作。

同時,隨着互聯網的發展,人們的消費頻率越來越高,存取款的頻率也越來越高,意味着銀行排隊的人越來越多,於是善於研究組織問題的人發現,能不能通過終端解決銀行櫃檯不夠的問題,進而爲儲戶帶來更好的體驗。甚至還可以將終端安裝在離儲戶近的地方,不用來銀行就可以完成取款或存款。於是業務序列圖又有了變化。

銀行組織的“取款用例”雖然經歷了3個階段的業務時序變化,但是對儲戶提供的價值沒有發生變化,而且儲戶的體驗越來越好,社會效率也越來越高。如果某家銀行沒有跟上業務時序中描述的變化,業務萎縮不會意外。

截止這裏,可以看出業務建模中的業務用例和業務時序是一種工具,幫助我們找準組織對外提供的價值,而且價值非常穩定不會輕易發生變化,同時幫助我們找準組織內部需要改善的問題和流程,通過數字化、人工智能、終端等方法進行改善,爲用戶(老大和涉衆)創造更好的價值,持續不斷帶來用戶體驗的變化。

三、需求分析

在“2.3 ATM取款機出現”章節中提到,因爲消費頻次變高,來銀行排隊存/取款的人越來越多,用戶存/取款體驗越來越差,所以研究組織在存/取款的體驗時,提出了存/取款終端設備:“ATM取款機”,進而改進了業務時序圖。所以我們需求的研究範圍來自“2.3 ATM全款機出現”章節改進後的業務時序圖。

需求分析分爲系統用例和系統用例規約。以下以ATM取款機爲研究對象,分析系統用例和系統用例規約。

1. 系統用例

系統用例包括系統執行者和系統,系統執行者是系統的使用者,比如ATM取款機是系統,儲戶是系統執行者。

先理解系統執行者和系統:

  • 系統必須能夠獨立對外提供服務,可以是數據服務,也可以是行爲服務,比如“2.3 ATM取款機出現”中的風控系統獨立對外提供取款風控安全監測,短信系統獨立對外提供短信通知服務。
  • 系統邊界是責任邊界,比如“2.3 ATM取款機出現”中的風控系統、短信系統的責任完全不一樣,分別提供自己職責範圍內的服務。
  • 系統執行者和系統要有交互,比如研究的系統是售票系統,系統執行者是售票員。如果研究的系統是12316 APP,系統執行者就不是售票員了,而是旅客。
  • 系統執行者有主執行者和輔執行者,輔執行者是被動參與,比如“2.3 ATM取款機出現”章節中的儲蓄系統是ATM取款機的輔執行者。
  • 系統執行者不一定是人,也可以是系統或時間,比如定時器可以是系統執行者,本質上是驅動系統運行。

識別系統執行者和系統用例方法比較簡單,如果業務時序圖畫的非常標準和詳細,系統執行者和系統用例都可以來自業務時序圖中。以“2.3 ATM取款機出現”的業務時序圖爲例,給出ATM取款機的系統用例。

因爲“2.3 ATM取款機出現”只是畫了取款業務時序,實際還可能包括查詢餘額、轉賬、存款等業務時序流程。所以完整的ATM取款機系統用例如下:

4個用例的主執行者都是儲戶,但是輔執行者沒有短信系統和風控系統,原因是研究的系統是ATM取款機。如果研究的系統是儲蓄系統,則儲蓄系統取款用例的輔執行者會有短信系統。需要把研究的系統作爲前提。

2. 系統用例規約

一個用例一份用例規約,每份用例規約是需求細節的詳細描述,包括用例名稱、系統執行者、前置條件、後置條件、涉衆利益、主要流程、擴展流程、業務規則、質量要求、設計約束等。需求描述的詳細程度直接影響交付質量,用戶體驗。

以ATM取款用例爲例:

受限於銀行業務知識不足,以上的需求用例規約不一定詳盡,但是從前置條件、後置條件、涉衆利益、主要流程、擴展流程、業務規則、質量要求、設計約束等角度描述出來的用例,會影響從需求→研發→交付上線整個生命週期的效率和質量。

四、領域建模

DDD,中文叫領域驅動設計,領域可以理解爲業務,比如郵件業務、銀行存款業務等都是業務領域,告訴我們要從業務出發去設計我們的系統,是“軟件方法學”的範疇,提供了一套思維模式和分析方法用於開發複雜軟件的系統化方法和思想。

DDD價值是什麼?可以提供系統從0到1搭建、也可以指導系統架構治理、還可以指導架構師培養等。如何運用DDD,主要是學習DDD的“手藝”方法,比如事件風暴、聚合、值對象等。

1. 事件風暴識別領域名詞

領域建模最核心也是最重要的一步是識別領域對象,只有領域對象被識別出來,才能基於領域對象畫出領域對象之間的領域關係。如何識別領域對象有3種方法,分別是:(1)僅通過聊天就能識別,這種人是絕對高手;(2)事件風暴法,通過多人協作完成;(3)通過系統用例找對象。

爲了增強協同,往往推薦事件風暴法,但需要業務、架構師、開發人員等一起,基於腦暴的方式來完成,但如果團隊中,或者產/技之間沒有形成這種能力,我建議由懂的人來完成(比如架構師),基於完成後的領域模型拉上相關同學分享和評審,請大家幫忙補充是否有遺漏的領域對象,然後基於新的領域對象再次完善領域關係。

事件風暴法分爲3個順序動作:識別領域事件 → 識別命令 → 識別領域名詞。還是以銀行爲例。

識別領域事件

領域事件是指已經完成或發生的事實,比如資金已轉賬、資金已存款等,是完成時 + 被動語態,比如資金已存款 = 資金被存款,“已”表示完成。請記住,在識別領域事件的過程中,需要重點關注那些可能在需求文檔中沒有提到的領域事件及業務規則,並把業務規則找出來進行管理。

以銀行爲例,包括現金、理財、貸款、信用卡、積分、短信等業務。按照“完成時 + 被動語態”來識別領域事件,包括資金已存款、資金已轉賬、貸款已到賬、資金已取款、積分已消費、資金已存定期、資金已轉活期等。

其中資金已存款和資金已存定期爲什麼不用一個領域事件:資金已存款。是因爲資金已存款和資金已轉定期背後的業務邏輯不一樣。比如:活期存款利息0.3%,但是一年定期利息1.75%,二年定期利息2.25%,而且活期可以隨時取款/轉賬,定期需要先轉成活期才能取款/轉賬,而且一旦轉成活期,利息只能按照活期0.3%計算。這些業務規則需要被單獨整理和管理起來。

下圖以現金業務和貸款業務識別對應的領域事件及業務規則。

識別命令

領域事件是指已經完成或發生的事實,而命令是引發領域事件發生的操作,及誰執行了該命令(執行者是誰),執行該命令時做了什麼查詢操作(執行者→發起命令→事件發生)。

比如領域事件“資金已轉賬”的命令是“轉賬資金”,轉賬人是“銀行櫃檯”,轉賬人在轉賬資金時需要查詢出“目標儲戶”,也可以將“目標儲戶”理解爲被執行者。有時一個領域事件的操作人有多個,比如除了“銀行櫃檯”,可以是櫃檯經理,也可以是大堂經理在自助終端幫助轉賬,甚至是儲戶自己,因爲儲戶可以在手機銀行轉賬。

以此類推,直到將所有領域事件的命令和執行者找出來,以“現金業務”爲例,識別命令和執行者後,得到下圖:

識別領域名詞

識別領域名詞是指從領域事件、命令、執行者、查詢對象(如存入資金之前需要登錄儲戶賬戶)等上找出名詞。比如領域事件“資金已轉賬”的命令是“轉賬資金”,命中中的資金也是領域名詞,同時執行者中的“儲戶”、“櫃員”、“櫃員上級”、“目標儲戶”也是領域名詞,

這裏要說清楚:領域名詞 ≠ 領域對象。領域對象可能是多個領域名詞的合併,也可能領域名詞是領域對象的一個角色,比如目標儲戶只是儲戶(領域對象)的一個角色,儲戶和目標儲戶合併爲是儲戶(領域對象)。

如下圖,以“現金業務”爲例識別出來的領域名詞有儲戶、資金、賬戶、目標賬戶、櫃員、櫃員經理、大堂經理等,可能還有不全,需要通過在後續領域模型設計,及評審過程中發現和挖掘。當然這些領域名詞還不是最終的領域對象,需要對領域名稱進行合併歸類、抽象,比如櫃員、櫃員經理、大堂經理可以抽象成銀行職員。

上面通過便籤紙產出領域事件→命令→領域名詞的方式適合多人協同,如果是架構師一個人的工作,可以通過表格的方式來整理,更簡單和直觀,效率也更高,比如下圖:

業務模塊 執行者 命令 領域事件 領域名詞 業務規則
現金業務 儲戶 取出資金 資金已取出 儲戶、資金 取款需要登錄ATM限額1萬
儲戶 轉出資金 資金已轉出 儲戶、賬戶、資金、對方賬戶 ATM限制轉賬手機銀行上限20萬
儲戶 存定期 資金已轉定期 儲戶、賬戶、資金 1年期利息1.75%2年期利息2.2.5%......
櫃員 取出資金 資金已取出 櫃員、儲戶賬戶、資金  
櫃員 存入資金 資金已存入 櫃員、儲戶賬戶、資金  
櫃員 轉出資金 資金已轉出 櫃員、儲戶賬戶、對方賬戶、資金  
......        

2. 理解類圖6種UML關係

插入UML關係理解會有點突兀,但是先深入理解類圖中的UML關係,會在接下來的領域建模中不會受限於UML概念模糊,進而影響表達領域對象之間的關係。

理解類圖的6種UML關係,在後續的領域模型、模塊設計、類圖等關係中都會用到,但是並不是說每種設計都需要完全用到6種關係,比如模塊設計可能更體現依賴關係,領域模型更多會使用關聯、泛化和聚合(其實應該是組合)關係,類圖中更多使用關聯、泛化和實現。

加個自己的理解:依賴、組合、聚合、泛化和實現5種UML關係都是關聯關係的擴展,目的是爲了讓業務知識或代碼設計時含義更具體,抽象更清晰。比如組合表達的是整體和部分的關聯關係,且部分不能脫離整體存在。

關聯

表示兩個類之間有聯繫(2端沒有箭頭,可以理解爲是一種雙向關係,比如通過客戶可以以查找到該客戶的賬號,通過賬號可以查找到賬戶所屬客戶),其中一個類對象可以訪問另一個類對象的屬性或方法,可以說是數據導航關係。關聯關係用一條普通的實線表示。比如客戶和賬戶之間的關聯關係,客戶可以查詢自己賬戶中的資金。

具體的含義解讀:1個客戶至少有1個賬戶(如果在銀行未開號就不屬於客戶),最多*個賬號。但是一個賬戶只能屬於一個客戶,不能屬於多個賬戶,否則賬戶中的資金就變成了共享資金了。

依賴

表示一個類的實現依賴於另一個類,即一個類的方法中使用了另一個類的對象,表達的範圍更廣。而且,關聯是依賴的一種關係。依賴關係用一條帶箭頭的虛線表示(只有一端有箭頭,是一種單向關係)。比如櫃員(銀行職員)接收客戶轉賬需求時,依賴客戶賬號授權(提供銀行卡,輸入密碼)才能幫助客戶完成轉賬。

依賴常見應用場景,可能更多出現在模塊的依賴關係,比如根據依賴倒置原則,低層模塊不能依賴高層模塊,比如應用層依賴領域層,但是不能出現領域層依賴應用層。

組合

表示整體與部分之間的關係,整體包含部分對象,部分對象不能脫離整體而獨立存在。組合關係用一條帶實心菱形箭頭的連線表示,指向整體類。比如人和四肢,沒有人,四肢也沒有存在價值;比如員工和員工技能,沒有員工,員工技能也沒有價值,所以組合的另外一種隱藏價值是用於保護業務規則被破壞的一種手段,是領域模型中非常重要的一種關係。

什麼是規則被破壞,比如在併發場景下,(1)線程A查詢員工小明編程技能,沒有編程技能;(2)線程B也同時查詢員工小明編程技能,也沒有編程技能;(3)線程A添加小明編程技術,添加成功;(4)線程B也開始添加小明編程技能,因爲之前沒有查詢到,也會新增編程技能。這樣導致的結果是,小明的編程技能被增加了2條,但業務規則要求編程技能只出現一條。

合理的方式,是把小明的信息作爲整體,不能被2個人同時操作,也就是說把小明作爲整體鎖起來,只有小明事務整體提交,並釋放鎖之後,其他線程才允許操作。

比如,銀行系統中,現金賬戶和定期賬戶、活期賬戶是整體和部分的關係,現金賬戶中的資金分別活期、定期。

聚合

表示整體與部分之間的關係,整體可以包含多個部分對象。聚合關係用一條帶空心菱形箭頭的連線表示,指向整體類。比如,人和汽車、電腦的關係,汽車和電腦是我的財產,我也可以轉移財產給家人用,甚至賣掉(組合中的人和四肢關係,四肢沒法轉移,甚至賣掉)。

比如,銀行職員的業務結果包含哪些?可以用聚合表示,比如包括個人客戶和企業客戶,因爲如果職員離職了,個人和企業客戶都可以轉交個接手的銀行職員跟進。

領域模型經常提到聚合,但卻用實心菱形箭頭連線表示,可以說表達的是組合,關係卻用了聚合關係表達。

泛化

泛化用空心箭頭表示,是一種統稱或分類關係,比如生物可以分爲動物和植物,動物又可以分爲哺乳動物和爬行動物。或者哺乳動物和爬行動物統稱爲動物。也就是說,泛化出來的對象是一種更抽象的概念,能夠表達不同對象間的共性和個性。

比如,哺乳動物和爬行動物共性部分是都有呼吸系統,用於呼吸氧氣和排出二氧化碳;個性部分在於哺乳動物的卵類很少,多數是胎生的,而爬行動物的卵類很多,多數是卵生的。

比如,銀行系統中的賬戶和貸款賬戶,資金賬戶可以用泛化表示,共性部分是都需要有賬號和密碼驗證,個性部分在於貸款賬戶是欠錢(貸款金額)、資金賬戶是儲蓄(儲蓄金額)。

實現

表示一個類實現了一個接口,必須實現接口中定義的所有方法。實現關係用一個空心的帶箭頭的虛線表示。實現關係一般出現在代碼設計的類圖上,是一種面向接口編程思路。比如,電商購物支持買家在線付款,付款類型包括招商銀行、工商銀行、花唄等。

備註:上面的UML圖中,有些有+,有些沒有,區別是什麼?

1.代碼UML圖:+在UML圖中表示訪問權限,比如+表示public,-表示private,沒有表示包訪問權限。

2.領域模型UML圖:領域模型表達時沒有必要體現代碼實現時的訪問權限。

3. 建立領域模型

事件風暴的價值是識別出領域名詞和業務規則,是對業務的直接描述,而領域建模的價值是將領域名詞轉爲領域模型,需要抽象和提煉,比如把櫃員,大堂經理抽象成崗位,更加深入業務本質,而不僅僅停留在表面,是DDD最重要的成果沉澱。建立領域模型包括從一堆領域名詞中識別領域對象,梳理領域對象之間的關係,領域對象的關鍵屬性,將領域對象組成模塊等。產出領域模型有2個好處:

1.將領域知識可視化,方便日常溝通、共識。

2.指導軟件設計和編碼,轉換成代碼和數據庫。

領域模型通常用UML來表達,UML中文名稱是“統一建模語言”,從名字可以看出2個關鍵信息:“統一”和“建模”。“統一”是指統一了大家溝通時的語言和名詞,溝通效率高;“建模”是指方法,採用UML工具可以幫助我們完成建模的動作。

UML中最重要的概念包括:類和類6種關係(聚合、組合、泛化、實現、關聯、依賴),類之間通過6種關係連接產生的圖,叫類圖。

領域模型中最重要的概念是實體和值對象,領域模型中的實體可以映射爲類,畫領域模型就是畫實體間的類圖關係,比如賬戶是實體。

畫領域模型的時候,一開始只需要畫領域對象的關係,且領域對象一般用中文表示(避免畫的時候就考慮到我的實體就是類名),同時領域對象的屬性也可以先不體現,或者只體現核心屬性幫助理解領域對象,畢竟我們的目的是通過領域模型表達業務知識。

在畫領域模型圖的時候,需要區分領域模型是描述業務,類圖是技術視角的視線,比如下圖:

解讀領域關係圖

儲戶 vs 客戶:事件風暴中識別出來的領域名詞是儲戶,這裏爲什麼把儲戶改成了客戶?主要是對儲戶做了一層概念抽象,字面理解儲戶一般是存錢,但是去銀行不僅僅是存取錢,還包括貸款,理財等,可以是個人,也可以是企業,所以統一用客戶作爲領域對象(領域建模時需要把看到的現象進行抽象)。

新增資金流水:事件風暴中未識別出來資金流水領域名詞,這裏爲什麼加上資金流水領域對象?主要是資金會發生變動,而且資金的安全性和準確性要求非常高,需要能夠追溯每筆資金動向,所以這裏加上了資金流水領域對象(事件風暴方法中可能會存在潛在看不見的領域事件,或者用例中可能無法看見的用例)。

資金 vs 現金賬戶:事件風暴中識別出來的領域名詞是資金,這裏爲什麼把資金改成了現金賬戶?主要是事件風暴階段,我們只考慮存取款等操作,更多的是對資金操作。但是客戶在銀行的業務可能是現金業務,也可能是貸款業務、信用卡業務、理財業務,每個業務應該是獨立的業務規則和業務流程。

操作人 vs 職員&賬戶:資金流水關聯操作人,但是操作人和儲戶&職員的關係是什麼,表示現在還沒完全想清楚用哪種UML關係表達。

新增崗位、交易類型等:事件風暴中識別出來領域名詞是櫃員,櫃員經理和大堂經理,但是上圖中領域名詞變成了崗位,這也是畫領域對象時經常會用到的方法,需要做一層領域名詞提煉,避免直接平移。也就是說我們眼睛看到的事物,不一定和系統中完全一一映射。

關聯

表示兩個實體之間有聯繫(2端沒有箭頭,可以理解爲是一種雙向關係),比如資金賬戶和資金流水是一個關聯關係,每發生一筆資金流水,就會產生一次關聯,所以資金賬戶和資金流水1對多的關係。

當然可以再抽象,比如發生資金流水本質上是發生了一筆交易,也就是說可以抽象出“交易”實體,資金賬戶和交易產生關聯關係,資金流水和交易是聚合關係。

思考:資金和資金流水爲什麼不用聚合關係?資金流水無法獨立於資金。

聚合

按照UML關係中的聚合概念:聚合表示整體和部分關係,部分離開整體可以獨立存在,比如人和汽車,雖然汽車歸屬我,但是我可以將汽車借給別人。同時聚合關係用一條帶“空心”菱形箭頭的連線表示,指向整體類。

但是在領域建模中聚合用“實心”菱形箭頭表示,按照我自己的知識體現理解,用聚合表達不對,或者通過UML關係表達業務知識時,把組合和聚合統稱爲聚合,表達整體和部分關係,且部分離開整體不能獨立存在。

比如上圖,資金賬戶是整體,活期和定期是部分,資金賬戶總額由活期和定期組成,活期和定期有明確的歸屬賬戶,不能離開資金賬戶獨立存在。如果獨立存在,相當於錢跑到別人賬戶裏去了。

泛化

泛化用空心箭頭表示,是一種統稱和分類關係,比如生物可以分爲動物和植物,動物又可以分爲哺乳動物和爬行動物。或者哺乳動物和爬行動物統稱爲動物。也就是說,泛化出來的對象是一種更抽象的概念,能夠表達不同對象間的共性和個性。

比如上圖,賬戶是統稱,而資金賬戶、貸款賬戶、及未畫出來的信用卡賬戶是分類。他們之間的共性在賬戶中,比如賬戶所屬人,賬密。他們之間的個性部分在各自內部,比如資金賬戶是銀行付息給儲戶,貸款賬戶是儲戶付息給銀行。同時每一個分類的業務規則和業務流程差異較大。

思考:賬戶和資金賬戶,貸款賬戶爲什麼不用聚合關係?是否可以用聚合?

值對象

實體和值對象容易模糊,但若抓住“不可變”這個規則,就比較容易區分了,比如歲數5是一個值對象,5放在我身上和放在其他人身上都一樣,或者我兒子今年5歲,明年雖然6歲,5還在。

值對象可以是原子的,比如數字5。也可以是複合的,比如姓名,由姓和名組成。在領域模型中抽象值對象,主要是抽象出組合值對象,或枚舉類型。比如員工狀態,包括實習、正式和離職,員工狀態可以被定義一個值對象。

業務規則

業務規則是軟件非常重要的組成一部分,規則沒有被遵守,導致出現軟件Bug或漏洞。如何更好地管理規則,簡單的方法是基於系統用例進行集中管理,按模塊管理,比如下列表格:

業務模塊 系統用例 業務規則
現金業務 ATM取款 ATM取款需要登錄ATM取款單筆上限3000,單日上限1萬......
櫃檯取款 櫃員需要驗證賬戶所屬人爲取款人取款結束需要取款人簽字確認......
存定期 1年期利息1.75%、2年期利息2.2.5%...未到期定期轉活期,按照活期計息......

五、軟件設計

軟件設計範圍非常廣,比如概要設計中的物理架構、邏輯架構、數據架構、可用性、安全、高性能等。詳細設計中的接口設計、表結構、類圖、狀態流轉等。

本文是從軟件方法角度學習總結,所以在軟件設計章節總結過去被省略、或經常被討論但是彼此都很難說服對方的內容進行總結。還是延續本文銀行案例,因爲無銀行經驗,可能設計細節會有出入,但是不妨礙我們通過分享和討論逐漸理解模糊部分。

我會挑以下部分進行自己知識結構的梳理,分別是狀態機、充血還是貧血、一定要聚合根嗎。

狀態機

通過下圖讀懂狀態機圖示表達,需要特別記住的知識點:從A狀態轉化成B狀態的箭頭直線,描述了某個「事件」發生時,恰好某個「條件」成立,通過具體「動作」,實現狀態A到狀態B的轉化。

比如準備「睡覺」這個事件發生,此時「燈亮」這個條件成立,通過「語音關燈」指令完成關燈,將「燈亮」狀態變成了「燈滅」。

同時狀態機中還有一個知識點需要記住:如果多種事件導致同一個狀態發生,可以直接在目標狀態中描述,比如下圖中的燈滅有3種轉化,分別是睡覺[燈亮]/按下按鈕、睡覺[燈亮]/語音關燈、限電[燈亮]/拉下總閘。

有了對狀態機的理解,下面開始瞭解爲什麼狀態機很重要。

領域模型中的領域對象有生命週期,意味着生命週期變化過程中會因爲不同事件發生,帶來領域對象不同狀態變化。如果狀態演變流程複雜且多狀態,會大大增加系統複雜度及狀態準確性,如果缺乏狀態機的有效設計,會增加代碼處理的複雜度,進而帶來Bug及用戶不滿意。

解決的方法就是基於狀態機描述清楚領域對象狀態流轉的業務知識,然後用代碼實現。還是以銀行爲例,比如領域對象「資金」,資金有交易狀態,包括資金凍結和資金正常狀態。可以設計如下的狀態機。

上圖狀態機把因爲各種事件導致的狀態變化集中在了資金凍結和資金正常2個狀態中,當然也可以把各種事件都畫出來體現圖複雜性,但是我覺得必要性不高,會增加理解成本。

同時資金交易狀態,我只定義了“資金凍結和資金正常”,實際銀行的資金狀態可能不是這2個狀態,但是沒關係,我們要達成的目標:通過狀態機清晰定義哪些事件導致狀態變化,把這些事件用代碼實現它,就能規避很多問題。

另外,狀態流轉背後是複雜業務知識流程,當多個領域對象都存在狀態變化,我們要避免在一張狀態機圖中體現出所有的狀態流轉,那樣起不到簡化業務知識的效果,反而增加理解的難度。

比如賬戶有很多狀態,比如開戶中,凍結中,風控異常,正常等。如果把賬戶狀態和資金狀態放在一張狀態機會大大增加閱讀難度和理解成本。如果再加一個領域對象的狀態在一張狀態機中,複雜度會再次增加。

充血還是貧血

貧血還是充血一直被爭論,其中2個爭論較多:(1)業務邏輯放在實體對象,還是Servie中?(2)實體對象是否需要依賴Repository?不同的人實踐結果不一樣,誰也無法說服誰,這也是爲什麼將“充血還是貧血”作爲一個小節。

爭論1:業務邏輯放在實體對象,還是Servcie中?

貧血模型:一個領域對象的業務邏輯實現由一個服務類和實體對象完成,實體對象包含領域對象中的屬性和數據,服務類中包含領域對象的所有方法,負責具體的領域邏輯處理,及持久層的調用。也可以簡單理解爲領域的行爲都由服務類對外暴露,如下圖:

充血模型:領域對象的業務邏輯實現同樣由服務類和實體對象完成,但是實體對象不僅包含領域對象中的屬性和數據,還包括具體的方法,服務類僅僅面向外部暴露接口,具體的邏輯處理和持久層處理由實體對象完成,如下圖:

通過上面2張圖,看起來充血模型要優於貧血模型,因爲所有邏輯都封裝在領域對象。但究竟貧血模型好,還是充血模型好?爭論不休。但可以確定的是,一個團隊或一個系統來說應該統一模型,不能一個系統中既出現貧血模型,又出現充血模型。

當然,如果只是理論討論,不涉及代碼實現,大部分人可能會贊同使用充血模型,核心原因是充血模型還原了領域模型的原貌(從模型到代碼一致性),包括所有的組合,聚合對象的封裝等,比如資金領域模型,包括活期資金、定期資金、資金明細等。這些關係在領域模型中體現的是業務的複雜度,但如果把複雜度平移到代碼中,可能會增加代碼的複雜度,這也是爲什麼,很多代碼反而是貧血模型,而不是充血模型的關鍵所在。

當然,現實中更多采用貧血模型,還有另外一個原因:充血模型需要更強的領域設計能力和對象抽象能力。而貧血模型把邏輯放在Service中,面向過程編程,或面向步驟編程,可能更符合我們編寫代碼的思路,從第一步...第N步。

如果要問自己的選擇?基於自己的編程經歷,會選擇貧血模型。主要有以下幾個原因:

  • 無論是實體對象,還是服務類,本質上都是對外暴露行爲,上層調用者不關心實體對象或服務類的內部實現,而且從命名上可以方便看出領域的含義,比如FundService和FundEntity。
  • 服務類靈活性高,如果一個領域對象的邏輯非常複雜,把所有領域邏輯封裝在一個實體對象裏,估計實體對象會爆炸,既包含數據,也包含行爲,反而給上層使用帶來負擔。但是服務類可以通過職責拆分,比如拆分成資金取款服務類、資金轉賬操作類等,這些服務類,Repository和實體對象共同組成了領域模型的實現。
  • 經典三層分層(Controller、Service、DAO)將領域模型進行了切割,每一層職責清晰,也容易理解。同時因爲實體對象反應的是業務領域的關係,這個關係並未因爲分爲三層被破壞。

爭論2:實體對象是否要依賴Repository?

關於數據的持久化存儲放在哪裏?也是貧血模型和充血模型的爭論點,如果是業務邏輯爲什麼要關心數據的持久化還是非持久化呢?如果內存足夠大,是否可以直接在內存中運行呢?通過極端條件討論理論指導的正確性,有時候也是一種方法。所以貧血模型是把數據存儲從領域模型中分離出來,變成基礎設施層是一種指導思想。

領域模型的存在不應該依賴任何技術框架,是純粹對業務的邏輯抽象。但是如果使用充血模型,充血模型會依賴Respository,那麼充血模型的Respository操作必須只和本領域相關,如果跨多個領域模型的操作,就破壞了領域。

面向聚合根統一操作

聚合的一個主要特徵是具有不變規則。而維護不變規則的前提是要做好對聚合的封裝,否則,外部的對象就可能無意間破壞聚合內部的規則。

意味着,服務類的行爲入參都是聚合根,比如,以人爲例,人是四肢和器官的聚合根,如果四肢或器官受傷了,肯定需要把人送到醫院才能做治療,不能把四肢砍下來,或者把器官割下來送到醫院治療,治療之後再運回來安裝上去,即便四肢和器官治療好了,但是人估計早就沒命了。同樣現金賬戶也是同樣的道理,在操作定期還是活期,都需要通過現金賬戶這個聚合根。站在技術實現的角度,通過聚合根還能夠保證事務的完整性。

聚合根的邏輯非常符合我們的思維模式,相信在理論層面的討論沒人會拒絕“面向聚合根統一操作”的指導方式,但爲什麼現實情況又有這麼多的爭議,而且實際代碼的實現往往是破壞這個指導方式。

我認爲本質原因有如下幾點:

  • 基於聚合根操作給代碼實現帶來了複雜性,每次操作聚合對象都需要通過聚合根來實現。
  • DDD思想出現在20年前,當時的軟件系統很多是桌面端或者單機,像互聯網這種分佈式系統還未流行,通過聚合根實現事務比較簡單。但是分佈式系統下的分佈式事務下,事務的保障並非聚合根的思想就可以解決。
  • 現實中對聚合對象的操作雖然沒有基於聚合根,但並未破壞“聚合根思想”,這點很多時候並未被提出來討論過。比如,日常操作聚合對象的時候,聚合對象上都有聚合根的ID,同時如果涉及聚合對象變更之後,聚合根也要變化(反之),在Service中會按照面向過程編程,把聚合根也做變化。

所以,我的觀點是“面向聚合根統一操作”的指導思想,是告訴我們一個聚合內部的邏輯要保持一致性(就像事務一致性一樣),每次變更聚合根(比如刪除),對應的聚合對象也要變化。而不是每次都要new聚合根和new聚合對象,這樣確實給代碼編寫帶來了很多成本和複雜度,可以通過聚合根ID來操作對應的聚合根和聚合對象。

應用分層

領域模型是解決業務複雜度的問題,代碼是領域模型的具體實現。可以直接從領域模型平移到代碼(比如DDD中倡導的聚合根統一操作),也可以採用分層架構思想。但是,代碼的實現是否要完全從領域模型平移過來,我自己是打個問號的?

一方面很少見到直接從領域模型平移過來的實現,另外一方面,自己的編程習慣或CR看到的編程習慣,大部分還是面向過程,面向步驟的編程。所以以下給出的應用分層只是參考建議,各自可以參考,也可以調整。但是有些原則需要得到遵守:

  • 依賴倒置:有2個含義,分別是(1)高階模塊不能依賴低級模塊,比如基礎設施層不能依賴應用層;(2)依賴抽象,不依賴實現,層與層之間的依賴通過抽象依賴。
  • 服務粒度:也有叫界限上下文,不管何種叫法,服務的拆分粒度只要拿出來討論,肯定會有不同的觀點。我的觀點,物理粒度無需過多討論,需要的時候自然而然會拆分,比如併發上來了會拆分多個服務,從單體應用多分佈式應用。所以我們更側重在邏輯粒度上,邏輯粒度可以按照聚合角度拆分,未來需要做物理拆分,可以將單體應用中的聚合拆分即可,而且因爲低耦合快速實現物理拆分。

1.橫向每一層

Adapter層:適配層,個人不太喜歡該名字,也許用網關更合適,負責接受來自不同設備的請求和響應,包括請求和響應之間的安全校驗、登錄驗證等。Adapter可以向下依賴應用層,依賴的方式遵循依賴倒置原則,通過抽象Client二方包(接口定義和DTO定義),實現依賴。

應用層:向上暴露領域對象的行爲(叫方法也行),向下執行各領域服務的編排、調用和結果封裝。應用層不應該包含領域的業務邏輯,是很薄的一層。同時應用層不能向上依賴Adapter(通過Client接口抽象暴露領域的行爲),可以向下依賴依賴領域層和基礎設施層,但是層與層之間的依賴遵循依賴倒置原則。

領域層:領域的業務邏輯在領域層實現,包括領域服務、實體、值對象等,甚至還包括DO,這裏需要說明下我自己的觀點,DAO和DO等不屬於基礎設施的實現,只是我們在使用基礎設施時做的一個封裝,應該放在領域層。同時,領域層還會涉及究竟是用充血模型和貧血模型,個人的經驗更傾向於貧血模型,具體的原因參考《5.2 充血還是貧血》。領域層是業務邏輯的內聚,勢必會涉及到DB存儲,緩存提速,文件處理等,對這些基礎設施的依賴遵循依賴倒置原則。

基礎設施層:主要是各類基礎設施,包括DB、Cache、File等,這些基礎設施一般都會提供各自的Client包,領域層基於Client可以完成對基礎設施的調用。

2.縱向每一列

每一列的劃分基於聚合維度,比如還是以銀行爲例,資金是一個聚合維度、貸款是一個聚合維護、賬戶也是一個聚合維度,各自應該有獨立的Adapter層、應用層、領域層、基礎設施層。但是物理結構上有可能是獨立,也可能是在一個應用中。

總結

軟件方法是一套思想或方法論,從業務理解到軟件設計一體化的思想和方法,在思想和方法論被不斷運用和實踐之後,相對應的各種能力會逐步達到熟練狀態(比如業務用例,業務時序,UML使用,領域建模等),自然而然能夠提升工作交付的效率和質量,職業發展和軟件質量會因此受益於軟件方法背後的抽象能力、架構能力、及高效高質量的交付能力得到認可和發展。

每位技術人員應該優先側重思想背後的能力提升,也許1年,也許3年。如果像本文一樣,邊學習邊總結邊輸出,撐死1年,快的話半年基本可以熟練掌握軟件方法的思想,未來再具體實踐完善不足和理解不到位的地方。

參考資料:

潘加宇:《軟件方法-上》

鍾敬:《手把手教你落地DDD》

作者 | 聰安

點擊立即免費試用雲產品 開啓雲上實踐之旅!

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章