實現需求:懶骨頭們趕快拿起獵槍來打獵啊。
近期我在水球軟體學院中的遊戲微服務計畫 (Game as a Service) 讀書會直播分享到:「其實呀,在公司固定預算和時限下,要去實現需求呀,就像是一場快活的打獵活動。」
在向讀書會的夥伴們展開了這項比喻之後,才發現實在是太合理啦!所以,雖然這篇文章選擇以一則打獵的趣味故事作為開頭,大家可以在閱讀故事時,猜猜看實際上類比的是軟體專案活動中的何處,但實際上我想要分享的是我對於近期快速浮上水面的「某軟體設計方法論」的深刻理解。
哇嗚,這麼大一頭怪物,要打嗎?
獵人們見獵心喜,發現一頭鮮肉多汁的獵物,於是便躲在外圍遠處仔細觀察著。(1)
「要打的話,該如何下手才好呀?」獵人們感到緊張、刺激,打獵最可怕的事就是深知「出手的機會可能不多」,要嘛就是在短時間內火力全開讓他斃命,要嘛就要與他死纏爛打。於是獵人們決定先「設計好戰略」(2):觀察著獵物的全貌,如頭部、軀體、四肢、關節⋯⋯等等,不斷分析著獵物的「弱點」。
「聽好了,各位,待會我們分成四組人,A 組攻擊他的背部、B 組攻擊他的頭部、C 組和 D 組則瘋狂攻擊他的腿部四肢。」
擬定了獵殺行動的大方向之後,獵人們繼續盤算著各組之間的協作(3)。「另外,C 組和 D 組是主要火力,所以請 A 組和 B 組當『餌』!如果發生任何意外⋯⋯請 A 組和 B 組往彼此的方向跑,來擾亂這頭怪物所定的攻擊目標,並拉長 CD 組發揮的時間,但千萬別跑向 CD 組的位置啊!」
獵人們找了一塊木板,並在上面刻畫著 ABCD 四組的戰略行動,簡單地以視覺化的方式快速繪製了獵物的模樣,並讓所有組員輪流在獵物形狀上指出自己負責的位置,確定所有人都熟知了這場獵殺行動的各項細節。(4)
接下來呢?
「啊⋯⋯等等,槍枝的數量不夠啊,只有兩組人能配到槍枝,槍枝就給 CD 組吧!」負責攜帶武器的其中一員急著告知著大家武器資源不足的問題。
於是獵人們開始在有限的資源下開始「策劃著戰術」(5):「C 組去拿手槍,一手一支,D 組使用獵槍。嗯⋯⋯ AB 組,你們得使用弓箭啊。你們⋯⋯可以嗎!?」
「可以啦,弓箭的技術訓練可從沒少過。」老練的獵人們拿著十分 Old-School 的弓箭獨自樂著。
然而不免團隊中還是有人吵著要使用 Ak47,還說著如果沒有使用 Ak47 的話,那麼在這場狩獵行動中就學不到新的技術了。
「呀,沒有,我們真的需要 Ak47 嗎?我們沒有錢買那種武器,而且我們也用不上啊,真用上了,用不好走火怎麼辦啊?」
獵人們照著戰略和戰術的設計上分頭行動 — — 「那上吧!」ABCD 組分別到了不同的定點,並且當即開始朝著獵物無間斷地射擊。哎,令人出乎意料之外啊,獵物比想像中還來得強悍許多。
「啊!不是腿部!打他腿部沒用!」C 組成員中的隊長大吼著,並嘗試揮著手指揮 D 組成員與他們會合。
但是,突然其來的攻擊激怒這頭怪物,牠氣勢洶洶,開始往四周尋找著那些該死的侵入者們。可突然間,獵物就像發狂似的朝著一個方向使勁跑去,讓各組成員都看呆了,因為獵物跑向的方位背對著所有人,看起來就像是逃走似的。
眼看獵物逃走,大家大鬆了一口氣。A 組隊長隨即射出一支弓箭命中四組人的中央,並吹著哨子邊跑邊指揮著所有組員往此處集合。
「到底怎麼回事!?為什麼牠沒有倒下?CD 組的獵人們有沒有擊中牠??」A 組隊長怒不可遏地質問著。
「打牠腿部一點用都沒用,我們朝著腿部開了好多槍!牠的腿感覺跟鑽石一樣的硬啊!」CD 組人員搶著喊冤回答。「那麼,為什麼獵物會突然跑走呢?」
「是我們 B 組的隊長,看著獵物都不倒地之後,便在弓箭上掛了一包血袋朝那個方向射去。怪物以為我們在那裡,才會往那裡氣沖沖地跑去啦。」
獵人們喘息且慶幸著他們依然活著。於是找回了剛剛的那片策略戰略用的木板,並將獲得的新的資訊拼湊回這片木板上。
「腿部不是他的要害,但是我們現在已經清楚知道我們能夠透過血袋簡單地誤導這隻怪物的思緒,這能夠幫我們拖延許多時間來分析牠的要害⋯⋯!」
全員在木板上輪流比手畫腳視覺化地表達著自己的職責,快速迭代了額外的戰略和戰術,為即將開始第二次大戰做好準備。
「來吧!我們要來第二波進攻了!一定要來個大豐收!」
回到軟體現場吧!
「我們必須要立即啟動這三項專案,不然對手就要比我們還早進入市場了。而且,我們的資源就只有這麼多。」
軟體方法門派至今已百家爭鳴,但軟體工程師的使命始終是幫助團隊在「如時、如質、如預算」的限制下交付軟體,快、狠、準地實現需求。卻因需求是無形的,就像獵人無法快速定位獵物的致命之處,所以必須面對需求的變動並根據新掌握的資訊快跑一次迭代。獵人們不只要快、狠、準,身子和行動更要敏捷。
在大多數人的專案生活中,「需求」聽起來是讓我們淪為「程式黑手」的焦慮來源。聽到需求這兩個字,就很無奈啊。整天在程式碼中鑽東鑽西,職業情懷到底都跑哪裡去啦?
你得看到獵物,並被獵物看到,你才會感受到活著。
我運氣較好,參與了滿多有趣的軟體專案,之前參與過一項台大的「線上批改系統 (Online Judge System)」的系統專案。由於這個專案十分有趣,在參與的初期我便見獵心喜,對於即將開發的成品感到非常期待。
「這做得起來嗎!?」
「可以吧,試試看。」在專案的早期,我與年輕的團隊成員們花了點時間理解線上批改系統的願景和目的,現行系統的缺陷和業務上的瓶頸,甚至瓶頸強烈到可以說是軟體危機的重現。由於瓶頸之大,於是我們必須先瞭解清楚線上批改系統的各項專業術語、Problem、Submission、TestCase、Judge、Verdict⋯,了解系統最有價值的地方,以及其中次要、輔助的部分有哪些(1)。
在對線上批改系統有了初步的理解之後,接著我們找了幾塊木板,阿不,是一塊超大畫布,與 Shareholders 花了數十分鐘的時間,藉著某種輕便的圖款共同繪製線上批改系統在台大課堂上的操作流程和細項規則 (4),並在視覺化對基礎下指出瓶頸落於何處,然後輪流走查流程確認認知無誤。接著,我們在畫布上簡述了對於軟體架構的想法,雖然物理邊界尚未成形但邏輯邊界逐漸形成,從畫布上能看出線上批改系統的實作重點會落在幾個的區塊 (2),而且能看出這幾個區塊的關係:部分區塊可以平行開發,而部分則有相依關係 (3)。最後,我們針對每一個區塊,展開其中的結構,和決定適當的設計 (5)。而在決定完 Tech Stack 之後,便開著 Tickets 下去幹活去了。
靠著合理的分工,三週後我們確實實作出了初步的 workable solution,能夠解決現有的效能和業務瓶頸。但隨之也接收到了隕石。「Gosh,有隕石,shareholders 希望線上批改系統能夠更厲害些,批改的判決中要計算『程式碼的分支複雜度 』,才能夠電爆那些使用『醜陋暴力解』的小朋友們。」說到底就是要做 Code Quality Static Analysis 啦。這一大需求實在是來得突然,但他確實是線上批改系統最有價值的部分之一,得做!
於是我們立刻回到了畫布上來繼續策劃著一二 (4),在共有的視野上我們定位了新需求會影響的局部 (3):
「只有 Verdict Issuing Policy 會受到影響。那還容易實作的,完全不會破壞任何既有程式。」
「Shareholders 認為這是很偉大的實作 (1),未來也會不斷擴充新的 Code Quality Static Analysis 算法。而且, Code Quality Static Analysis 演算法的研發人員是新加入的成員,對線上批改系統沒有過多的理解,引入這項新的設計要更加考慮開閉原則。來我們寫到畫布上去。」
確定了影響範圍之後,接著進到該範圍對應的結構面上來套用相關設計模式 (5) 來讓線上批改系統的 Verdict Issuing Policy 與 Code Quality Static Analysis 模組之間依賴反轉,促使系統在支援新的 Code Quality Static Analysis時能遵守開閉原則。
接著,配合著新加入的演算法研發成員的合作,我們確定了這個戰略的實行下是能夠平行開發的,也沒有太多整合上的問題。於是在半工半讀的三週內我們成功整合了 Code Quality Static Analysis 機制。
在看見了線上批改系統的願景時,我與 shareholders 都感到十分興奮,就像見獵心喜似的。但是啊,真正讓我們感覺快活的,是當這個獵物發現了我們,並和我們來了好幾次的回合戰:「就憑你們現有這視野、這架構、這野心,一回合內怎麼可能能輕易命中我的要害呢,準備好接招了嗎,線上批改系統的願景你們還得再扛個好幾輪呢。」
一個偉大的系統會不斷發展下去,工程師往往在長期的發展下失去了生命力,但如果工程師們時時刻刻都能看得到全貌,並能快速回到全貌上推演新的戰略和戰術,就像是一場快活的狩獵行動,你攻——牠守;牠攻——你守,獵人們看得越來越清楚,獵物拿我們越來越沒辦法。
對企業而言,到底什麼才是獵物呢?
軟體工程至今發展出了各式各樣的「方法論」,我想來為我的類比做一個完整的總結。
要讓工程師們感到快活,可能,就得像打獵般精彩。是獵物驅動了我們的行為,和每一行程式碼。
那麼到底對於一間企業而言,或是對於一道軟體工程而言,什麼才算得上是獵物呢?這個問題其實相當困難,不同學派有不同說法。在我以個人見解回答這個問題之前我們必須先理解「何謂軟體的價值」。
在軟體出現之前,人們在各個區域中自主生活著,但人們在長期來往之下遇到了各式各樣的問題。這些問題困擾了人們太久啦,於是人們嘗試解決這些問題便組織成了一些團體,還發展出了各式各樣的規則、知識和文化。
不過呀,團體的活動由於皆為人工執行,依然有效率上的嚴重瓶頸。此時便有人提出需求、要求實作軟體來解決這個執行效率上的瓶頸。
軟體的價值便在此時出現,軟體的介入解決了人為活動上效率的瓶頸,解決了領域中的問題。
軟體企業的價值:便是開發產品來解決各個領域中長期發展下的問題。因此對於企業而言,真正讓企業見獵心喜的啊,是存在多道有價值問題的「問題領域」啊!這個獵物——就叫做「問題領域 (Problem Domain)」。
「是問題領域讓企業興奮,是與問題領域完美契合的解決方案領域 (Solution Domain) 讓工程師們興奮。」
如果把問題領域視為獵物,那麼這場期望能夠快、狠、準的狩獵行動,就叫做「領域驅動設計 (Domain-Driven Design, DDD)」。
「比起議論紛紛,最可怕的還是沒人議論啊」。
近幾年來這個方法論越來越受到關注,很開心能夠用我的視角和大家介紹何謂領域驅動設計,來讓這個類比告下一個段落。
領域驅動設計是一個方法論,並不創新、也不偉大、更不該被膜拜。實際上領域驅動設計只想稍微撥亂反正,而不是想灌輸人們「應該照他的話做」。在人們鼓吹使用微服務以及帶出的一拖拉庫堆疊的強烈技術色彩下,領域驅動設計只想多說兩句話:「問題領域和解決方案之間必須要契合,才不會過度設計 (Over-Design)。」「一起看著問題領域做設計其實滿快活的,喔,我是指真的要看到,不是讀死板的文件,是一起看到問題領域。」
那人們鑽研著領域驅動設計,都在鑽研些什麼呢?
領域驅動設計這個方法論提出了「問題領域和解決方案領域之間契合的邏輯」。在這個邏輯上才發展了各式各樣的活動,來指導人們如何設計出能夠緊密對焦於問題領域的程式碼。
那麼領域驅動設計中有哪些活動呢?
大家應該有注意到第一段獵人活動和第二段線上批改系統專案時,我有標註數字在各個環節中吧?以下是每個環節數字對應到領域驅動設計中的活動:
(1) 領域分析 (Domain Analysis) — 分析問題領域的各個面向。
(2)(3) 戰略設計 (Strategic Design) — 對焦問題領域,決定軟體設計切入點。主流以「限界上下文 (Bounded Context)」來表示。每一個限界上下文如同是獵物的一處弱點範圍,獵人們鎖定這些弱點的過程中會先在木板上畫好那個範圍大致上的邊界。
(4) 團隊建模 (Collaborative Modeling) — 團隊成員透過即時協作的方式同步對於領域知識的認知 — 主流有兩種方式:「事件風暴 (Event Storming)」和「領域敘事 (Domain Storytelling)」,如果獵物複雜度太大,還會搭配物件導向分析與設計,或是其他種結構分析/設計法。
(5) 戰術設計 (Tactical Design) — 決定如何實作軟體設計 — 主流方案是套用 Aggregate、Entity、Value Object、Repository、Domain Service 等詞彙構 成的模式語言(Pattern Language)以及能夠保護領域模型的階層架構 (Layered Architecture)。
其中 (1~5) 並不要求做到最完整,各方法都有(也應該要)提出「相較敏捷、演化式」的運行方式和元素,並尋求迭代。而目前 (1~5) 底下的模式不只(也不該只有)一種,每一種模式必有其適用情境 (Context),以及套用成果 (Resulting Context)。模式被提出來,甚至還給模式一個名字,並非是為了刻意包裝已知問題,更不樂見自成一個複雜體系,給予名字是便於人們長期發展、驗證和修正。
「一個沒有名字的東西,人們要怎麼批判啊?」
領域驅動設計的人們僅是期望能夠順著「Problem Domain/Solution Domain Fit」的邏輯進一步提出諸多模式來把這些環節做好而已。至於要如何做到最好?其實答案仍是開放的,領域驅動設計的人們樂於探索和分享打獵的過程,但過程中充斥著大量的經驗法則,我們必須欣賞一次彼此的打獵過程,才能在彼此的獵場中一起覆盤這些精彩的環節和足跡。
這些都是我個人的心得。我只想分享我在軟體圈中多次打獵下的喜悅。我們本不是黑手,也不該只是黑手。而在我的經驗下,讓團隊全體成員「意識到我們正在打獵,還正被獵物看見」讓我的團隊成員們都有了更好的效率,以及更了解程式的發展方向或是改動方向。在 (1~5) 的幫助下,這能更有效地做到。
我個人喜歡領域驅動設計,僅僅只是因為它讓我和我的團隊在打獵的時候有了很明確的主軸和視野,使我們快活了起來。認識領域驅動設計的途徑不該是從某個簡報中,更不該是為了微服務技術鏈的溫床。正相反的,這都不樂見,認識領域驅動設計的最佳途徑應該是直接與我們一起體驗一下打獵的過程。而正因為打獵是有可能會死的,所以我們眼睛必須總緊盯著獵物,希望能將技術收斂到剛好的大小就好,而不過度把玩著手上的武器,會走火的啊。
是的,領域驅動設計有明確的適用場景,那就是當你的「獵物夠強,問題領域夠複雜或是夠龐大」的時候才需要帶著獵人們一同執行領域驅動設計。反之,如果你感覺你接到的專案只是一直在寫 CRUD API、只是一直串東串西、做做純粹的資料報表 ,沒有太多的行為和複雜的流程及知識隱含在其中,那這獵物不怎麼有威脅啦。在這種情況下,雖然套了領域驅動設計依然會有主軸指導和共享視野的成效,但你會覺得很無聊,就像一群快活的高等獵人,快活地策劃著戰略和戰術,但獵殺的只是一些毫無挑戰性的小怪似的。
當年加入領域驅動設計 TW (DDD TW) 社群擔任志工之後,我才發現,啊,原來我對領域驅動設計的認知其實相當受限,只停留在「領域建模和 Aggregate 那些感覺有好多要學好煩的鬼東東」而已。但其實領域驅動設計的重點是在於「該如何讓這群獵人們都快活起來——來一起和獵物搏鬥呢?」。資深的 DDD 獵人常常都會分享著,打獵得做好長期死鬥的準備,我們總期望企業能長期發展,所以獵場其實很大。隨著獵物變得更強大、數量更多——「涉及了多項重要的子領域」,我們往往必須和一群獵物們扭打在一塊,而無法將其一一快速斃殺。因此啊,領域驅動設計中也開始探討著領域及對應軟體架構的演化機制,有更多的著墨是在「問題領域(獵物)」各個面向的分析上,而非僅在於戰術或是程式設計上。
我在領域驅動設計上的了解小於其他在一線戰場上 DDD 前輩們。不過 DDD TW 中的夥伴們大多都有家室要顧,沒什麼時間寫文,所以我就想主動藉著這個機會和大家分享一下我所理解的領域驅動設計囉!
祝福大家國慶日快樂,天氣變冷了,一起當個快活的獵人吧!
換你分享你的想法
最後我想問問大家一個問題,請大家留言分享一下,你覺得一道軟體工程最重要的是什麼?
A. 一個有效驗證的流程
B. 高品質的軟體架構設計
C. 全體成員的 Mindset
D. 遵守著方法論明定的邏輯
E. (請提出你的想法)
— — — — — — — — — — — —
如果喜歡我的文章的話,可以追蹤我個人臉書頁面或是到以下地方鎖定我的活動,目前我是全職在創辦線上軟體社群,我每週都會舉辦各種線上聚會來和大家分享軟體設計學的知識以及和技術演講相關的技巧來讓大家快活起來!:
- 我每週二、四、六都會在我開設的 Discord 社群中直播和大家聊聊天。(連結在留言區):https://pse.is/4j3ppp
- 請加入我的臉書社團,我會首先分享我的文章和活動到這個社團:https://pse.is/4jdrx3
- 如果你和我一樣,懶得讀一堆書,想先耳濡目染之後再深入學習領域驅動設計的夥伴們,可以加入 DDD TW LINE 社群:https://pse.is/4hcnky,以及 DDD TW 臉書群組:https://pse.is/4hud59
為了不打擾大家,平常我少少一次公開分享,我們社群中見了!