Service

使用 Service 辅助 Controller

若将商业逻辑都写在 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?