架構設計 EP0. 從無到有的腦內脈絡

David Lee
iCHEF
Published in
5 min readOct 12, 2020

說到架構,就是一個百家爭鳴的主題。每個工程師可能都有自己的一套作法。

就像沒有最完美的架構一樣,選擇適合自己,適合團隊,適合組織的設計脈絡,才能對開發帶來最大的助益。

本篇的核心精神將會著重於兩點:

  1. 專注可見的需求,不過度設計
  2. 保持彈性,在開發過程中不排斥迭代

如果說一個工程師每每可以從全知視野來看見當前要開發的功能,那肯定是種天賦,也是許多人追求的目標。

不過進入了實際架構的設計時,我反而喜歡不要看得太遠。不需要用很抽象的方式強迫自己想清全局,大多數的情景是,在你拿到開發 spec 的時候,你過去的經驗會自動給你一個大概的想法,根據你熟悉或著不熟悉可能細節不見得會很清楚,沒有關係,有很多方法可以幫助我們一步一步的來進行思考。

簡單來說只有一句話「專注眼前看得到的需求,一步一步沿著已知條件發展。」

首先從最確定的部分開始下手,以分工來說,通常對於後端最為清楚的部分就是API的樣貌與作用了,同時,API 介面可以為你劃出最外圈的架構邊界,以此畫出最外面的圈圈然後開始延伸。

在實現API介面時,特別注意只留下最必要的輸出和輸入,剩下的東西都不應該放在這裡,而這些就是第一步要被抽出來獨立的地方。

例如今天我們要實作一個密碼登入的功能,我們可能就需要註冊的 API,登入的API或者也許還有忘記密碼的 API。

抽出第一層之後,接下來根據被抽出來的商業邏輯再進行下一次的切分,規劃一下,在整個邏輯之中,是否有需要再拆分給不同層級的類別來負責。

顯然第一個要抽出來的,應該會是在資料庫建立使用者這個行為,這不是我們會希望在 View(API) 層級看到的邏輯。另外密碼驗證,重新設置密碼的邏輯也要給他一個負責的類別 (class)。

邏輯的拆分往往是剛入門的人相對比較難以下手的地方,因為他沒有硬性規定的拆分方式。

但通常可以根據兩個原則來做,大原則是如果這條邏輯在系統層面有很明確的定位的話,應該優先遵循這個系統定位。

例如我們如果想去寫入登入資料的話,顯然這個東西要跟資料庫或者 ORM 更靠近一些,其他相近的還有如果要拿取登入資訊,例如姓名等資料,應該也會從靠近的地方來取用。

至於這邊說的靠近,是什麼意思呢?

當我們拆分程式邏輯的時候,通常會針對功能拆分大的資料夾,接下來再依據不同的特性往下拆分資料夾。

可能大的資料夾叫做使用者,下一層就有分 model/view/middleware/other-specific-action-hanlders…,再往下可能有更細節的拆分。

那所謂的近,其實就是放在更接近的資料夾甚至同一個檔案的意思。

從系統的視角來拆分這些東西,很大的重點是如此一來不同層級之前的依賴性才可以更好的被管理,不同資料夾之前的互相 import 結構往往可以在實作階段讓你提前發現是否有些東西放置的位置有問題,看起來很不合理的時候,就可能需要停下來先重新設計一下。

例如如果你在 user 的 model 裡面因為試圖加入傳送驗證簡訊的功能,因而 import了 xxx.utils.sms.xxx …,基本上以 model 的位置來看,他和 utils 距離其實滿遠的,而且作為 model 他可能會跟很多人有依賴性,這時候加入一個相對遠的 utils 很容易會導致整個相關的依賴鍊過於複雜或者是之後有維護上的疑慮。遇到這樣需要把兩個位置較遠的功能在同一個邏輯下使用的時候,大多數的情境都適合把它獨立的再抽出去自己放在一個地方,更加精確的位置,針對相關的商業邏輯來放置。

e.g.

users/model/user
global/utils/sms

/user/model.py

from global.utils import sms...

可能就適合拿出去
/users/utils/sms_utils

from users.model import users
from global.utils import sms
...

在大多數的情境,盡量保持在同一個資料夾同一個檔案只會用到自己附近的函式或者類別,當你需要綜合很多不同的資料夾的資源的時候,可以檢視一下這時候就應該要再往外抽了,他會是一個更有針對性的邏輯,而不要故意把這樣複雜的邏輯寫得很通用。只要服務原本他應該要服務的商業邏輯就好。

有點扯遠了,回到剛剛「專注眼前看得到的需求,一步一步延著已知條件發展。」

我們剛剛的範例已經從找到下手點: 例如 API 層,然後做了切分,在接下來其實就是一直遞迴地執行這個過程直到你覺得拆分的已經足夠細節,就可以先停止。

什麼叫做足夠細節,可以試試看如果你拆分的結構,在你預估開發時程的時候可以幫助你把時程粒度切分到半日的話,也許就足夠了。

另外在過程中時時檢視已經規劃好的架構,再逐步累積之後是否還是最合理的架構,在這個階段不用怕推翻自己,越早把架構穩定到理想的狀態,之後可以節省更多的時間,越合理的設計,三個月後的自己也會更容易讀懂。

想要好讀懂,好維護,就關係到前面提到要依循兩個原則的第二點。

在符合系統架構的前提之下,依據邏輯本身的關聯性與脈絡來切分資料夾與檔案。

這個過程就很像你在寫文章或者一篇故事,你的結構敘事性越強,一般而言維護性就越強,因為會有比較好的可讀性。

程式語言的本質是給機器閱讀的,但是從系統的角度來看,他同時也是要給維護的工程師讀的,也就因為這樣,能夠越快被看懂的程式架構,一般來說都是較被推崇的。

設計架構的過程中最困難的可能會是有很多不確定的抉擇點,在這裡建議從已知的需求下手,可以適度延伸到可以預測的未來來規劃擴充性,「專注眼前看得到的需求,一步一步延著已知條件發展。」,多多嘗試,多討論不要害怕被挑戰,每次的討論可能都會造就一個更好的程式架構,也會給三個月後的自己,或是其他負責維護的人,很多幫助。

--

--