先來看看一個預設 Rails 專案的預設資料夾結構:
我們可以看到,他遵循著 MVC 架構,將各自的檔案分別放到不同的資料夾裡
這樣的資料夾結構將整個專案視為同一個系統,而隨著專案的擴張和需求的改變,整個專案系統複雜度會呈指數成長,且因為每個 model 都在同一個資料夾中,所以會很容易沒有意識到需要好好管理model 間的依賴關係,時常出現雙向耦合的情況,而過多的耦合將導致測試難以撰寫,每次開發新功能的成本也急遽攀升。
以下是一個簡單購物系統的 ER-Diagram
在 Rails 中每個 table 都會對應到一個 model,假設現在需要顯示顧客最新訂單裡的所有產品
# Models
class Customer < ActivRecord::Base
has_many :orders
end
class Order < ActivRecord::Base
has_many :products, through: :order_product
belongs_to :customer
scope :successed_paid, -> { where(status: :paid) }
scope :latest, -> { where(order_date: :desc).first }
end
# Product Controller
customer = Customer.find(1)
products = customer.orders.successed_paid.latest.products
在專案前期,透過 rails 的 relation 可以很有效率地滿足需求,但當專案變大,這種寫法反而會造成許多問題。Order 上的 scope 或 method 可能會出現在任何一個 controller 或甚至是 view 上,這樣一來就會有很多地方依賴這個介面的邏輯,但如果未來有某一個依賴介面的地方有新需求,新需求與原本的邏輯不符,這時候很容易會去調整原本大家共通依賴的介面,又沒有找到並調整所有依賴此介面的地方,這時候就會導致原本的功能壞掉。
以上述的例子來說,顧客最新訂單裡的所有產品 在 Order 中管理,卻在 Product Controller 中取用,開發人員難以定義變動的範圍,也不易清楚掌握每個 model 間的依賴關係 (在我們的專案中很常出現雙向耦合的 model)
如果整個程式架構是一塊水果千層,MVC 架構則代表各層餡料,像是對整體程式架構的橫切,而每層則有其各自的責任。此外還需要對這塊蛋糕縱切,對層中的餡料合理布局與管理。
下一篇會總結前面遇到的問題,並說明為甚麼一開始我們的專案會選用 (貌似被我處處嫌棄的) Rails 來開發