每时每刻,我都会在Rails生态系统的红宝石中遇到这个问题:
class LocalizeUrlService
class Services::UpdateUserRegistrationForOrder
class ProductionOrderEmailService
UserCart::PromotionsService.new(
Shipping::BulkTrackingService.new(bulk_update, current_spree_user)
您还可以看到示例here
但是,在“ Ruby On Rails指南”之类的官方示例中,我从未见过。这使我相信这是一个不同于Rails / OOP的语言/范式的概念。
这种范式/趋势从何而来?有教程/书吗 这些人受到影响了吗?这些人是几年前SOA趋势的坚持者吗?
将代码放入app / service / blah_service.rb是一个好主意吗? 如果是,则可以将什么逻辑/代码视为“服务”材料。 是否有/将要作为服务使用的任何种类的代码?
哪个gem / plugin创建了app / services文件夹?最初未附带Vanilla Rails应用。
sidetone:就个人而言,我在实例化服务方面遇到问题。我觉得班级和实例化被ametuer程序员滥用了。我觉得上课和实例化是“为了一件事情”,服务是“要做的事情” 所以mixins / defs / include应该是我的感觉。
答案 0 :(得分:2)
服务对象用于不适合常规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
和{{1} }成为联系API并更新多个模型的复杂工作流程。
您可以将所有内容编写为关注点或模块,将所有内容包括在这三个以前简单的模型中,并编写大型destroy
和Subscription.register
类方法。现在您的订阅可以发推并发布到Slack并验证电子邮件地址并执行后台检查吗?奇怪的。您的模型现在充斥着与其核心功能无关的代码。
相反,您可以编写Subscription.remove
和SubscriptionRegistration
服务对象。这些功能包括推文和存储统计信息以及执行后台检查等功能(或更可能将其放入更多服务对象中)。它们每个都有一个公共方法:SubscriptionRemove
和SubscriptionRegistration.perform(user, list)
。用户,列表和订阅不需要了解任何信息。您的模型保持苗条,只做一件事。而且您的每个服务对象都做一件事。
关于您的具体问题...
这种范式/趋势来自哪里?
据我所知,这是“胖模型/瘦控制器”趋势的结果;这就是我的想法。虽然这是一个好主意,但通常您的模型变得过于肥胖。即使有模块和关注点,将其塞入一个类也太多了。通常会使模型或控制器肿的其他业务逻辑进入服务对象。
哪个gem / plugin创建了app / services文件夹?
您知道。 SubscriptionRemove.perform(subscription)
中的所有内容都会在Rails 5中自动加载。