
在现代mvc架构中,不应让控制器直接组合多个子控制器或操作多张数据表;正确的做法是将业务逻辑移至独立的服务层(service layer),由服务协调多个领域模型与数据映射器,控制器仅负责请求分发与响应组装。
在现代mvc架构中,不应让控制器直接组合多个子控制器或操作多张数据表;正确的做法是将业务逻辑移至独立的服务层(service layer),由服务协调多个领域模型与数据映射器,控制器仅负责请求分发与响应组装。
当面对电商系统中的 Order 这类聚合根(Aggregate Root)时——它天然关联 orders、order_items、order_sellers、order_buyer 等多张表——许多初学者会本能地尝试创建一个“全能型” OrderController,内部持有一堆其他控制器实例(如 OrderInfoController、OrderItemController),再在 create() 或 get() 中依次调用它们。这种设计看似直观,实则严重违反了单一职责原则与关注点分离(SoC),并导致控制器承担了本应属于业务逻辑层的协调职责。
✅ 正确架构:分层清晰,各司其职
- Controller(控制器):仅接收 HTTP 请求,解析输入参数,调用对应服务方法,并返回响应对象(如 ResponseInterface)。它不处理数据库、不执行 JOIN、不管理事务边界。
-
Service(服务层):作为业务用例的核心载体(如 OrderService),封装跨多个模型的完整业务流程。例如 createOrder(array $data) 方法内部可:
- 创建 Order 实体;
- 批量创建并关联 OrderItem 实体;
- 关联 Buyer 和 Seller 身份;
- 在单个数据库事务中持久化全部变更。
- Domain Model(领域模型):定义业务概念(如 Order、OrderItem 类),包含业务规则与状态约束,但完全不依赖数据库或框架。
- Data Mapper / Repository(数据映射器):唯一知晓 SQL 与数据库细节的组件。它可通过 JOIN 查询一次性加载订单全貌,也可分别映射各实体——实现对上层透明。
? 关键实践示例:订单创建服务
以下是一个符合上述分层思想的 OrderService 核心逻辑示意(使用伪代码强调结构):
<?php
namespace App\Service;
use App\Domain\Model\Order;
use App\Domain\Model\OrderItem;
use App\Domain\Model\Buyer;
use App\Domain\Model\Seller;
use App\Infrastructure\Repository\OrderRepository;
use App\Infrastructure\Repository\OrderItemRepository;
use App\Infrastructure\Repository\BuyerRepository;
use App\Infrastructure\Repository\SellerRepository;
use PDO;
class OrderService
{
public function __construct(
private OrderRepository $orderRepo,
private OrderItemRepository $itemRepo,
private BuyerRepository $buyerRepo,
private SellerRepository $sellerRepo,
private PDO $pdo
) {}
public function createOrder(array $input): Order
{
// 1. 开启事务(确保一致性)
$this->pdo->beginTransaction();
try {
// 2. 创建领域对象(纯内存操作)
$order = new Order($input['order_number'], new \DateTime());
$buyer = $this->buyerRepo->findById($input['buyer_id']);
$seller = $this->sellerRepo->findById($input['seller_id']);
// 3. 构建聚合关系
foreach ($input['items'] as $itemData) {
$item = new OrderItem($itemData['product_id'], $itemData['quantity']);
$order->addItem($item);
}
// 4. 持久化整个聚合(由各 Repository 协同完成)
$this->orderRepo->save($order);
$this->itemRepo->saveAll($order->getItems());
$this->pdo->commit();
return $order;
} catch (\Throwable $e) {
$this->pdo->rollback();
throw $e;
}
}
}⚠️ 重要注意事项
- 控制器绝不持有其他控制器:OrderController 应只依赖 OrderService,而非 OrderItemController 等。控制器间耦合会导致测试困难、职责混乱与循环依赖风险。
- 避免“表对控制器”陷阱:OrderItemController 并非必须存在——若 OrderItem 仅作为 Order 的内聚子项(无独立生命周期),则其 CRUD 应完全由 OrderService 封装,对外暴露统一的订单接口。
- JOIN 是数据映射器的事,不是控制器的事:OrderRepository::findWithDetails(int $id) 可在内部执行 SELECT ... FROM orders JOIN order_items ...,并将结果组装为富领域对象,上层服务与控制器完全无感。
- 领域模型 ≠ 数据表:Order 类不必与 orders 表字段一一对应;它可以包含计算属性(如 getTotalAmount())、业务方法(如 cancelIfNotShipped()),这才是 MVC 中 “M” 的真正价值。
? 总结
构建复杂业务控制器的正解,不是横向拼接多个控制器,而是纵向深化分层:将跨模型的协调逻辑下沉至服务层,让控制器回归“请求路由器”的本质角色。这不仅提升可测试性(可单独单元测试 OrderService)、可维护性(业务规则集中一处),更使系统具备应对未来变化的弹性——例如将 MySQL 切换为 MongoDB 时,只需重写 Data Mapper 层,Controller 与 Service 无需修改。真正的 MVC 强大之处,正在于这种清晰的契约与隔离。










