Skip to content

中大型项目架构 ​

Laravel 的初学者分为两种:

  • 一种是乖乖的将程序在写 MVC 架构内,这将导致 Controller 和 Model 非常臃肿,日后代码很难维护。
  • 一种是不知道将程序写在哪一个Class内而犹豫不决。

本文整理出最适合 Laravel 的中大型项目架构,兼具易维护、易扩展、易复用和易测试的特点。

Controller 过于肥大 ​

受RoR的影响,初学者常认为 MVC 架构就是 Model,View,Controller:

  • Model 就是数据库。
  • Controller 负责与 HTTP 通信,调用 Model 与 View。
  • View 就是 HTML。

假如按照此定义,以下需求应该写在哪里呢?

1.发送 Email,使用外部 API。

2.使用 PHP 写的逻辑。

3.依需求将显示格式作转换。

4.依需求是否显示某些资料。

5.依需求显示不同资料。

其中1, 2 属于商业逻辑,而3, 4, 5 属于显示逻辑,若依照一般人对 MVC 的定义,Model 是数据库,而 View 是HTML,以上这些需求都不能写在Model 与View,只能勉强写在Controller。

因此初学者开始将大量程序写在 Controller,造成 Controller 的肥大难以维护。

Model 过于肥大 ​

既然逻辑写在 Controller 不方便维护,那我将逻辑都写在 Model 就好了?

当你将逻辑从 Controller 搬到 Model 后,虽然 Controller 变瘦了,但却肥了 Model,Model 从原本只处理数据库逻辑,现在变成还要负担商业逻辑与显示逻辑,结果更惨。

Model 代表数据库吗?把它想成是 Eloquent class就好,数据库逻辑应该写在 repository 里,这也是为什么 Laravel(Lumen) 已经没有 Models目录,Eloquent class 仅仅是放在 app 根目录下而已。

中大型项目架构 ​

那我们该怎么写呢?别将我们的思维局限在 MVC 内 :

  • Model : 仅当成 Eloquent class。
  • Repository : 辅助 Model,处理数据库逻辑,然后注入到 service。
  • Service : 辅助 Controller,处理商业逻辑,然后注入到 Controller。
  • Controller : 接收 HTTP request,调用其他 service。
  • Presenter : 处理显示逻辑,然后注入到 View。
  • View : 使用 blade 将资料 binding 到 HTML。

其中蓝色为原本的 MVC,而紫色为本文要介绍的的重点 : Repository 模式,Service 模式与 Presenter 模式。

arch002.svg

箭头表示物件依赖注入的方向。

我们可以发现 MVC 架构还在,由于 SOLID 的单一职责原则与依赖反转原则 :

  • 我们将数据库逻辑从 Model 分离出来,由 repository 辅助 Model,将 Model 依赖注入进 repository。
  • 我们将商业逻辑从 Controller 分离出来,由 service 辅助 Controller,将 service 依赖注入进 Controller。
  • 我们将显示逻辑从 View 分离出来,由 presenter 辅助 View,将 presenter 依赖注入进 View。

建立目录在 src 的 Bundle 目录下建立 Repositories,Services 与 Presenters 目录。

别害怕在 Laravel 预设目录以外建立的其他目录,根据 SOLID 的单一职责原则,class 功能越多,责任也越多,因此越违反单一职责原则。

所以你应该将你的程序分割成更小的部分,每个部分都有它专属的功能,而不是一个 class 功能包山包海,也就是所谓的万能类。

所以整个项目不应该只有 MVC 三个部分,放手根据你的需求建立适当的目录,并将适当的 class 放到该目录下,只要我们的class 有namespace 帮我们分类即可。

Repository ​

由于篇幅的关系,将 repository 独立成专文讨论,请参考如何使用 Repository 模式 ?

Service ​

由于篇幅的关系,将 service 独立成专文讨论,请参考如何使用 Service 模式?

单元测试 ​

由于现在Model、View、Controller 的依赖都已经拆开,也都使用依赖注入,因此每个部分都可以单独的做单元测试,如要测试service,就将repository 加以 mock,也可以将其他 service 加以 mock。

结论 ​

本文谈到的架构只是开始,你可以依照实际需求增加更多的目录与 class,当你发现你的 MVC 违反 SOLID原则时,就大胆的将 class 从 MVC 拆开重构,然后依照以下手法 :

  • 建立新的 class 或 interface。
  • 将依赖类依赖注入到 class。
  • 在 class 内处理他的职责。
  • 将 class 或 interface 注入到 Controller 或 View。

最后搭配单元测试,测试重构后的架构是否与原来的需求结果相同。