Service
若将商业逻辑都写在 controller,会造成 controller 肥大而难以维护,基于SOLID原则,我们应该使用 Service 模式辅助 controller,将相关的商业逻辑封装在不同的 service,方便中大型项目的维护。
商业逻辑
商业逻辑中,常见的如 :
牵涉到外部行为 : 如发送Email,使用外部API…。
使用PHP写的逻辑 : 如促销规则计算、订单创建等。
若将商业逻辑写在 controller,会造成 controller 肥大,日后难以维护。
Service
牵涉到外部行为
如发送Email,初学者常会在 controller 直接调用 Mail::queue():
public function store(Request $request)
{
Mail::queue('email.index', $request->all(), function (Message $message) {
$message->sender(env('MAIL_USERNAME'));
$message->subject(env('MAIL_SUBJECT'));
$message->to(env('MAIL_TO_ADDR'));
});
}Mail::queue()只有一行可能无感,但很多外部服务需要一连串 API,甚至还要有 try/catch 处理。
在中大型项目,会有几个问题 :
将牵涉到外部行为的商业逻辑写在 controller,造成 controller 的肥大难以维护。
违反 SOLID 的单一职责原则 : 外部行为不应该写在 controller。
controller 直接相依于外部行为,使得我们无法对 controller 做单元测试。
比较好的方式是使用 service :
将外部行为注入到 service。
在 service 使用外部行为。
将 service 注入到 controller。
EmailService.php
UserController.php
将外部行为写在 service,解决 controller 肥大问题。
符合 SOLID 的单一职责原则 : 外部行为写在 service,没写在 controller。
符合 SOLID 的依赖反转原则 : controller 并非直接相依于 service,而是将 service 依赖注入进 controller。
使用 PHP 写的逻辑:
如根据购买的件数,有不同的折扣,初学者常会在 controller 直接写 if...else 逻辑。
在中大型项目,会有几个问题 :
将 PHP 写的商业逻辑直接写在 controller,造成 controller 的肥大难以维护。
违反 SOLID的 单一职责原则 : 商业逻辑不应该写在 controller。
违反SOLID的单一职责原则: 若未来想要改变折扣与加总的算法,都需要改到此method,也就是说,此method 同时包含了计算折扣与计算加总的职责,因此违反SOLID 的单一职责原则。
直接写在 controller 的逻辑无法被其他 controller 使用。
比较好的方式是使用 service。
将依赖的类注入到 service。
在 service 写 PHP逻辑使用相依物件。
将 service 注入到 controller。
OrderService.php
OrderController.php
将PHP写的商业逻辑写在 service,解决 controller 肥大问题。
符合 SOLID 的单一职责原则 : 商业逻辑写在 service,没写在 controller。
符合 SOLID 的单一职责原则 : 计算折扣与计算加总价分开在不同 method,且归属于 OrderService,而非 OrderController。
符合 SOLID 的依赖反转原则 : controller 并非直接相依于 service,而是将 service依赖注入进 controller。
其他 controller 也可以重复使用此段商业逻辑。
Controller
涉及到外部行为
使用 PHP 写的逻辑
若使用了 service 辅助 controller,再搭配依赖注入与 service container,则 controller 就非常干净,能专心处理接收HTTP request,调用其他class的职责了。
Conclusion
实际上会有很多 service,须自行依照 SOLID 原则去判断是否该建立 service。
Service 使得商业逻辑从 controller 中解放,不仅更容易维护、更容易扩展、更容易重复使用,且更容易测试。
Last updated
Was this helpful?