门面模式解决复杂子系统调用耦合问题,通过统一入口封装多类协作流程;需依赖注入、动词命名、仅编排不封装逻辑;适用于跨域协作频繁场景,单类或无关联模块不应滥用。

门面模式不是“语法糖”,也不是 Laravel 专属的魔法,它就是一个专门给复杂子系统装上统一操作按钮的设计套路——你不用知道后台调了几个类、连了几个数据库、发了几条消息,只要对 OrderFacade::place() 按一下,事情就办妥了。
门面模式到底解决什么问题?
当你的 PHP 项目里出现这类信号,就该考虑加个门面了:
- 一个业务动作(比如「用户下单」)要手动 new
User、Cart、Inventory、Payment、Notification五个类再串起来调用 - 新同事看注册流程,得翻 7 个文件才能搞清数据流向
- 测试时 mock 对象越来越多,
Mockery::mock(InventoryService::class)都快写成一行诗了 - 想把支付模块从支付宝换成微信?改完
Payment类,发现OrderController里还藏着三处直接调用
门面不改变子系统本身,只在它们前面立一块操作面板——把分散的入口收拢,把耦合的依赖隔离。
怎么写一个靠谱的门面类?关键三点
别照抄示例里的空壳 Facade 类,真实项目中容易踩这些坑:
立即学习“PHP免费学习笔记(深入)”;
-
别在构造函数里硬 new 子系统:用依赖注入或服务容器获取实例,否则无法单元测试、无法换实现。正确写法是
public function __construct(InventoryService $inventory, PaymentGateway $payment) -
方法命名要动词开头、语义完整:用
placeOrder(),别用doIt()或handle();返回值尽量统一(如总返回OrderResult对象),别一半 returntrue、一半 throwException - 不封装逻辑,只编排调用:门面里不该有 if-else 业务规则、不该算库存扣减公式、不该拼接短信模板——那些属于子系统职责。门面只负责“先调 A,成功再调 B,失败就回滚 C”
class OrderFacade
{
public function __construct(
private InventoryService $inventory,
private PaymentGateway $payment,
private NotificationService $notify
) {}
public function placeOrder(array $data): OrderResult
{
$order = $this->inventory->reserve($data['items']);
$payment = $this->payment->charge($order->total);
$this->notify->sendSuccess($order->id);
return new OrderResult($order->id, $payment->ref);
}
}
什么时候不该用门面?
门面不是万能胶,滥用反而添乱:
- 子系统只有 1 个类、2 个方法,比如
ConfigReader::get('db.host')—— 直接用,加门面纯属套娃 - 多个子系统之间根本没协作关系(比如日志和缓存),硬凑成
SystemFacade::logAndCache(),违背“单一职责” - 团队还没形成子系统边界意识,各模块代码仍高度交织 —— 先拆接口、再定义契约、最后加门面,顺序不能反
真正值得门面化的,是那些跨域协作频繁、变更频率不一、且外部调用点分散的场景,比如订单履约链路、用户生命周期管理、第三方对接聚合层。
门面真正的价值不在“写出来”,而在“谁都不再需要绕过它”——一旦所有控制器、命令、事件监听器都只认 OrderFacade,你就拿到了重构自由度的第一把钥匙。











