大家都在談 MVC,但我想先談談更基本的東西,那就是 controller 和 service 的職責,不管寫了幾年的 code,總是會不停看到,應該寫在 service 的邏輯放在 controller 中,應該寫在 service 的 code 寫在 controller 中。
大多數的人都說 controller 就是負責流程控制,service 負責商業邏輯,但什麼是流程控制,什麼是商業邏輯,老實說剛寫個幾年程式哪聽的懂,有些事呢,就是要自己受過折磨,才能深刻體會。
controller 的責任
做值的檢查
- xxx != null
- xxx > 0
決定走哪個 view 或 response http code,接 Error
1 | create : async(req,res) => { |
呼叫 service
並只傳內容物(Object Data 或 簡單型別值) 給 service。除了一些特例,牽涉到 session、cookie、request header 特定的邏輯,但大多遇到的情況都不需要,誤用居多。
1 | OrderService.create(req); // (X) |
service 的責任
商業邏輯
(e.g. 寄信、訂單計算、與第三方平台溝通…)
1 | getServiceFee : async(user, order) => { |
複雜的物件生成
1 | getNewUser : async(...略...) => { |
與資料庫溝通
如果有 Dao 的架構的話,可以呼叫 Dao 的 method,如果沒有 Dao 的架構,那 Dao 所做的事,就可以在這裡做。
略…
這樣做的好處是什麼?
職責分明,減少重複的code
如果 controller 跟 service 都要做值的檢查,那不是重工?如果有時候把檢查放在 controller,有時候放在 service,那有沒有哪一天,根本忘了做值的檢查,所以遊戲規則要先講好。
減少重工的部分,可以看看 Clean Code 這本書,如果沒看過 Clean code,可以先看看 Kent Beck的實作模式,比較薄,而且談的東西,好像都可以馬上運用在工作中,Clean Code 有些例子真的有點太極端,即使了解了,也很難完全實行,而且看完後,你會覺得兩本書提到的理論,有會相衝突的地方很有趣。
易於測試
如果 controller 把 request 當參數傳給 service,那你 service 的測試 code,不就要寫得跟 controller 一樣?(重工) 所以只傳 Object data 跟 簡單型別值,你的測試會比較好寫。在者,並不是只有HTTP一種協定,如果現在變成接 Socket,那 service 不就不能共用。
結論
每個 Pattern 都是為了解決一些前輩的問題,而演變而成的,多多讀讀 Pattern 就可以少踩一些雷,多希望我能更早領悟這個道理。