# Chapter 25 層與邊界 LAYERS AND BOUNDARIES 我們可以將系統看作是 UI、業務邏輯、Database 這三個 components 所組成的,這對簡單的系統是足夠的,但是對於大多數的系統來說,components 的數量是多過三個的。 例如一個簡單的遊戲可以很容易想出三元件: UI component 處理 player message 並送給 遊戲規則 component,而 遊戲規則 component 會利用某種持久的資料結構儲存遊戲的狀態 ## Hunt the Wumpus 還沒玩過的可以先玩一下遊戲 * [Super_Wumpus_Land](https://catseye.tc/installation/Super_Wumpus_Land) * [Wumpus Adventure](http://christianjhughes.github.io/WumpusAdventure/) > Hunt the Wumpus 是一個 1972 年開發的古早遊戲(但是 wiki 寫 1973),這是一款 Text-Based 的遊戲,玩家可以輸入簡單的命名例如 GO EAST 或是 SHOOT WEST,而電腦會回應角色看到、聽到、聞到、體驗到的東西,遊戲的目的就是避開陷阱等危險並在洞穴中找到 Wumpus > Hunt the Wumpus > by Gregory H. Coresun > Debuted: April 1973 > Language: BASIC > Platform: PDP-8 ![](https://i.imgur.com/GDEKYWX.png) ### 25.1 任何數量的UI元件都可以重複使用遊戲規則 ![](https://i.imgur.com/2Rugpry.png) 假設我們現在保留 Text-Based 的 UI,並希望和遊戲規則 decouple ,才能讓我們可以有多國語的版本。也就是說遊戲規則和 UI 是用與語言無關的 API 溝通,UI 會把 API 轉換為人類語系。 ### 25.2 遵循依賴原則 ![](https://i.imgur.com/4J3zRAd.png) 另外我們也假設遊戲狀態可能會保存在 Flash memory 或是雲端上,也不希望遊戲規則知道這些細節,因此我們也會建立一個 API 讓我們可以使用該 API 與儲存 component 進行通訊。 ## 簡潔的架構 Clean Architecture UI 的變化不一定是在語言上,我們可能還會需要改變通訊的機制,例如透過 Console 或是以簡訊、聊天軟體,甚至還有很多其他變化的可能性。 因此我們必需建立新的 API 來隔離語言和通訊機制,建構跨越邊界的 API。 ### 25.3 修改過後的圖 ![](https://i.imgur.com/a1fUyzc.png) 上圖雖然有點複雜,但是應該不會感覺意外,虛線外框的 component 定義 API,並由實線外框的 component 實作。例如 Language API 就有 Englisth 和 Spanish 兩個實作。 API 是由使用者定義,並不是由實作者來定義。例如 GameRules 想要與 Language 溝通時,會使用 GameRules 定義的 API,但是由 Language 來實作 將不同的實作給去除,只關注在 API 的部份,可以得到下圖: ### 25.4 簡化圖 ![](https://i.imgur.com/EgjCqyX.png) GameRules 是最高層級策略的元件,因此被放到最高處 我們來看資料流的方向,注意圖上的箭頭方向是原始碼的依賴方向,並非資料流的方向。使用者的輸入會先經過 Text Delivery 與 Language 元件,並轉換為 Game Rules 的命令,最終被送往 Data Storage。Data Storage 的資料也會被 Game Rules 送往 Language 轉為人類語言後送到 Text Delivery 這個架構將資料流分成了左右兩個 Stream,左邊專注在與使用者的通訊,右邊則是資料的儲存 ## 跨越流 Crossing the Streams ### 25.5 增加一個網路元件 ![](https://i.imgur.com/Ad8HWAU.png) 當然我們可能會需要不只兩個 Stream,假設現在是多人連線的遊戲,可能就會增加 Network components 並一樣由 Game Rules 控制。 ## 分割流 splitting the Streams ### 25.6 更高層級的 策略是用來管理玩家 ![](https://i.imgur.com/7RH6DOj.png) 現實當中很有可能不一定所有資料流都匯集到單一頂點,這邊我們將 Game Rules 分為玩家移動的策略和玩家血量、事件的策略,Move Management 是比較低層級的元件,會向更高級元件 (Player Management) 回報事件,而 Player Management 就會決定遊戲的輸贏 ### 25.7 加入一個微服務 API ![](https://i.imgur.com/i3TNt5u.png) 那麼 Move / Player Management 之前是否有 Boundary 呢? 作者這時想要做成大型遊戲多人版本,因此將 Player Management 放到伺服器上運行,而 Move Management 留在 Clietn 端 這時就形成 Player Management 提供 API 給所有 Client 端的 Move Management 使用,而他們之前就有了一個明顯的 Boundary 了 ## 總結 本章節透過簡單的例子發展出複雜的架構,目的是為了讓我們理解 Boundary 處處存在,我們必需辨別何時需要有邊界,也要意識到全部實作的代價是很高的,但忽視該有的邊界,日後要再加入也要付出很大的代價。 那麼我們到底該怎麼做呢? 過猶不及的設計都是不好的,身為架構師必須看到未來,權衡成本,聰明的選擇邊界所在的位置 ![](https://i.imgur.com/qgCLS5D.png) > You Aren't Gonna Need It - YAGNI > Always implement things when you actually need them, never when you just foresee that you may need them. 當然這個決定不是一次性的,在專案發展的初期不能草率的做決定,要觀查專案的發展,在實作的成本與忽略的成本黃金交叉時下手實作 需要神之眼 (watchful eye)