问题
Rails 项目随着代码量不断增大,复杂度不断增加,开始越来越难以维护。表现在:
- fat controller
- 不断增加的需求使 controller 塞满了各种 action, 比如 search, order 等等。
- 某些 action 的逻辑特别复杂,比如 search 会根据 params 实现不同的 filter 逻辑。
- fat model
- 不断增加的需求使 model 塞满了各种 method, scope and callback
fat model 和 controller 降低了代码的可读性和可扩展性,让我们在增加新功能时束手束脚,让测试速度降低,让新人陷入阅读面条代码的痛苦中。
‘Growing Rails Applications In Practice’ (以下简写为 ‘Growing Rails’) 这本书提出了一套解决方法,让 Rails 项目在不断扩张的同时保持良好的可读性和可扩展性。
观点
‘Growing Rails’ 提出了几点观点,书中所有的方法都是建立在这些观点之上的,它们是:
- complex is complex. 复杂的代码就是复杂的,我们能做的只是用模块的方法管理复杂度
- we can manage the complexity in Rails way. 我们选择了 Rails 框架,就要利用 Rails 推荐的方法来写代码。也许会有些反直觉的做法,把这当成是一种 trade-off
方法
Better Controller
采用一种一致的 controller 设计:Rails CRUD
咋看上去不适于 CRUD 的交互行为,也 map 到 CRUD 上,比如订阅/退订可以用 subscription create/destroy
Use ActiveRecord’s validation and callback
使用 validation and callback 而不是自定义的方法来保证 model 不被误用
User ActiveRecord-like model
那些不和数据库打交道的交互,比如搜索,登陆等,仍然使用像ActiveRecord一样的建模方式,除了可读性好,心理负担轻以外,还有如下好处:
- 利用表单,比如 simple_form 的友好的错误提示
- 自动转换 params 的 string 到 integer 等等格式
- i18n
- Transaction
Dealing with fat model
避免 fat model 的关键是多写 model,复杂的就是复杂的,我们只是用模块的方式来管理复杂度。
书中举了一个例子,就像你每天都有一堆邮件寄到家里,你要么做一个邮箱装它们,要么这些邮件就散落在书桌,饭桌,床等各处。”Code never goes away”, 代码不会凭空消失的,我们要么新建一个模块放置它,要么把它散落到现有的模块中。而好的管理方法是一个模块只做一件事。
我们把 model 分成一个核心类和n个附属类,核心类只包括:
- 最少的 validation, callback 保证数据完整性
- assciations
- 通用的 scope
那些只在特定页面出现的特定逻辑不应该放在核心类中:
- 只在某些页面出现的 validation,比如注册才需要密码
- 与数据库不一一对应的属性,比如用逗号分隔的tags
- 只在某些场景需要的 callback, 比如欢迎邮件
- 权限管理,比如 admin
- 与 view 有关的 helper
以上这些都应该放在 form model 并在核心类的 namespace 下管理。