蒼時弦也
蒼時弦也
資深軟體工程師
發表於

Controller - 重新思考 Rails 架構

這篇文章是 重新思考 Rails 架構 系列的一部分。

有了第一個 Cucumber 撰寫的測試後,我們對於更新運送狀態的行為有一定的概念,那麼就可以針對這個機制來進行 Controller 的實作。

路由

首先,要讓 POST /shipments/:id/routes 這個端點可以被存取,因此需要在 Rails 的路由設定中加入以下的內容。

1Rails.application.routes.draw do
2  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
3
4  # Defines the root path route ("/")
5  # root "articles#index"
6  resources :shipments, module: :shipments do
7    resources :routes, only: [:create]
8  end
9end

這樣一來 Rails 就會知道要使用哪個 Controller 來處理,這是大多數開發者很直覺能實踐的事情,然而在 Clean Architecture 的角度分析,這是一個低階元件,也就是描述細節的資訊。

因此會被分類在最外圈 Framework & Drivers 這一階段,這類型的物件通常會跟環境有很深的連結,因此當我們要改為使用 gRPC 之類的方式互動時,這些實作就無法繼續使用,就 Rails 的角色,就只能針對 Web(HTTP 協定)的情境。

控制器

Controller 想必也不需要特別討論,在這個階段我們要先讓 Cucumber 的測試通過,因此可以直接將輸出寫死。

 1module Shipments
 2  class RoutesController < ApplicationController
 3    def create
 4      render json: {
 5        id: 1,
 6        state: 'shipping',
 7        routes: [
 8          {
 9            route_id: 'OKA',
10            shipment_id: 1,
11            delivered_at: Time.utc(2024, 3, 19, 0, 0, 0)
12          },
13          {
14            route_id: 'TPE',
15            shipment_id: 1,
16            delivered_at: Time.utc(2024, 3, 19, 2, 0, 0)
17          }
18        ]
19      }
20    end
21  end
22end

在應用到 Clean Architecture 時,可能就會有一些爭議出現。因為我們以往的經驗,會把處理的行為設計在 Controller 中,那麼這樣算是一種 Use Case(使用案例)的類型嗎?

然而,在 Clean Architecture 由外向內的第二圈舉例中有 Controller 並且歸類為 Interface Adapter(介面轉接器)的類型,這是否表示我們不該在 Controller 中有實際的商業邏輯呢?

反思

意識到這個問題後我做了不少思考,大致上來說這件事情並沒有一個絕對正確的答案,或者說他是一個系統發展過程中的一個階段。

在 Clean Architecture 中並沒有限制切分的層級數,書中以四層為例子確實是大部分情境都通用的情況,那麼 Rails 採用的 Model View Controller(MVC)設計,我認為可以看作是只有兩層的情況,也就是指具備 Framework & Drivers 和 Interface Adapter 的情境。

從另一個角度來看,當下的 Interface Adapter 因為跟實際的 Use Case 處理的內容非常一致,因此也沒有特別要做 Adapt(適應) 的必要,只需要直接使用即可。

然而,從職責的角度來看,隨著在 Controller 中實作的邏輯增長,會開始出現需要拆分職責的情況,剛開始可能會用 Fat Model 的模式來做,然而不論怎樣處理最後還是會有職責過多的狀況,進而需要切分出新的物件類型,而 Service Object 這類 Rails 常見的物件也因此而生。

直到這個階段,才開始出現更向內的第三、第四層級,也才開始有明確的 Use Case 和 Entity(實體)的劃分跟界定。