ActionMailer 最佳实践:在模型或控制器中调用方法?

Posted

技术标签:

【中文标题】ActionMailer 最佳实践:在模型或控制器中调用方法?【英文标题】:ActionMailer best practices: Call method in the model or the controller? 【发布时间】:2010-10-05 05:56:42 【问题描述】:

发送电子邮件通常是在对模型执行操作之后调用,但电子邮件本身是一个视图操作。我正在寻找您如何考虑要问自己哪些问题来确定将操作邮件方法调用放在哪里。

我见过/使用过它们:

在模型方法中 - 相关但独立的关注点的不良耦合? 在模型中的回调中(例如 after_save) - 就我目前的知识水平而言,最好的分离。 在控制器操作中 - 感觉不对,但在某些情况下这是构建代码的最聪明的方式吗?

如果我想知道如何编程,我需要像程序员一样思考,因此学习如何思考特定的编程解决方案值得我独自进行数月的编码。谢谢!

【问题讨论】:

【参考方案1】:

迟到的答案,但我想对这个主题进行合理化:

通常,在网络应用程序中,您希望发送电子邮件作为对客户的直接反应。或者作为一项后台任务,以防我们谈论的是新闻通讯/通知邮件之类的事情。

该模型基本上是一个数据存储映射器。它的逻辑应该封装数据处理/通信与数据存储处理。因此,插入与它无关的逻辑有点棘手,并且在大多数情况下是错误的。让我们举个例子:用户注册一个帐户,应该会收到一封确认电子邮件。在这种情况下,可以说,确认电子邮件是创建新帐户的直接影响。现在,尝试在控制台中创建用户,而不是在 Web 应用程序中执行此操作。在这种情况下触发回调听起来是错误的,对吧?因此,回调选项被划伤。我们还应该在模型中编写方法吗?好吧,如果它是用户操作/输入的直接影响,那么它应该保留在该工作流程中。成功创建用户后,我会将其写入控制器。直接地。无论如何,在要在控制器中调用的模型中复制此逻辑会增加不必要的模块化,以及 Active Record 模型对 Action Mailer 的依赖性。尝试考虑在许多应用程序上共享模型,其中一些应用程序不希望使用 Action Mailer。出于上述原因,我认为邮件呼叫应该在他们有意义的地方,并且通常模型不是那个地方。试着给我一些例子。

【讨论】:

稍后评论,但这里有一个在模型中可能有意义的示例:在 status 更改后触发电子邮件时。例如,如果用户被锁定(例如,将 status 更改为 locked),无论它是如何被锁定的,它都应该收到一封电子邮件。你怎么看?【参考方案2】:

嗯,看情况。

我已经使用了所有这些选项以及您关于“我为什么要把它放在哪里?”的观点。很好。

如果我希望每次以某种方式更新模型时都发生这种情况,那么我将其放入模型中。甚至可能在模型中的回调中。

有时您只是在发布报告;没有任何更新。在这种情况下,我通常会获得一个带有发送报告的索引操作的资源。

如果邮件程序与正在更改的模型并不真正相关,我可以看到将其放入回调中。我不经常这样做。我更有可能将其封装在模型中。我做过,只是不经常。

【讨论】:

【参考方案3】:

我知道这已经有一段时间了,但最佳实践永远不会消失,对吧? :)

电子邮件定义为异步通信(确认电子邮件除外,但即使是这封电子邮件,在必须确认之前留出延迟应该是一种最佳做法)。

因此在我看来,最合乎逻辑的发送方式是:

在后台操作中(使用Sidekiq 或delayed_job) 在回调方法中:“嘿,这个动作成功完成了,也许我们现在可以告诉全世界了?”

Rails 的问题是它没有太多的回调(例如在 JS 中):我个人觉得有这样的代码很脏:

after_save :callback

def callback
  if test_that_is_true_once_in_the_objects_life
    Mailer.send_email()
  end
end

所以,如果你真的想像程序员一样思考,你的想法是在你的应用程序中设置一些自定义回调系统。

例如。

def run_with_callback(action, callback_name)
  if send(action)
    delay.send(callback_name)
  end
end

甚至creating an event system in your app 也是一个不错的解决方案。

但最终这些解决方案在时间上是相当昂贵的,所以人们最终会在行动之后内联编写它

def activate
  [...]
  user.save
  Mailer.send_mail
  respond_to 
  [...]
end

这是在同步编程中最接近回调的方式,结果是邮件程序到处调用(ModelController)。

【讨论】:

【参考方案4】:

为什么控制器是邮寄者的好地方有几个原因:

与模型无关的电子邮件。 如果您的电子邮件依赖于多个相互不了解的模型。 将模型提取到 API 并不意味着重新实现邮件程序。 邮件内容由您不想传递给模型的请求变量确定。 如果您的业务模型需要大量不同的电子邮件,则模型回调可以叠加。 如果电子邮件不依赖于模型计算的结果。

【讨论】:

以上是关于ActionMailer 最佳实践:在模型或控制器中调用方法?的主要内容,如果未能解决你的问题,请参考以下文章

构建异步邮件的最佳实践(使用 Sidekiq)

在 Rails 中测试模型的最佳实践是啥?

Yii2 HOW-TO:最佳实践

MVC - 数据计算最佳实践 - 视图模型与控制器

KVO 在 UITableView 中观察模型变化的最佳实践

iOS - 为帖子 + 评论数据模型设置视图控制器的最佳实践是啥?