如何用 DDD 結合 TDD 的思想「分治」複雜問題?

PS:理論上,我應該在上個月 “交付” 這篇文章,自覺得有一些論據不夠強有力。但是,因為疫情的原因,我離我的書架很遠(電子書不方便翻閱)。所以回到杭州,搬完家後,我便繼續補充這篇文章剩下的部分。

軟件開發是一項複雜的集體活動,它涉及到一系列的行為和藝術,如項目管理、流程管控、知識轉化、程序員心理學(狗頭)等。從個體出發時,這些都是一些無關緊要的因素。作為一個 “螺絲釘”,我們所關心的是:如何去解決問題?當然了,我們把組織視為一個個體時,我們也只關心:如何去解決問題?

本文是我在寫 Chapi 過程中的一個感悟與過程的思考,因為它具有一定的代表性,所以記錄一下供大家參考。

TL;DR 版:沒有,建議閱讀全文。

如何用 DDD 结合 TDD 的思想「分治」复杂问题?

問題的模式

通用問題的模式

所以,為了解決『如何解決問題』這個問題,我們開始嘗試各種各樣的解決方案,如 Cynefin 框架,還有 DDD(領域驅動設計),BDD(行為驅動開發),TDD(測試驅動開發)等等。我們所做的事情是尋找合理的原則與模式,以更好地解決問題,並記錄這些實踐,比如此文。

在一個團隊、組織中,往往會強調文檔和紀律的重要性 —— 你按文檔上的過程來,不會出錯(出錯了,也不怪你)。因為前人總結了大量的經驗,記錄在案,告訴了你:遇到這樣的問題,你應該這麼做。順便一提,這種做法各有利弊,不好的一個方面是:缺乏靈活性;缺少解決問題的能力。

回到,我們的元問題上,如何解決『如何解決問題』這個問題?

這個時候,我們可以嘗試使用第一個模式,使用 Cynefin 框架。於是乎:

  1. 簡單的問題,尋找最佳實踐。

  2. 繁雜的問題,尋找最好的實踐。

  3. 複雜的問題,探索、嘗試,如轉換為繁雜問題。

  4. 混沌,嘗試採取行動,轉向複雜問題。

  5. 失序。

最後,我們的問題就落到了:如何將複雜問題拆解到人能處理的範圍?換句話來說,作為一個資深的管理者,又著程序員、Tech Lead,我們要做的事情就是將複雜問題拆解,並交給合適的人解決。順便一提,合適這一點很難,因為合格的領導者階除了要解決問題,還要考慮人的成長。

軟件開發中解決問題的模式

回到軟件開發的問題上來,在這個一領域,我們往往面對的都是複雜問題。除非,你遇到的問題是,你有一個億但是你不知道做點什麼,你卻想做點什麼,這個看上去就像是一個混沌的問題。總而言之,我們要解決的都是複雜問題,於是我們可以尋找一些合適的現成模式:

  1. 使用 DDD,將複雜問題轉化為繁雜問題

  2. 使用 TDD,將繁雜問題轉化為簡單問題

  3. 在簡單問題中,使用最佳實踐

嗯,聽上去就是這麼簡單。

將複雜問題轉化繁雜問題:DDD

在軟件開發這個行業裡,人們已經總結了大量的模式和解決問題的手法。只是呢,因為你一直在加班 + 生活,可能沒有時間去了解一些潛在的解決方案。於是,我們可以嘗試的第一個方案就是使用 DDD(領域驅動設計)的方式來解決複雜的問題。

於是,我們開始了旅程:

  1. 提煉問題域

  2. 找到問題域中的第一個核心子域

  3. 對第一個核心子域進行戰略設計

  4. 完善統一語言

  5. 視情況重複 2 ~ 5

過程中,我們所做的是聚焦、聚焦、再聚焦,解決核心的高價值問題,再逐一突破。

將繁雜問題轉化簡單問題:TDD

在我們分而擊之之後,我們就回到一個小的問題上面,開始我們的編碼。現在,我們就開始了我們的代碼之旅。在不考慮一些技術細節的問題之後,我們的過程變成了:

  1. 拆解問題(Tasking)

  2. 紅-綠-重構代碼

  3. 重構模型以加深理解

步驟,我們很熟悉,那麼怎麼拆解問題呢?這個問題,又變成了一個複雜問題,我們需要識別出大部分的場景,而後針對每一個場景編寫獨立的測試。換句話來說,我們要重新嘗試合適的切入點,而後再逐一解決問題,然後我們會形成最佳實戰。

如何用 DDD 结合 TDD 的思想「分治」复杂问题?

聚焦:核心域

如果把軟件開發想象成是一場軍事活動,那麼核心域相當於是敵方(問題)的主力。我們所要做的就是,選擇一個合適的方式來擊潰敵軍,以少勝多是我們一直追求的,但是沒有銀彈。所以,我們需要把關注力放置在核心域上面。

不同核心域

組織架構往往是金字塔式的層級架構。儘管,我們已經達到了組織所認為的關鍵核心所在。但是事實上,在這一個系統中,每個人所關心的核心要素都是有所不同的:

  • 組織統一核心域

  • 技術部分核心域

  • 代碼中的核心域

  • 個人的核心域

每個人對於統一目標的想法,會影響整個的行動。所以,組織中往往會強調個人興趣與組織目標的一種對齊。這樣一來,才能真正有效地在核心域實施行動。所以,最後就會落到人的問題上。畢竟,每個團隊都需要一些強有力的個人才能保障。

適應核心域的變化

核心域並非一成不變的,它會隨著業務中心的轉移發生遷移。因為核心域會隨著市場門檻的降低,慢慢變成通用域。

也因此,我們需要尋找新的核心域。而原先我們的支撐域,可能因為種種原因,轉變為核心域。一個有意思的故事是牛仔褲的故事,原生它只是淘金者 Levi Strauss 的支撐域,核心域是淘金,隨後它湊成變成了 Levi's 的核心域。不同公司、不同項目的核心域往往有可能相同的。

重新聚焦核心域

這一點,實現上是對於個人而言。儘管你是一個系統的核心部分,但是你做的事情,是不是依舊核心,這就是一個非常有意思的問題了。因為隨著焦點的轉移,你做的事情可能對於你來說價值不大。

如何用 DDD 结合 TDD 的思想「分治」复杂问题?

拆解:驅動開發

我們已經有太多的關於驅動開發的模式:

  • 測試驅動開發

  • 行為驅動開發

  • 驗收驅動開發

它們以不同的視角來嘗試拆解一個複雜的問題,從一個用戶故事的驗收開始,考慮一個個的用戶場景,再落到某一個函數的實現。隨後,通過這一個個的用例完善促成整個用戶故事的完整性,以實現代碼的價值。

分解任務,分離關注點

標題即內容。

從無序分解到有序實施

開始的時候,你並不是一帆風順的。嘗試一個新技術、框架的時候,我們總是一點點領悟出來的,所以我們會從一個大方向正確,但是小小的走彎,直到正確的抽象出特定的模式。以 Chapi 語法解析作為示例,嘗試了不同的方案之後,最後出現了一個統一的步驟和模式,如:

  1. 解析依賴

  2. 解析 data struct

  3. 解析 function name

  4. ……

這樣一來,當我們遇到不同的語言時,我們都可以嘗試這樣的 solution。不過解決方案並不是通用的,還會遇到一些特殊的情況。

如何用 DDD 结合 TDD 的思想「分治」复杂问题?

模式:持續改進

沒有什麼設計可以一次設計到位。舉個最簡單的例子,在我畢業的這 6 年裡,我搬過 5 次家,住過 10 年或者是 20 年前設計的老房子,我經常看到一些設計不足:

  • 20 年前的房子,房間的插座不夠,客廳大到離譜,房間很小。

  • 10 年前的房子,房間的插座夠了,但是它在每個房間有個電視,還有網線。而我們用路由器和電腦(含平板)。

  • ……

  • 1 年前設計的房子,可能考慮不到在家辦公的需求,所以疫情期間要買各種設備。

因為種種原因,比如家有老小,所以你可能像我一個同事一樣在廚房辦公,它又大又安靜。

所以啊,你設計的系統能滿足未來一年的變化,已經非常了不起了。我們能所做的就是預留空間、引入創新文化,以便持續改進。

如何用 DDD 结合 TDD 的思想「分治」复杂问题?

完善:領域模型重構

慢慢地,當我們接入越來越多的需求時,會發現模型會發現一些變化,所以我們需要一些重構才能支撐起新的業務場景。如在 Chapi 中,我們遇到的第一個挑戰是,有的語言它是基於函數的,如 Go等,而有的語言是基於類的,如 Java。

所以,我們需要對模型進行重構以及設計改進。

模式:演進的統一語言

如上。

如何用 DDD 结合 TDD 的思想「分治」复杂问题?

回顧:缺失的地方

回顧,設計中缺失的地方,以便下次吸取一些經典(當然了,不要過度地預先設計):

  • 模型的適用性。在設計的過程中,我假定了不同的編程語言使用的是同一個模型,但是模型缺少邊界。

  • 如何從代碼中顯現概念?畢竟代碼上可能只有一個字段,一行註釋。我所應對的一種方式是測試、查看調用方,還有知識共享的方式。

好了,這篇文章又寫得太長了。

如何用 DDD 结合 TDD 的思想「分治」复杂问题?

結論

事實上,你並不需要上述的驅動開發方式,你所需要的是:如何有序地解決一系列的複雜問題?

同時,你所歸納出來的這個方法,可以被快速地大規模複製。一個啟發的文章是《驅動方法不能改變任何事情》,如文章所說,你需要創造出吸引人的基因(朗朗上口):

<table><thead>框架它的承諾吸引人的文化基因/<thead><tbody>TDD你的產品將幾乎沒有可見的 bug,同時除了必須的代碼外,不會生產過多的代碼。紅 - 綠 - 重構,單元測試BDD你將 TDD 與功能需求關聯起來Given, When, ThenDDD應用的架構完全反映現實業務,因此後續需求的實現將非常自然;沒有變通方法,沒有秘密路徑,只有純粹的、不受影響的以及獨立的模型。組成部分(Building Blocks),無歧義的語言(Unambiguous Language)和策略設計(Strategic Design)/<tbody>/<table>

順帶吐槽一句,從這個基因來看,DDD 有點複雜。

如果沒有的話,那麼使用別人總結的方法,是一種更省事的方式。


分享到:


相關文章: