架構規劃 - 重新思考 Rails 架構
經過這段時間的分析,我們對於以往在 Rails 開發系統時會遭遇的問題有了近一步理解。接下來,我們要再一次的分析系統的架構,來對原本的設計進行改善。
過度設計
使用像是 Clean Architecture(清楚架構)這類方法來規劃時,我們仍需要考慮過度設計的問題。假設系統本身並不複雜,也沒有立即性的擴充需求,並不一定要像 Clean Architecture 的例子中分為四個層級,只有一個或者兩個也沒問題。
以 Rails 為例子,在沒有特別區分的狀況下 MVC 框架可以看做只有 Framework(框架)跟 Use Case(使用案例)兩個層級。
然而,在系統逐漸複雜的狀況下,單一層級的物件無法負擔不斷擴展的職責,那麼就需要進一步的切分出新的層級來確保職責足夠單純。
根據經驗,Clean Architecture 例子中分為四個層級對大多數系統不會太過於複雜,也不會太少的狀態。
領域劃分
若要將系統規劃清晰,我們可以從「領域(Domain)」和「元件(Component)」兩種不同面向的職責劃分來進行思考。
這裡選擇不用「功能」來描述是因為容易混淆,因為可以表示某個領域提供給使用者的功能(Feature)或者在系統運作上扮演的功能(Function)
以物流系統來看,我們大致上會需要處理幾種不同領域的問題
- 訂單(Order)
- 運送(Shipment)
- 集裝(Container)
除此之外還要能夠提供報表相關的機制,因此還會有像是運送報告(ShipmentReport)之類的領域劃分出來。
有了領域的劃分,才能意識到邊界(Boundary)的存在,每個領域都無法直接互相干涉,需要透過特定的物件協調(如:UseCase)來確保不會有預期外的修改操作發生。
對 Ruby 開發者來說,Private(私有)的概念可能不是那麼常見,然而要保護系統內部資訊的一致,就需要透過私有方法來限制外部存取可用的手段。
元件劃分
元件(Component)可以看作是一系列讓系統可以運作的零件,每個領域中都會有類似的元件,然而透過不同的元件搭配,就能夠得到不一樣的效果。
我們以「運送」這個領域作為例子,以 Clean Architecture 的角度來看該如何劃分出對應的元件(Framework 就是 Rails 本身,因此不特別列出)
- Interface Adapter
- ActionController
app/controllers/shipments_controller.rb
- …
- ActionView
app/views/shipments/new.html.erb
- …
- ActiveRecord
app/model/shipment.rb
- …
- ActiveJob
app/jobs/refresh_shipment_state_job.rb
- …
- Form
app/forms/create_shipment_form.rb
- …
- ActionController
- Use Case
- Command
app/commands/create_shipment_command.rb
- …
- Query
app/queries/user_shipment_query.rb
- …
- Command
- Entities
- Shipment
lib/shipment/shipment.rb
lib/shipment/item.rb
- …
- Order
- …
- Container
- …
- Shipment
若要能夠將 Clean Architecture 的實踐套用到 Rails 上,同時也不違背 Domain-Driven Design 的設計,上述的架構會是相對合理的。即使我們通常會認為 Rails 中的 Model 應該是 Entity 的角色,然而從 ActiveRecord 的設計來看,因為具有 SQL 資料庫的 Adapter 性質,更可能是 Interface Adapter 的一種類型。
另一方面,這也對應到在一開始所提出「系統初期不會過於複雜」這樣的論點,當我們系統複雜度增長的時候,是逐步抽離出高階元件(High-Level Component)來抽象化,而演變出了 Use Case 和 Entity 的類型。
另一方面,高階元件因為抽象程度足夠,也不應該受到框架的限制跟綁定,那麼以 Library(函式庫)的型態存在,也是相當合理的。
如果對這篇文章有興趣,可以透過以下連結繼續閱讀這系列的其他文章。
- 軟體架構的挑戰 - 重新思考 Rails 架構
- 資料驅動設計 - 重新思考 Rails 架構
- 複雜的操作 - 重新思考 Rails 架構
- 時區換算 - 重新思考 Rails 架構
- 報表機制 - 重新思考 Rails 架構
- 通用化功能 - 重新思考 Rails 架構
- ActiveRecord 的限制 - 重新思考 Rails 架構
- 領域驅動設計 - 重新思考 Rails 架構
- 從架構到設計 - 重新思考 Rails 架構
- 重復使用的反思 - 重新思考 Rails 架構
- 釐清脈絡 - 重新思考 Rails 架構
- 劃分邊界 - 重新思考 Rails 架構
- 職責劃分 - 重新思考 Rails 架構
- 架構規劃 - 重新思考 Rails 架構
- 驗收測試 - 重新思考 Rails 架構
- Controller - 重新思考 Rails 架構
- Form - 重新思考 Rails 架構
- Use Case - 重新思考 Rails 架構
- Entity - 重新思考 Rails 架構
- Repository - 重新思考 Rails 架構
- Output - 重新思考 Rails 架構
- Query - 重新思考 Rails 架構
- 可能性 - 重新思考 Rails 架構