Laravel程序架构设计思路之使用动作类

当我们谈论到应用程序的架构的时候,经常会问到一个经典的问题,那就是“这段代码应该放在哪里比较好”。 因为 Laravel 是一个相当灵活的框架,所以要回答这个问题其实没那么容易。我应该把我的业务逻辑写在 Model 层,还是 Controller 层,或者是其他地方?

当你的应用程序仅有一个接入点,把业务逻辑写在 Controller 层是可以的。但是现在更普遍的的情形是,有很多接入点去调用相同的功能模块。

比如说,太多数的应用程序都有用户注册的功能,它的流程是调用一个控制器然后返回一个注册成功或者失败的视图。假如这个应用程序还有移动端,那就很可能要提供一套针对移动端用户注册的 API ,因为它需要返回的数据格式是 JSON 。而且利用 Laravel 的 artisan 命令来创建用户也很常见,尤其是在项目前期的开发阶段。

Laravel程序架构设计思路之使用动作类

上面这两段代码可能看起来没有什么问题的,但是,随着业务逻辑的增加,就会显得代码很冗余。举个例子,如果你需要新用户注册完之后,增加给用户发送邮件通知的功能,你必须要再上面两个控制器中都添加发送邮件的代码。但是如果要保持代码的简洁优雅,我们可以把这些业务逻辑写到其他地方。

对于“把业务逻辑代码写到哪里”的这个问题,你去任何论坛都可以得到一个普遍的答案,那就是 “使用一个 service 层,然后在 controller 层调用这个服务类”。是的,没错,问题是我们应该怎么设计 service 类?是创建一个 UserService 类来实现所有跟用户用户有关的业务逻辑,然后把这个类注入到需要用到的 Controller 层?或者是还有其他方案?

避免神类的坑

首先,可以尝试为一个特定的模型创建一个单一类,其中包含所有的代码。例如:

看起来很完美:我们可以任何控制器中申明或者使用 create/delete 方法,并且得到我们想要的结果。但是,这种实现有什么问题呢? 那就是我们在解决问题的过程通常很少使用单一的模型 。

比如说,当我们给一个用户创建了账号的时候,也要同时给用户单独创建一个 blog 。如果按照当前的方式去实现这个流程,我们就必须创建一个 BlogService 类,然后将其依赖注入到 UserService 类。

Laravel程序架构设计思路之使用动作类

显而易见,随着应用程序的业务的增长,将会有几十到上百个 service 类,其中的一些 service 类需要依赖 5 到 6 个其他 service 类,最终的结果就是,出现代码的冗余跟混乱的局面,而这个局面是我们想不惜一切代价去避免的。

介绍单动作类

那么,如果不是用一个单一的服务类加上几个方法,我们决定把它分成几个类?下面是我最近每一个项目都采用的方法,结果很不错,推荐给大家。

首先,让我们抛弃过于笼统和模糊的服务术语,来了解一下我们的新动作类,并定义它们是什么以及它们可以做什么。

一个动作类,应该有一个能够说明其功能的名字,比如:CreateOrder, ConfirmCheckout, DeleteProduct, AddProductToCart等。

它应该有且只有一个公共方法,作为 API 。理想的情况下,应该是相同的方法名,像 handle() 或者 execute() 。如果需要对我们的动作类实现某种适配器模式,这是非常方便的。

它必须对请求和响应不可知。它不处理请求,也不发送响应。这样的职责应该由控制器来承担。

它可以依赖其它的动作类。

如果有任何事情阻止它执行和/或返回期望的值,那么它必须通过抛出一个 Exception 来强制执行相关的业务逻辑,并且让调用者(或者 Laravel 的 ExceptionHandler )来承担如何呈现/响应异常的责任。

创建我们的 CreateUser 动作类

现在,让我们看看前面的例子,并用一个单动作类来重构它,我们将命名为 CreateUser 。

Laravel程序架构设计思路之使用动作类

你或许想知道当邮箱地址已经被占用时,该方法为什么会抛出了异常。 这难道不是请求验证来保证的吗?当然可以。然而,在动作类内部来执行业务逻辑不是更好吗?这样使得逻辑变得易于理解和调试。

让我们看看使用我们动作类之后的控制器代码,如下:

Laravel程序架构设计思路之使用动作类


现在,无论我们做什么修改,用户注册过程都会由 API 和 Web 版本处理,优雅整洁。

动作类的嵌套

假如,我们需要一个动作类将 1000 个用户导入我们的应用中。我们可以写一个动作类,并且继续使用上文的 CreateUser 类:

Laravel程序架构设计思路之使用动作类


内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/089b9471a3379d4dd6d24e64a82f01f4.html