服务对象用于不适合正常 MVC 范例的事物。它们通常用于业务逻辑,否则会使您的模型或控制器太胖。通常,它们没有状态(保存在模型中)并执行诸如与 API 或其他业务逻辑对话之类的事情。服务对象让您的模型保持精简和专注,每个服务对象也很精简并专注于做一件事。
Rails Service Objects: A Comprehensive Guide 提供了使用服务对象管理与 Twitter 对话或封装可能跨多个模型的复杂数据库事务的示例。
Service Objects in Ruby on Rails…and you 显示创建服务对象来管理新用户注册过程。
EngineYard 博客发布了Using Services to Keep Your Rails Controllers Clean and DRY,其中包含一个处理信用卡的服务对象示例。
如果您正在寻找起源,Service objects in Rails will help you design clean and maintainable code. Here's how. 是 2014 年他们出现的时候。
服务的好处是将应用程序的核心逻辑集中在一个单独的对象中,而不是将其分散在控制器和模型周围。
所有服务的共同特点是它们的生命周期:
如果这听起来很像函数的作用,那你是对的!他们甚至建议使用call 作为服务上的公共方法名称,就像Proc 一样。您可以将服务对象视为一种命名和组织大型子例程的方式。
Anatomy of a Rails Service Object 解决了服务对象和关注点之间的区别。它涵盖了服务对象相对于模块的优势。它详细介绍了什么是好的服务对象,包括......
- 不存储状态
- 使用实例方法,而不是类方法
- 公共方法应该很少
- 方法参数应该是值对象,可以被操作或需要作为输入
- 方法应该返回富结果对象而不是布尔值
- 依赖的服务对象应该可以通过私有方法访问,并且可以在构造函数中创建或延迟创建
例如,如果您有一个应用程序为用户订阅可能是三种模型的列表:用户、列表、订阅。
class List
has_many :subscriptions
has_many :users, through: :subscriptions
end
class User
has_many :subscriptions
has_many :lists, through: :subscriptions
end
class Subscription
belongs_to :user
belongs_to :list
end
使用基本的create 和destroy 方法和关联以及一些回调,在列表中添加和删除用户的过程非常简单。
现在你的老板想要一个精细的订阅流程,它可以进行大量日志记录、跟踪统计信息、向 Slack 和 Twitter 发送通知、发送电子邮件、进行大量验证……现在,简单的 create 和 destroy 变得复杂工作流联系 API 并更新多个模型。
您可以将所有这些都编写为关注点或模块,将所有这些内容包含在这三个以前简单的模型中,并编写大的 Subscription.register 和 Subscription.remove 类方法。现在您的订阅可以在 Slack 上发推文和发帖,并验证电子邮件地址并执行背景调查?诡异的。您的模型现在充斥着与其核心功能无关的代码。
相反,您可以编写 SubscriptionRegistration 和 SubscriptionRemove 服务对象。这些可以包括发布和存储统计数据以及执行背景检查等的能力(或者更有可能将其放入更多服务对象中)。它们各有一个公共方法:SubscriptionRegistration.perform(user, list) 和 SubscriptionRemove.perform(subscription)。 User、List 和 Subscription 不需要知道任何关于它的信息。您的模型保持苗条并做一件事。您的每个服务对象都做一件事。
至于您的具体问题...
这种范式/趋势从何而来?
据我所知,这是“胖模型/瘦控制器”趋势的结果;我就是这样想到的。虽然这是个好主意,但您的模型通常会变得太胖。即使有模块和关注点,塞进一个班级也太难了。通常会使模型或控制器膨胀的其他业务逻辑进入服务对象。
什么 gem/plugin 创建了 app/services 文件夹?
你会的。 app/ 中的所有内容都会在 Rails 5 中自动加载。