您的軟體架構夠敏捷嗎?(二)- 持續演進的軟體架構



這次,於 2019/05/05 受新竹敏捷社群總舵主柯仁傑的邀請,在新竹敏捷社群我分享了一個關於 您的軟體架構夠敏捷嗎?的主題,在這個主題當中,我分享了我個人早期多年在專案開發上的系統設計相關經驗外,與最近這五年執行顧問時,替企業規畫與設計軟體架構相關經驗的分享,過程中,與觀眾有幾個共鳴點待我在下方娓娓道來。

軟體的快速交付≠有價值的交付
課程一開始,我提到了在十多年前,我曾待過一家軟體公司,在這個環境中我們嘗試創造出一個 0 缺陷的軟體架構,詳細的內容我在上一篇文章【敏捷的軟體架構設計:可擴展的軟體架構】裡有說明,這裡不再重述。
接著,我提到了現在也是開發流程+開發方法百花齊放的年代,只是 Agile/DevOps 特別為人所知而已,但在這個混亂的年代分常容易讓人無所適從,尤其時初學者,因為你所處的環境有可能已經追不上大環境中的變化與改變的速度了,但是你卻還是有很多東西要學,對吧?我的意思是,最讓人無所適從的倒不是技術的推層出新,慢慢的反而是到底是改怎麼做才是正確的?這部分我們以下在詳盡說明。
先前我曾聽過某大廠的 App 能夠在一天之內交付 20 ~ 30 次的版本,這對一家軟體公司來說,維運與開發之間必須非常的運作與整合完備情況下,再加上工具的熟悉度極高,才有可能將 CI/CD 持續整合+自動化部暑到如此境界,有許多軟體開發商無不卯足全力渴望達成此境界!但是據我所知,大部分大型購物網站、或者對外的訂票系統,事實上,他們在實務上只需要一天上一個至兩個版本便足夠了!如果您需要在一天之內上 20-30 次版的話,通常代表著您的開發流程可能是出了問題,比如:團隊與 User 可能存在著隔閡/開發人員或小組之間沒有共識+不清楚需求就冒然的修改程式碼… 等等。
我在這邊請大家思考另一個面向問題,就是,軟體公司究竟想要什麼?做專案+產品想要的是什麼?就是【獲利】!對吧?做這麼多,改變這麼多,去了解敏捷是什麼!最終目的,就是要賺錢,但是要賺錢,就得先思考如何【獲利】呢?因為你們在進行系統開發時一定常常遇到下面問題:
  • 測試總是花費許多時間?
  • 軟體的修改總是牽一髮動全身?
  • 無法掌控的技術債?失控的技術債?
  • 需求確定後,才可以開始寫程式?
  • 架構設計完成後?才可以開始寫程式?
對了,看到這裡答案就呼之欲出了,公司要賺錢,就是要將『成本』控制在範圍內,不然怎麼賺錢?

價值(Value) vs. 架構(Architecture)

圖(二)、不良的軟體架構就會像手銬一樣會限制住公司的發展
不過,目前許多大企業內部的現狀是,已經存在著許多高度耦合的遺留系統,而這些系統若又存在著不良的軟體架構的話,那麼,這些系統就會像【手銬】一樣,【限制】住公司的業務發展量,好一點的狀況就是拖慢開發速度+專案賠錢,比較不好的狀況就是甚至會【限制住】公司的發展。

你的軟體架構已經設計好了?
在傳統的瀑布式開發裡面,我們可能很常聽到 SD 跟 PG 說,我的架構已經設計好了,你們可以開始寫程式了!以現今的角度來看這其實有點詭異~ 怎麼說呢?如果您有真的認真地了解過敏捷,或者您有真的實行過敏捷,您應該會發現,什麼叫做好了?好了這件事情很弔詭,為什麼我這樣說?從下面兩個面向來看問題:
  • 要怎麼樣設計一種軟體架構來滿足使用者不斷變動的需求?
  • 如果在設計架構之前,就要了解所有的需求,那豈不是代表要先確定所有的需求?
先來看第 一個,本文的上方也有稍微提到,在軟體開發的現代,我們慢慢理解到了一件事情就是,你不可能完全的凍結需求,也就是不太可能凍結使用者的想法,因為有可能使用者沒想清楚、或者是你沒聽清楚但大方向的需求會不變,但需求的細節部分永遠都可能會變所以現今開發人員面臨的一個考驗就是,怎麼設計出一種架構可以滿足使用者不斷的變動的需求,這部分 Clean Architecture 的插件式軟體架構有可能可以解決這樣的問題,但是也別期望太高, 因為不可能 100%,甚至我們能做到 50% 就偷笑了,這我們留到下方的 #軟體架構如何敏捷 Topic 再來談。
再來第二個,傳統的瀑布式的軟體開發方法總是希望需求能夠確定,甚至希望使用者趕快畫押不要再改了,不管你用一種方式開發方法,若需求能固定對軟體開發來說都是好事,但由於這個機會實在非常的低,所以敏捷將專案切割成一個個小階段來進行(稱作Sprint/衝刺),每一個小階段都是增量的開發+大方向不變+透明化+減少浪費,透過每一個衝刺所需完成的代辦項目(PBI)並且,再透過價值來排立優先順序,因此在每一個小階段裡都可以完整地審視一下目前有什麼做錯的地方?優先順序有什麼需要改變的?什麼重要?什麼不重要?也就是說,在每一個小階段裡都是完整的系統分析=>設計=>撰寫程式=>測試=>上版 的完整流程,但是這裡我個人會比較建議採 TDD/ATDD 流程來進行。因為 TDD/ATDD 非常適合進行迭代的開發、尤其是敏捷,前一篇文章 敏捷的軟體架構設計:可擴展的軟體架構 中 也說明了『測試是迭代開發的最佳保護網』,因此 TDD/ATDD 剛好補足這一塊。
記得先前也在一篇文章『您的軟體架構夠敏捷嗎?』也有提到,敏捷與 TDD 之中,雖然不建議一開始就進行大量的分析,但是不代表完全不分析,我們期望實作一個可抽換的架構,而這個架構有可能都只是個 interface 定義而已,而且都還未實作。

軟體架構如何敏捷?

圖(三)、內聚性原則張力圖
OK!那麼,這個時候,其時才真正進入我們本篇的主題,到底?怎麼樣的軟體架構才能被稱作敏捷的軟體架構呢?我相信有些讀者應該也發現了,就是要確保我們目前所做出的是【正確】的軟體,什麼叫做正確的軟體?就是使用者需要的軟體,許多需求分析的工具像是五卡法、User Story Mapping 都可以幫助我們分析需求,但是要真正的釐清需求必須與使用者密切合作才會讓事情更順利許多,因為在敏捷理將業務與客戶均是團隊的成員,這樣做可以確保業務和研發一致加上有效率的敏捷。
然而,架構設計也是一樣,當軟體開發切割成一個個的小階段的時候,你除了必須以【價值】導向的方式來思考並排列在這個衝刺內每一個 Product Backlog,所需要的適應性(演進的/剛好夠用)的軟體架構應該是怎麼樣的?因為在敏捷裡,並沒有『架構師』這角色,因為在敏捷裡面,是由團隊來負責架構,所以我先前才說,敏捷是有一定門檻的。
由於,我們在每一個衝刺裡面,完成一個剛好夠用的架構,常見的設計技巧就是透過 interface 實作可抽換 (Inversion Of Control, IoC) 都是物件導向五大設計模式 SOLID 裡面的概念而已,我們利用介面 interface 來降低彼此的依賴,也利用介面來達到一定的可抽換性,我們希望軟體架構可以含括住需求,但是我們又不希望花太多時間在沒有價值的(功能/方法)上面,所以對外部,我們可能都只有先設計 interface 或 abstract class 而已,如下圖:

圖(四)、【軟體架構】&【使用者需求】的擴張

前面講了這麼多,下面我們來看一個實際的例子。

實務案例:
一個 ATM 自動櫃員機的需求
需求描述:
身為貴銀行的顧客,我可以在各分行的行庫的提款機提取現金。
我只需要插入提款卡,輸入密碼進行身分驗證後,就可以提領現金。

這個需求的 Use Case 分析如下:

Use Case 可當作我們在這裡進行 Domain Modeling 分析時的進入點,如果您使用 DDD (Domain Driven Development) 開發方式時,也可從這裡做為分析的起點。但這邊我嘗試以 OODA 分析來驅動 Domain Modeling 的分析,當我們可以進入 TDD 開發方式時,表示我必須找出我的 Domain Model 與 Target 的 Class,這個 Class 怎麼來的?當然是分析來的,絕對不是隨便找一個,然後事後不斷的 Refactor 系統就會是使用者所想要的系統。
這邊,我們簡單的用五卡法進行分析,因為需求如上方的需求描述裡面所提的,加上 Use Case 分析也完成了,這邊我使用 CARD Board 線上的 User Story Mapping 服務來繪製 User Story Mapping。

有了 User Story 接著團隊就有 Release 的項目(包括:Features/Functional),喔!對了,前面忘了提到,也因為敏捷的思想幾乎改變了軟體開發對於【交付階段】的看法,這個概念也來自【建構微服務】這一本書,系統一開始的建置應該就要考量到【部署】這件事情會更全面,服務本身的(顆粒度/細粒化)也都與容不容易擴展有著密切關係,軟體開發做到最後你會發現,每樣都是相輔相成 + 一環扣一環,一樣都少不了…阿~ 扯遠了…. 我們回頭過來看 User Story,經由 User Story 與 Use Case Diagram 分析可以得到下面的 Domain Conceptual Modeling 概念圖。

註:上方圖形所使用的技巧為 UML 當中透過 Use Case 加上基本的名詞分析法找出的領域模型(Domain Class Diagram),詳細實行方式可參考筆者以前的文章:Visual Studio 2010 塑模化應用程式分析(六)
不過目前的 Domain Conceptual Modeling 還非常不完整對吧?這表示我們 Catch 到的內容無法滿足一個概念的(完整性/可用性),這我在【決戰 OOAD 系列課程(線上課程)】中有說明過。那怎麼辦呢?應該與 Product Owner 討論,釐清,這裡我們假設擁有提款卡的客戶應該是有開戶的顧客,有開戶的顧客,才可以在這個銀行中提領現金(先不考慮跨行這件事),所以有開戶也代表有帳戶,這邊我用【存戶】來表示。

所以,這個時候,我們的 Domain Conceptual Modeling 會更趨於完整。

這裡,我們要先產生可以測試的 Test Case,這裡須來自顧客可以提領現金這件事,而這件事也衍伸並分析出包含了【插入提款卡】、【輸入密碼】、【提領現金】這三件事,輸入密碼執行的是『身分驗證』,稍後補上 Sequences Diagram。
在來,我們有顧客,有帳戶、有存戶、顧客必須先行登入+驗證後,方可提領現金,這些都弄清楚了之後,我們就可以分析並產出下面的 Class Diagram 了。

待續… 實在太長,寫不完 XDDD
下一次,我會將上述的 (interface /領域物件)使用 Sequence Diagram 進行分析後,再使用 EA 的 Generate Code 將介面產生到 Visual Studio 2019 環境之中,實作 Target Class 後,就可以進入 TDD 撰寫紅燈測試了。



關於 Gelis:

資深 .NET 技術顧問

FB 社團 (軟體開發之路):

https://www.facebook.com/groups/361804473860062/

FB 粉絲團 (Gelis 的程式設計訓練營):

https://www.facebook.com/gelis.dev.learning/

我講授過的課程 SlideShare:

https://www.slideshare.net/GelisWu


以下是我經營的項目與內容:

(1). 企業內訓課程

(2). 專業顧問


企業內訓課程:

1. .NET Core 3.1 從入門到進階

先前實體課程連結

2. 跨平台的 Web API Framework 框架設計

先前實體課程連結

3. 決戰 OOAD 系列課程 - 使用 UML

先前實體課程連結

4. 單元測試 UnitTest 與 Moq 物件實務課程

先前實體課程連結

5. 快速開發系列 - C# Project Templates 範本設計

留言

這個網誌中的熱門文章

軟體工程師 - 成長的 10 個階段

常見的程式碼壞味道(Code Smell or Bad Smell)

什麼是 gRPC ?