首页 > 解决方案 > 人们在 Rails 应用程序中使用什么 app/services/

问题描述

我现在和他们都会在 ruby​​ on rails 生态系统中遇到这种情况:

class LocalizeUrlService
class Services::UpdateUserRegistrationForOrder
class ProductionOrderEmailService
UserCart::PromotionsService.new(
Shipping::BulkTrackingService.new(bulk_update, current_spree_user)

您还可以在此处查看示例

但是,在例如“Ruby On Rails Guides”的官方示例中,我从未见过这种情况。这让我相信这是一个来自不同于 Rails/OOP 的另一种语言/范式的概念。

这种范式/趋势从何而来?是否有这些人受到影响的教程/书籍?这些人是否对几年前的 SOA 趋势持反对态度?

将代码放在 app/service/blah_service.rb 中是个好主意吗?如果是,哪些逻辑/代码可以被视为“服务”材料。是否有任何类型的代码可以/不属于服务?

什么 gem/plugin 创建了 app/services 文件夹?vanilla rails 应用程序一开始并没有附带它。

sidetone:就我个人而言,我对实例化服务有疑问。我觉得 ametuer 程序员滥用类和实例化。我觉得类和实例化是“为了一件事”,而服务是“做”的事情,所以我觉得 mixins/defs/include 应该是要走的路。

标签: ruby-on-rails

解决方案


服务对象用于不适合正常 MVC 范式的事物。它们通常用于业务逻辑,否则会使您的模型或控制器太胖。通常,它们没有状态(保存在模型中)并执行诸如与 API 或其他业务逻辑对话之类的事情。服务对象让您的模型保持精简和专注,每个服务对象也很精简并专注于做一件事。

Rails Service Objects: A Comprehensive Guide提供了使用服务对象管理与 Twitter 的对话或封装可能跨多个模型的复杂数据库事务的示例。

Ruby on Rails 中的服务对象……您将展示如何创建服务对象来管理新用户注册过程。

EngineYard 博客发布了使用服务保持 Rails 控制器清洁和干燥,并提供了一个服务对象的示例,该服务对象进行信用卡处理。

如果您正在寻找起源,Rails 中的服务对象将帮助您设计干净且可维护的代码。就是这样。是从 2014 年他们来到现场时开始的。

服务的好处是将应用程序的核心逻辑集中在一个单独的对象中,而不是将其分散在控制器和模型周围。

所有服务的共同特征是它们的生命周期:

  • 接受输入
  • 执行工作
  • 返回结果

如果这听起来很像函数的作用,那你是对的!他们甚至建议call在服务上使用公共方法名称,就像Proc一样。您可以将服务对象视为一种命名和组织大型子例程的方式。

Rails 服务对象剖析解决了服务对象和关注点之间的区别。它涵盖了服务对象相对于模块的优势。它详细介绍了什么是好的服务对象,包括......

  • 不存储状态
  • 使用实例方法,而不是类方法
  • 公共方法应该很少
  • 方法参数应该是值对象,可以被操作或者需要作为输入
  • 方法应该返回富结果对象而不是布尔值
  • 依赖服务对象应该可以通过私有方法访问,并且可以在构造函数中创建或延迟创建

例如,如果您有一个应用程序将用户订阅到可能是三个模型的列表:用户、列表、订阅。

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 发送通知、发送电子邮件、进行大量的验证……现在,从一个简单createdestroy流程变成了一个复杂的工作流程,需要联系 API 并更新多个模型。

您可以将所有这些都编写为关注点或模块,将所有这些内容包含在这三个以前简单的模型中,并编写大方法Subscription.registerSubscription.remove类方法。现在您的订阅可以在 Slack 上发推文和发帖,并验证电子邮件地址并执行背景调查?诡异的。您的模型现在充斥着与其核心功能无关的代码。

相反,您可以编写SubscriptionRegistrationSubscriptionRemove服务对象。这些可以包括推文和存储统计数据以及执行背景检查等的能力(或者更有可能将其放入更多服务对象中)。他们每个人都有一个公共方法:SubscriptionRegistration.perform(user, list)SubscriptionRemove.perform(subscription). User、List 和 Subscription 不需要知道任何关于它的信息。您的模型保持苗条并做一件事。您的每个服务对象都做一件事。

至于你的具体问题...

这种范式/趋势从何而来?

据我所知,这是“胖模型/瘦控制器”趋势的结果;我就是这样想到的。虽然这是个好主意,但您的模型通常会变得太胖。即使有模块和关注点,塞进一个单一的类也变得太多了。通常会使模型或控制器膨胀的其他业务逻辑进入服务对象。

什么 gem/plugin 创建了 app/services 文件夹?

你做。app/Rails 5 中的所有内容都是自动加载的。


推荐阅读