閒談軟體設計:友善的距離

閱讀時間約 10 分鐘
https://en.pandaid.jp/hygiene/social-distancing

https://en.pandaid.jp/hygiene/social-distancing

心態轉變

因為組織調整,我又變成 architect 了 (2016 ~ 2017),早期對 architect 有很多憧憬,面對問題,心中總有一張設計圖,但實際當 architect 後,發現這是一個很不容易的職位,特別是要能說服別人使用某個設計或不用某個設計,另外,是在有時程壓力下,對一個有點歪的架構上,如何微妙地讓它保持像比薩斜塔般不至於垮掉,還能持續成長,又是另一個難題。

在讀《建構微服務》有段內容讓我玩味很久:

我們借用其他行業的名稱,稱自己為 software engineer 或 architect,但我們不是,對吧?architect 與 engineer 的嚴謹性和紀律性是我們在現實中無法冀求的,而且他們對社會的重要性是眾所周知的。記得有一次我和一位朋友聊天,就在他成為合格 architect 的前一天,他說:『明天,如果我在酒吧裡建議你如何建立某個軟體,而它是錯誤的,我就得承擔責任,我可能會被告,因為,我在法律上是一個合格的 architect,如果我犯錯,就必須負責』

怎樣才是稱職的軟體架構師呢?老實說,我恐怕還沒那資格說。

設計抉擇

軟體架構有很多 pattern,坊間也有很多書探討,像是《Pattern-Oriented Software Architecture》系列、《Software Architecture in Practice》、《Patterns of Enterprise Application Architecture》,但當我們套了這些 pattern 時,我們真的該用嗎?用對了嗎?還是只有有個殼而已,內容根本不是那麼一回事呢?

若深究每個 pattern 的形式,都會找到情境 (context)、遭遇的問題 (forces) 和對應的平衡解 (solution),但再仔細想想,這些解都圍繞在 separation of concerns 上,將不同的問題分離,以合適的方式處理,因為我們總是希望有個 high cohesion 及 loose coupling 的系統,但在面對實際的設計抉擇時,有時反而會做出違背上述兩原則的選擇,因為每個專案要考慮的因素都不同 (參考閒談軟體設計:設計抉擇的因素)。

例如,為了不重新造輪子,我們使用第三方的函式庫,這聽起來很合理,但我們真的了解我們引入的函式庫嗎?我們對該函式庫的掌握度有多高呢?每個被引入的函式庫意味著一種 coupling,不論是在 Java 上使用 Maven 或 Gradle 或是在 Objective C 中使用 CocoaPods,在編譯時,會看到這些套件管理工具幫我們下載眾多的第三方函式庫,這意味著我們不用重寫這些東西,開發效率能提升數倍甚至數百倍,但我們真的都能掌握這些 coupling 嗎?當這其中任何一個環節出錯,我們的系統架構真的很優雅地應付嗎?

這是為什麼我蠻喜歡 Onion Architecture (洋蔥架構) 和 Hexagonal Architecture (六角架構) 的原因了,在過去的專案中,我並沒有刻意使用這二個架構,畢竟我進去時,早已有龐大的程式碼基礎,不可能說改就改,只有在 Android 專案起始時,因為我是較資深的軟體工程師 (那時還不是架構師),主導整個架構走向 (參考閒談軟體設計:Android App Architecture),即便如此,我也只堅持 domain 要與 Android SDK 分離,維持使用而不相依的關係,而這也造就了後來開發PC 版時,有完整的 domain 核心可以直接使用不需修改,雖然這也不在當初的規劃就是了,但也省去了大量重複開發的時間。當要離職準備交接時,回頭檢視架構,上述二個架構的影子就穿插在程式碼之間了。

抽象滲漏

可能跟過去在學校的 OOAD 訓練養成有關,從 problem context 和 use case 中提取名詞,接著提取動詞找出關係與函式,然後利用 GRASP 逐步建構出整個 domain model 與 design model,這中間,完全沒思考過 UI (但如何與系統互動很重要) 與框架,可能是這樣,我後來在做設計時,很自然地就與框架保持距離,不論是用 C# 寫 Windows Forms 還是用 Java 寫 Web applications 。但這其實並不容易,我剛開始工作時,看《Hibernate in Action》時,有幾句話讓我印象深刻,第一句是:

We use transparent to mean a complete separation of concerns between the persistent classes of the domain model and the persistence logic itself, where the persistent classes are unaware of — and have no dependency to — the persistence mechanism.

其實,我畢業前就自學了 JPA,之後才學 Hibernate,上面那句話讓我開始思考,雖說 JPA 的 annotation 很方便,但不正是破壞了 transparency 嗎?但 Hibernate 也無法達到完全的 transparent:

We regard transparency as required. In fact, transparent persistence should be one of the primary goals of any ORM solution. However, no automated persistence solution is completely transparent: Every automated persistence layer, including Hibernate, imposes some requirements on the persistent classes.

因為再怎麼樣抽象化,資料庫對資料的描述與物件導向語言對物件的描述,存在著無法消除的 paradigm mismatch (有興趣可以找書來看,《Hibernate in Action》在第一章的第二節,花了整整一節說明這不匹配的情況),這讓我想起《約耳趣談軟體》第 26 章抽象滲漏法則中的一段:

所有重大的抽象機制在某種程式上都是有漏洞的。

而且有時候這些框架或工具會反過來影響 domain model 的設計,舉例來說,從 OO 設計的角度來看關係,若要好維護,一般會以單向關係 (unidirectional reference) 為主,但若要使用 ORM 或 CoreData 工具,為了確保工具能檢查資料完整性,會反過來在 domain model 上加上雙向關係 (bidirectional reference),但程式碼卻不見得需要去維護這雙向關係 (部分是 ORM 工具處理),這導致讀程式碼時會有點奇怪。

另一個例子是,過去在學 RDBMS 時,會學到正規化 (normalization)、主鍵 (primary key)、外來鍵 (foreign key)、索引 (index) 及一些 RDBMS 能在資料完整性幫上忙的工具,像是 cascade delete 等東西,因此 ORM 工具也常把這些資訊滲漏出來,滲漏也許還好,但把物件關聯的維護轉交給 ORM 工具上 (依賴 cascade delete 刪除不該存在的關聯),就是值得討論的設計,到底這物件間關聯的維護是商業邏輯層的責任,還是資料儲存層的責任?如果哪天,資料儲存層換了,偏偏不支援原有的 metadata (例如不支援 JPA 的annotation),那物件關聯的維護該怎麼處理?

保持友善的距離

所以重點是如何取得平衡?以上述的 ORM 工具來說,極端的兩邊:完全不使用 ORM 工具和毫不顧忌的讓 ORM 工具散布在 domain model 中,又或者是將 ORM 的滲透透過其他方式控管在特定的範圍中,例如:再建立一層抽象 (DAO 或 Repository),在實作中建立 ORM 所需的 data model;又或者是使用污染性較低的方案,例如:以傳統的 XML 取代 JPA annotation 描述 metadata,事實上,這沒有標準答案,每個軟體架構師的選擇都不同,上述二兩種折衷方案我都用過,也曾經完全不使用 ORM 工具,完全視情境而定,但原則到沒什麼不同,與框架及工具保持友善的距離,這同樣影響我之後在開發 iOS 時使用 CoreData 的方式。

或許是這樣,感覺自己比較像是 old-school 的軟體架構師,在選擇第三方函式庫或是框架時,相對比較保守,有時,還會為組織內部重新打造輪子,像是曾經在 Android 專案中復刻 iOS SDK 的 NSNotificationCenter,事實上,在 GitHub 上可以找到類似的第三方函式庫,像是 EventBus,但要不要採用一個第三方函式庫,除了該函式庫穩不穩定、文件夠不夠充足,還要看是否符合專案與組織的特性。

就專案來說,Android 專案分成兩個子專案:一個是只有 domain model 的 pure Java 專案,另一個是實際 Android UI 的專案,若要導入 EventBus,就只能在 Android UI 的專案中使用,因為 domain model 沒有相依 EventBus 所需的 Android SDK,這樣並無法滿足當初想用 NotificationCenter 減少 model 與 UI 之間 coupling 的初衷;另外,考量到希望 iOS developer 也能協助開發 Android,所當時以決定自己動手寫,並且在進行在復刻時,API 命名特意維持與 NSNotificationCenter 相似。

心得

因此,軟體架構不是一旦決定了就穩固了,它需要後續開發時,時時想著當下這個設計是否會把架構搞歪了,架構需要細心的照顧,否則很容易歪掉,歪掉也許沒事,程式也可能還能繼續正常執行,但埋伏在裡面的技術債,何時會引爆則是未知,一旦反撲,對專案的影響不僅是時程,還有開發團隊對整體架構的信心。

52會員
102Content count
這是從 Medium 開始的一個專題,主要是想用輕鬆閒談的方式,分享這幾年軟體開發的心得,原本比較侷限於軟體架構,但這幾年的文章不僅限於架構,也聊不少流程相關的心得,所以趁換平台,順勢換成閒談軟體設計。
留言0
查看全部
發表第一個留言支持創作者!
Spirit的沙龍 的其他內容
Offline first 的設計最近有越來越多的感覺,但好的 Offline first 設計要解決蠻多的問題,是否使用 offline first 設計真的需要好好思考,不然可能得不到好處,反而還引起一堆 bug,本篇先探討在 client 端可能會遇到的問題與一些可能的解法。
會後,我與其中一位創辦人聊聊他們 scrum 怎麼跑,以及程式、美術與企劃,這三種技能差異甚大的成員怎麼合作,他也苦笑,其實他們也花了很多時間磨合,但我們都提到,要引導團隊需要的不是 process,而是很多的軟技能,讓團隊自己能夠成長。
老實說,從中文書名無法聯想回原文書是《The Elements of Scrum》,雖然書名翻譯沒有太離譜(和內容無關之類的),但總覺得貼近原意會好一點。『Scrum團隊週記』這一章,整個讀完,其實就差不多可以了解Scrum的大部分,所以,若要讀這本書,又沒有太多時間,就先看這一章吧!
Immutable interface 讓封裝更有彈性,不用擔心 setter 的過度開放。當不希望物件被不允許的對象修改時,只需讓對方取得 getter 的介面即可,反之,讓能夠允許修改的對象取得有 setter 的物件即可。
創業團隊就會很在意story的內容,會有相當多的意見,refinement meeting就是一個很好的場合讓大家把對需求的想法提出來,否則讓成員失去參與感,這對創業團隊是很大的傷害。
書中畫了滿滿的筆記,不過我想每個人看這本書的想法應該都不會一樣,像是創業不一定需要創投,創投的目標是將獲得的股票回收最大化,和您的創業目標不見得一致,沒有創投可以取得最大的自主權。用既有的利潤來推動成長,而不是預期的利潤來推動成長。
Offline first 的設計最近有越來越多的感覺,但好的 Offline first 設計要解決蠻多的問題,是否使用 offline first 設計真的需要好好思考,不然可能得不到好處,反而還引起一堆 bug,本篇先探討在 client 端可能會遇到的問題與一些可能的解法。
會後,我與其中一位創辦人聊聊他們 scrum 怎麼跑,以及程式、美術與企劃,這三種技能差異甚大的成員怎麼合作,他也苦笑,其實他們也花了很多時間磨合,但我們都提到,要引導團隊需要的不是 process,而是很多的軟技能,讓團隊自己能夠成長。
老實說,從中文書名無法聯想回原文書是《The Elements of Scrum》,雖然書名翻譯沒有太離譜(和內容無關之類的),但總覺得貼近原意會好一點。『Scrum團隊週記』這一章,整個讀完,其實就差不多可以了解Scrum的大部分,所以,若要讀這本書,又沒有太多時間,就先看這一章吧!
Immutable interface 讓封裝更有彈性,不用擔心 setter 的過度開放。當不希望物件被不允許的對象修改時,只需讓對方取得 getter 的介面即可,反之,讓能夠允許修改的對象取得有 setter 的物件即可。
創業團隊就會很在意story的內容,會有相當多的意見,refinement meeting就是一個很好的場合讓大家把對需求的想法提出來,否則讓成員失去參與感,這對創業團隊是很大的傷害。
書中畫了滿滿的筆記,不過我想每個人看這本書的想法應該都不會一樣,像是創業不一定需要創投,創投的目標是將獲得的股票回收最大化,和您的創業目標不見得一致,沒有創投可以取得最大的自主權。用既有的利潤來推動成長,而不是預期的利潤來推動成長。
你可能也想看
Thumbnail
重點摘要: 1.9 月降息 2 碼、進一步暗示年內還有 50 bp 降息 2.SEP 上修失業率預期,但快速的降息速率將有助失業率觸頂 3.未來幾個月經濟數據將繼續轉弱,經濟復甦的時點或是 1Q25 季底附近
Thumbnail
近期的「貼文發佈流程 & 版型大更新」功能大家使用了嗎? 新版式整體視覺上「更加凸顯圖片」,為了搭配這次的更新,我們推出首次貼文策展 ❤️ 使用貼文功能並完成這次的指定任務,還有機會獲得富士即可拍,讓你的美好回憶都可以用即可拍珍藏!
Thumbnail
這篇文章探討了在軟體開發中的技術債可能來自哪些原因,以及如何自動化偵測與修復技術債。作者透過分享不同情境下的技術債選擇,提供了對於技術債的思考與建議,針對開發人員在需要做出無奈的技術決策時,提供了一些建議。此外,還提供了一些在做出技術決策時的方法,如保留抽象層和避免vendor lock-in。
Thumbnail
今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,Database Driven Design 不是不好,只是你的模型容易變成貧血模型,邏輯都集中在 service 層等等。
Thumbnail
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
起源是當時 Facebook 有篇文章討論不少人分不清楚上述二者的差別,當時寫了首部曲《閒談軟體設計:API Naming Style》,接著是《閒談軟體設計:內部函式庫》,但始終沒談到 library 和 framework 的差別,主要是沒有好的例子,這次這例子還蠻不錯的。
Thumbnail
我自己偏好用 Repository 搭配 decorator 來管理 cache,而不是在 controller 層或是到處都有快取的邏輯,如果程式都是透過 Repository 更新資料,Repository 就會是一個不錯的地方更新快取,邏輯也就不會散亂在各處了。
Thumbnail
  關於之前現實中以及其他平臺,有朋友詢問過我是如何寫文,或是開玩笑讓我教人寫文章之類的話,讓我想寫這一篇創作的過程,提供參考,不過我個人並不大建議使用我的方式,因為使用我的部分方法可能對一些人會有些副作用,並且我認為寫作還是要照著自己慣用的步調才是最自然的方式。   也許我的方式跟一部份的人不太一
Thumbnail
  關於自己筆名的由來,因為自己不是個擅長口語表達言詞的人,說話時總感覺自己頗詞窮;又因聽聞古人取名,會有取一些相反含意的名字,以期許往後可以免除壞事。故將自己名字的唸音取作「詞窮」,以期未來在創作的路上,文字能夠多變靈活,不要像口語表達的一般糟糕。
Thumbnail
《耳環》在短短的39頁內描繪思念出海失蹤的哥哥的少年良田,與將耳環丟入海中的少女あやこ兩人短暫相遇的故事。或許只有那個年齡、那個晚上、這樣的兩個人,才能達成的一段奇妙旅程。原本完整的ㄧ副耳環就此分別穿戴在彼此身上,一段共享的秘密。那個夏天的晚上,他們都因此成長了一點點。
Thumbnail
......戰鬥力跟不上版本的角色只能在雪藏跟情懷牌之間擇一嗎?不是這樣的,要塑造低戰力角色有的是辦法,而且解套的辦法究竟在哪,原作已經給出答案......
Thumbnail
......究其根本動畫版與漫畫版的癥結點是相同的,也就是配角人數過多使得情節處理難以做到盡善盡美,其中又以漫畫版的問題更為明顯......
Thumbnail
重點摘要: 1.9 月降息 2 碼、進一步暗示年內還有 50 bp 降息 2.SEP 上修失業率預期,但快速的降息速率將有助失業率觸頂 3.未來幾個月經濟數據將繼續轉弱,經濟復甦的時點或是 1Q25 季底附近
Thumbnail
近期的「貼文發佈流程 & 版型大更新」功能大家使用了嗎? 新版式整體視覺上「更加凸顯圖片」,為了搭配這次的更新,我們推出首次貼文策展 ❤️ 使用貼文功能並完成這次的指定任務,還有機會獲得富士即可拍,讓你的美好回憶都可以用即可拍珍藏!
Thumbnail
這篇文章探討了在軟體開發中的技術債可能來自哪些原因,以及如何自動化偵測與修復技術債。作者透過分享不同情境下的技術債選擇,提供了對於技術債的思考與建議,針對開發人員在需要做出無奈的技術決策時,提供了一些建議。此外,還提供了一些在做出技術決策時的方法,如保留抽象層和避免vendor lock-in。
Thumbnail
今天來聊個最近很夯的主題 DDD,但不是 DDD 的本尊 Domain Driven Design,而是無所不在的 Database Driven Design,Database Driven Design 不是不好,只是你的模型容易變成貧血模型,邏輯都集中在 service 層等等。
Thumbnail
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
起源是當時 Facebook 有篇文章討論不少人分不清楚上述二者的差別,當時寫了首部曲《閒談軟體設計:API Naming Style》,接著是《閒談軟體設計:內部函式庫》,但始終沒談到 library 和 framework 的差別,主要是沒有好的例子,這次這例子還蠻不錯的。
Thumbnail
我自己偏好用 Repository 搭配 decorator 來管理 cache,而不是在 controller 層或是到處都有快取的邏輯,如果程式都是透過 Repository 更新資料,Repository 就會是一個不錯的地方更新快取,邏輯也就不會散亂在各處了。
Thumbnail
  關於之前現實中以及其他平臺,有朋友詢問過我是如何寫文,或是開玩笑讓我教人寫文章之類的話,讓我想寫這一篇創作的過程,提供參考,不過我個人並不大建議使用我的方式,因為使用我的部分方法可能對一些人會有些副作用,並且我認為寫作還是要照著自己慣用的步調才是最自然的方式。   也許我的方式跟一部份的人不太一
Thumbnail
  關於自己筆名的由來,因為自己不是個擅長口語表達言詞的人,說話時總感覺自己頗詞窮;又因聽聞古人取名,會有取一些相反含意的名字,以期許往後可以免除壞事。故將自己名字的唸音取作「詞窮」,以期未來在創作的路上,文字能夠多變靈活,不要像口語表達的一般糟糕。
Thumbnail
《耳環》在短短的39頁內描繪思念出海失蹤的哥哥的少年良田,與將耳環丟入海中的少女あやこ兩人短暫相遇的故事。或許只有那個年齡、那個晚上、這樣的兩個人,才能達成的一段奇妙旅程。原本完整的ㄧ副耳環就此分別穿戴在彼此身上,一段共享的秘密。那個夏天的晚上,他們都因此成長了一點點。
Thumbnail
......戰鬥力跟不上版本的角色只能在雪藏跟情懷牌之間擇一嗎?不是這樣的,要塑造低戰力角色有的是辦法,而且解套的辦法究竟在哪,原作已經給出答案......
Thumbnail
......究其根本動畫版與漫畫版的癥結點是相同的,也就是配角人數過多使得情節處理難以做到盡善盡美,其中又以漫畫版的問題更為明顯......