原文请访问:[Maintain Slim PHP MVC Frameworks with a Layered Structure](https://www.toptal.com/php/maintain-slim-php-mvc-frameworks-with-a-layered-structure) 图灵社区的中译版本: [用分层结构打造苗条MVC框架](http://www.ituring.com.cn/article/274738) 基于诸如Yii和Laravel这样MVC框架的一个必然问题就是:臃肿的controller(控制器)和臃肿的model(模型)。而使控制器和模型变得臃肿的主要东西就是这些框架中强大而又必要的组件 -- Active Record。 ### 问题:违反了单一职责原则的Active Record ### The Problem: Active Records and Its Violation of the SRP Active Record是一个架构模式,一种访问数据库中数据的方式。由Martin Fowler在他2003年的《企业应用框架模式》一书命名并广泛应用在PHP框架中。 尽管它一是个非常必要的方式,然而Active Record(AR)模式违反了单一职责原则(SRP),因为AR模型: 处理了数据查询与数据存储。 (通过关系)知道了系统中太多其他的模型。 经常在应用的业务逻辑中直接调用(因为数据存储的实现与业务逻辑的表达密切相关)。 当需要尽快创建一个应用原型时,违反SRP对于快速开发来说是一桩好的交易,但当应用成长为一个中等或者大型项目时,这是非常有害的。“上帝”模型和臃肿的控制器难以测试和维护,并且如果你不可避免地不得不改变数据库结构的话,在控制器的各个地方随意使用模型将会导致维护成本巨大。 解决方案很简单:把Active Record的职责划分到几个层,并注入跨层(cross-layer)依赖。这种方式还能简化测试,因为它允许你模拟非当前所测试的层。 ### 解决方案:针对PHP MVC框架的分层结构 ### The Solution: A Layered Structure for PHP MVC Frameworks “臃肿的”PHP MVC应用随处都有依赖,错综复杂且容易出错,而分层结构则使用依赖注入保持干净和清晰。 主要有这五层,我们稍候会讲到: - The controller layer 控制层 - The service layer 服务层 - DTOs, a subset of the service layer 数据传输对象(DTO),服务层的一个子集 - View decorators, a subset of the service layer 视图装饰者,服务层的一个子集 - The repository layer 存储层 [](https://uploads.toptal.io/blog/image/122641/toptal-blog-image-1490891451776-58625ccb33d0309099261fd3792b791a.png) To implement a layered structure, we need a dependency injection container, an object that knows how to instantiate and configure objects. You don’t need to create a class because the framework handles all the magic. Consider the following: ```php class SiteController extends \Illuminate\Routing\Controller { protected $userService; public function __construct(UserService $userService) { $this->userService = $userService; } public function showUserProfile(Request $request) { $user = $this->userService->getUser($request->id); return view('user.profile', compact('user')); } } class UserService { protected $userRepository; public function __construct(UserRepository $userRepository) { $this->userRepository = $userRepository; } public function getUser($id) { $user = $this->userRepository->getUserById($id); $this->userRepository->logSession($user); return $user; } } class UserRepository { protected $userModel, $logModel; public function __construct(User $user, Log $log) { $this->userModel = $user; $this->logModel = $log; } public function getUserById($id) { return $this->userModel->findOrFail($id); } public function logSession($user) { $this->logModel->user = $user->id; $this->logModel->save(); } } ``` In the above example, ``UserService`` is injected into ``SiteController``,`` UserRepository`` is injected into`` UserService`` and the AR models ``User`` and ``Logs`` are injected into the ``UserRepository`` class. This container code is fairly straightforward, so let’s talk about the layers.