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

前言

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

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

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

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

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

避免神类的坑

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

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

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

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

介绍单动作类

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

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

  • 一个动作类,应该有一个能够说明其功能的名字,比如:CreateOrder, ConfirmCheckout, DeleteProduct, AddProductToCart等。
  • 它应该有且只有一个公共方法,作为 API 。理想的情况下,应该是相同的方法名,像 handle() 或者 execute() 。如果需要对我们的动作类实现某种适配器模式,这是非常方便的。
  • 它必须对请求和响应不可知。它不处理请求,也不发送响应。这样的职责应该由控制器来承担。
  • 它可以依赖其它的动作类。
  • 如果有任何事情阻止它执行和/或返回期望的值,那么它必须通过抛出一个 Exception 来强制执行相关的业务逻辑,并且让调用者(或者 Laravel 的 ExceptionHandler )来承担如何呈现/响应异常的责任。

创建我们的 CreateUser 动作类

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

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

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

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

动作类的嵌套

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

非常整洁,不是吗?我们可以通过将其嵌入在 Collection::map() 方法中来重用 CreateUser 代码,然后返回所有新建用户的集合。当邮件被占用的时候,我们可以通过返回 Null Object 或者在 Log 文件中记录一下,你应该已经想到了。

动作类的装饰

现在,假设我们想在日志中记录每一个新注册的用户。我们可以将代码写在动作类内部,也可以使用装饰者模式。

然后,我们可以使用 Laravel 的 IoC 容器将 LogCreateUser 类绑定到 CreateUser 类,所有每当我们需要一个后者的实例时,前者都会注入进来:

AppServiceProvider.php

这使得使用配置或环境变量来控制日志记录功能的激活或停用更为方便:

AppServiceProvider.php

总结

使用这个方法似乎会需要很多的类。当然,用户注册仅仅是一个简单的例子,旨在保证代码的简短清晰。一旦项目的复杂度开始增长,动作类的真正的价值就越来越明显,因为你清晰的知道代码所在及其界定。

使用单动作类的好处:

  • 小巧而单一的逻辑域能够防止代码重复并提高代码的可重用性,保持稳定。
  • 易于针对各种场景进行独立测试。
  • 富有意义的命名在大型项目中更容易阅读。
  • 易于装饰。
  • 整个项目的一致性:防止代码分布在 Controllers、Models 等。

当然,这个方法是基于我过去几年使用 Laravel 的一些经验和我在一些项目中的实践。这对我真的很有用,现在我甚至在一些中小型项目中使用。

如果你有不同的方法,我非常期待读一读。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Laravel中使用自己编写类库的3种方法

    虽然Composer使得我们可以重用很多现有的类库(例如packagist.org中的),但是我们仍然可能用到一些不兼容composer的包或者类库.另外在某一项目中,我们也可能会创建某一类库,而且可能并没有制作成为composer package 的打算.这个时候我们可以通过以下方式来使用自己的特有类库. 增加可直接实例化的类 有些需要直接在项目中使用的类,可以通过以下方式增加到Laravel中 1.创建类库文件app/libraries/class/myClass.php 2.写入文件内容

  • 对于Laravel 5.5核心架构的深入理解

    前言 本文主要给大家介绍了关于Laravel 5.5核心架构的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 1.依赖注入 方法传入组件名,框架会自动实例化,方法内可直接使用 例如最常用的requert对象 2.服务容器 其实,Laravel 的核心就是一个 IoC 容器,Laravel 的核心本身十分轻量,并没有什么很神奇很实质性的应用功能.很多人用到的各种功能模块比如 Route(路由).Eloquent ORM(数据库 ORM 组件).Request(请求)以及

  • Laravel 加载第三方类库的方法

    Laravel 版本:5.5 有很多第三方的类库并没有制作 Composer,而是还以 require 的方式进行加载.对于此类的类库,我们只要小粒度的修改,就可以进行使用.我以极验 geetest 和邮件服务 SendCloud 为例. 在 Laravel 框架中建立存放第三方的 SDK 目录 mkdir app/Libraries 放置 geetest.SendCloud 的 SDK 官方下载后相关 SDK 后,移动到 app/Libraries 目录下: app/Libraries/sen

  • Laravel 5.5 的自定义验证对象/类示例代码详解

    Laravel 5.5 将提供一个全新的自定义验证规则的对象,以作为原来的 Validator::extend 方法的替代. Laravel 5.5 将提供一个全新的自定义验证规则的对象,以作为原来的 Validator::extend 方法的替代..很多时候我们会直接用正则表达式来处理这种特殊的验证,也有时候我们会选择用 Validator::extend 来扩展一个自定义的规则.但在 Laravel 5.5 版本中,我们有了新的手段,只要定义一个实现 Illuminate\Contracts

  • Laravel框架中扩展函数、扩展自定义类的方法

    一.扩展自己的类 在app/ 下建立目录 libraries\class 然后myTest.php 类名格式 驼峰 myTest 复制代码 代码如下: <?php class myTest { public  function test() { return '1asdasd111'; } } 在 app/start/global.php 复制代码 代码如下: ClassLoader::addDirectories(array( app_path().'/commands', app_path(

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

    前言 当我们谈论到应用程序的架构的时候,经常会问到一个经典的问题,那就是"这段代码应该放在哪里比较好". 因为 Laravel 是一个相当灵活的框架,所以要回答这个问题其实没那么容易.我应该把我的业务逻辑写在 Model 层,还是 Controller 层,或者是其他地方? 当你的应用程序仅有一个接入点,把业务逻辑写在 Controller 层是可以的.但是现在更普遍的的情形是,有很多接入点去调用相同的功能模块. 比如说,太多数的应用程序都有用户注册的功能,它的流程是调用一个控制器然后

  • 嵌入式C语言轻量级程序架构内核编写

    目录 1.了解程序架构概念和作用 2.了解单片机常见的程序架构 3.轻量级程序架构设计思想 4.程序架构内核代码的实现原理 5.掌握轻量级程序架构内核编写 6.掌握轻量级程序架构内核移植 1.了解程序架构概念和作用 在写单片机程序的时候往往会遇见下面的情况 1.产品功能需要很多不同的延时效果,又不能用delay死延时,比方说按键检测.led不同闪烁效果. 2.程序功能一多起来,整个脑子就混乱了,不知道这么整合起来. 3.不同功能区域的除了共享全局变量或数组以外不知道该怎么做. 实时操作系统rto

  • 谈一谈jQuery核心架构设计

    jQuery对于大家而言并不陌生,因此关于它是什么以及它的作用,在这里我就不多言了,而本篇文章的目的是想通过对源码简单的分析来讨论 jQuery 的核心架构设计,以及jQuery 是如何利用javascript中的高级特性来构建如此伟大的javascript库. 1 初识jQuery 从核心功能来看,jQuery仅仅做了一件简单而又平凡的事:查询.它的语法如此简洁明了,以致于很多人在不知道javascript是什么的时候就已经会用jQuery了,用一个词形容就是:大道至简. 从设计层面来看,我们

  • 解析Tomcat架构原理到架构设计

    目录 一.学习目的 1.1.掌握 Tomcat 架构设计与原理提高内功 1.2.宏观理解一个请求如何与 Spring 联系起来 1.3.提升自己的系统设计能力 二.整体架构设计 2.1.连接器 2.2.封装变与不变 2.3.容器 2.4.请求定位 Servlet 的过程 三.Tomcat 为何打破双亲委派机制 3.1.双亲委派 3.2.Tomcat 热加载 3.3.Tomcat 的类加载器 3.4.Tomcat 类加载器层次 四.整体架构设计解析收获总结 4.1.连接器 4.2.容器 4.3.类

  • Java架构设计之六步拆解 DDD

    目录 引言 项目需求信息 DDD落地实践 战略设计 1.业务分析 (1)事前准备 (2)邀请参会的人 (3)业务讨论 2.领域建模 (1)领域对象分析 (2)构建业务聚合 3.划分边界上下文 战术设计 1.微服务拆分 2.领域分层 3.代码结构 总结 引言 相信通过前面几篇文章的介绍,大家对于 DDD 的相关理论以及实践的套路有了一定的理解,但是理解 DDD 理论和实践手段是一回事,能不能把这些理论知识实际应用到我们实际工作中又是另外一回事,因此本文通过实际的业务分析把之前文章中涉及的理论和手段

  • 一条sql详解MYSQL的架构设计详情

    目录 1 前言 2 应用层 2.1 连接线程处理 3 服务层 3.1 SQL 接口 3.2 SQL解析器 3.3 SQL优化器 3.4 执行器 3.5 查询缓存 4 存储引擎层 4.1 概述 4.2 缓冲池(buffer pool) 4.2.1 数据页.缓存页和脏页 4.2.2 元数据 4.2.3 free链表 4.2.4 flush链表 4.2.5 LRU链表 4.2.6 小结 4.3 undo log 4.4 redo log 5 总结 1 前言 对于一个服务端开发来说 MYSQL 可能是他

  • 大型JavaScript应用程序架构设计模式

    PDF版的PPT下载地址:http://www.slideshare.net/jibyjohnc/jqquerysummit-largescale-javascript-application-architecture 注:在整理的过程中,发现作者有些思想是返来复去地说,所以删减了一部分,如果你的英文良好,请直接阅读英文的PPT. 以下是本文的主要章节: 1. 什么叫"JavaScript大型程序"? 2. 顾当前的程序架构 3. 长远考虑 4. 头脑风暴 5. 建议的架构 5.1 设

  • MySQL20个高性能架构设计原则(值得收藏)

    开源数据库架构设计原则 01. 技术选型 选择成熟的平台和技术,同时是最熟悉的,能做到极致的,用好不用坏,用熟不用生.目前业界的MySQL主流分支版本有Oracle官方版本的MySQL.Percona Server.MariaDB. 02. 高可用选择 高可用解决方案探讨的本质上是低宕机时间解决方案,可以理解成高可用的反面是不可用,绝大部分情况下数据库宕机才会导致数据库不可用.随着技术发展,开源数据库方面很多高可用组件(主从复制.半同步.MGR.MHA.Galera Cluster),对应场景,

  • MySQL备份恢复设计思路

    背景 首先交代一下背景,由于某些因素的限制,我们公司目前的备份策略采用的是隔天全备的方案,增量备份则使用的是binlog server的方式,那么如何快速恢复就成为了我们需要思考的问题 恢复需求 根据我以往的一些经验来说,通常需要从备份恢复数据的场景有如下几种: 1.被误删库了 2.被误删表了,类型为TRUNCATE或者DROP 3.被误删列了,类型为ALTER ... DROP COLUMN 4.被误删数据了,类型为DELETE或者UPDATE或者REPLACE 5.表空间损坏或出现坏块了 根

  • 浅谈laravel中间件的创建思路

    Laravel 中间件提供了一种机制在不修改逻辑代码的情况下,中断原本程序流程,通过中间件来处理一些事件,或者扩展一些功能.比如日志中间件可以方便的记录请求和响应日志,而不需要去更改逻辑代码. 那么我们简化一下软件执行过程,现在有一个核心类kernel,下面是它的laravel代码 #捕获请求 $request = Illuminate\Http\Request::capture() #处理请求 $response = $kernel->handle($request); 代码的作用是 捕获一个

随机推荐