---1. MVC / MVP / MVVM MVC — 最基础的分层 esc to interrupt 大白话 把代码分三块各管各的。 用户请求 → Controller交通警察 ↓ Model数据库操作 ↓ View返回给用户看的 // Hyperf MVC class OrderController{publicfunctionshow(int$id): ResponseInterface{$orderOrder::find($id);// Modelreturn$this-response-json($order);// ViewJSON}}问题 Controller 越写越胖业务逻辑全堆里面难维护。 --- MVP — Controller 瘦身版 大白话 Controller 只管接收请求业务逻辑全扔给 Presenter。 Controller → Presenter干活的→ Model ↓ 返回数据给 Controller 在 Hyperf 里基本等同于加了 Service 层不常单独提。 --- MVVM — 前端概念 大白话 数据变了视图自动更新视图变了数据自动同步。Vue/React 的核心思想后端不涉及。 ---2. 分层架构 — 最实用的日常架构 大白话 每层只做自己的事上层调下层下层不知道上层。 HTTP请求 ↓ Controller → 只管接收请求、参数校验、返回响应 ↓ Service → 只管业务逻辑 ↓ Repository → 只管数据库操作 ↓ Model/DB // Controller只管进出不写业务 class OrderController{publicfunction__construct(private OrderService$service){}publicfunctionstore(Request$request): ResponseInterface{$dtoOrderCreateDTO::fromRequest($request);$order$this-service-createOrder($dto);return$this-response-json($order);}}// Service只管业务逻辑不管数据怎么存 class OrderService{publicfunction__construct(private OrderRepository$orderRepo, private ProductRepository$productRepo, private EventDispatcherInterface$dispatcher,){}publicfunctioncreateOrder(OrderCreateDTO$dto): Order{$product$this-productRepo-findOrFail($dto-productId);if($product-stock$dto-quantity){throw new InsufficientStockException();}$order$this-orderRepo-create([user_id$dto-userId,product_id$dto-productId,amount$product-price *$dto-quantity,]);$this-dispatcher-dispatch(new OrderCreated($order));return$order;}}// Repository只管数据库不管业务 class OrderRepository{publicfunctioncreate(array$data): Order{returnOrder::create($data);}publicfunctionfindByUser(int$userId): Collection{returnOrder::where(user_id,$userId)-orderByDesc(created_at)-get();}}app/ ├── Controller/ │ └── OrderController.php ├── Service/ │ └── OrderService.php ├── Repository/ │ └── OrderRepository.php ├── Model/ │ └── Order.php └── DTO/ └── OrderCreateDTO.php 这套架构适合90% 的业务系统先把这个搞熟。 ---3. 六边形架构 — 核心与外部隔离 大白话 业务核心放中间所有外部的东西HTTP、数据库、队列、第三方API都通过插口接进来核心不依赖任何外部。 生活比喻 手机核心功能是打电话发短信充电口、耳机口、SIM卡槽都是插口换个充电器不影响手机功能。 HTTP请求 ↓[输入端口/适配器]↓ ┌──────────────┐ │ 业务核心 │ ← 不依赖任何框架和外部 │(纯PHP逻辑)│ └──────────────┘ ↓[输出端口/适配器]↓ 数据库 / Redis / 第三方API // 核心纯业务逻辑不依赖 Hyperf、不依赖 MySQL // 只依赖接口端口 // 输出端口接口 interface OrderRepositoryPort{publicfunctionsave(Order$order): void;publicfunctionfindById(int$id): ?Order;}interface PaymentPort{publicfunctioncharge(int$amount, string$currency): PaymentResult;}// 业务核心只依赖接口不知道外面是 MySQL 还是 MongoDB class OrderApplicationService{publicfunction__construct(private OrderRepositoryPort$orderRepo, // 接口不是具体实现 private PaymentPort$payment,){}publicfunctionplaceOrder(PlaceOrderCommand$cmd): OrderId{$orderOrder::create($cmd-userId,$cmd-items);$result$this-payment-charge($order-totalAmount(),CNY);if(!$result-isSuccess()){throw new PaymentFailedException($result-message());}$order-markAsPaid($result-transactionId());$this-orderRepo-save($order);return$order-id();}}// 输入适配器HTTP → 核心 class OrderController{publicfunctionstore(Request$request): ResponseInterface{$cmdnew PlaceOrderCommand(userId:$request-input(user_id), items:$request-input(items),);$orderId$this-orderService-placeOrder($cmd);return$this-response-json([order_id$orderId]);}}// 输出适配器核心 → MySQL class MysqlOrderRepository implements OrderRepositoryPort{publicfunctionsave(Order$order): void{OrderModel::updateOrCreate([id$order-id()-value()],$order-toArray());}publicfunctionfindById(int$id): ?Order{$modelOrderModel::find($id);return$model? Order::fromArray($model-toArray()):null;}}// 输出适配器核心 → 支付宝 class AlipayAdapter implements PaymentPort{publicfunctioncharge(int$amount, string$currency): PaymentResult{$result$this-alipayClient-pay($amount);returnnew PaymentResult($result[code]SUCCESS,$result[trade_no]);}}app/ ├── Core/ ← 业务核心纯PHP无框架依赖 │ ├── Domain/ │ │ └── Order.php │ ├── Port/ ← 端口接口 │ │ ├── OrderRepositoryPort.php │ │ └── PaymentPort.php │ └── Application/ │ └── OrderApplicationService.php └── Adapter/ ← 适配器具体实现 ├── Http/ │ └── OrderController.php ├── Persistence/ │ └── MysqlOrderRepository.php └── Payment/ └── AlipayAdapter.php 好处 换数据库、换支付方式只改适配器核心业务代码一行不动。 ---4. 整洁架构 — 依赖只能向内 大白话 洋葱结构外层依赖内层内层完全不知道外层存在。越往里越稳定越往外越容易变。 ┌─────────────────────────┐ │ 框架/数据库/UI最外 │ │ ┌───────────────────┐ │ │ │ 接口适配层 │ │ │ │ ┌─────────────┐ │ │ │ │ │ 应用层 │ │ │ │ │ │ ┌─────────┐ │ │ │ │ │ │ │ 领域层 │ │ │ │ ← 最内层最稳定 │ │ │ └─────────┘ │ │ │ │ │ └─────────────┘ │ │ │ └───────────────────┘ │ └─────────────────────────┘ 依赖方向只能从外指向内 → // 领域层最内层纯业务规则零依赖 class Order{private OrderStatus$status;private Money$amount;private array$items;public staticfunctioncreate(UserId$userId, array$items): self{$ordernew self();$order-statusOrderStatus::PENDING;$order-items$items;$order-amountself::calculateAmount($items);return$order;}publicfunctionpay(TransactionId$txId): void{if(!$this-status-isPending()){throw new\DomainException(只有待支付订单才能支付);}$this-statusOrderStatus::PAID;}publicfunctiontotalAmount(): Money{return$this-amount;}}// 应用层编排流程调用领域对象 class PlaceOrderUseCase{publicfunction__construct(private OrderRepositoryInterface$orders, private PaymentServiceInterface$payment,){}publicfunctionexecute(PlaceOrderRequest$request): PlaceOrderResponse{$orderOrder::create($request-userId,$request-items);$result$this-payment-charge($order-totalAmount());$order-pay($result-transactionId());$this-orders-save($order);returnnew PlaceOrderResponse($order-id());}}// 接口适配层转换外部格式 class OrderController{publicfunctionstore(Request$request): ResponseInterface{$reqnew PlaceOrderRequest($request-all());$response$this-placeOrderUseCase-execute($req);return$this-response-json([id$response-orderId]);}}// 框架层最外层具体技术实现 class EloquentOrderRepository implements OrderRepositoryInterface{publicfunctionsave(Order$order): void{OrderModel::create($order-toArray());}}app/ ├── Domain/ ← 领域层最稳定零依赖 │ ├── Order.php │ ├── Money.php │ └── OrderStatus.php ├── Application/ ← 应用层依赖领域层 │ ├── UseCase/ │ │ └── PlaceOrderUseCase.php │ └── DTO/ │ ├── PlaceOrderRequest.php │ └── PlaceOrderResponse.php ├── Interface/ ← 接口适配层依赖应用层 │ └── Http/ │ └── OrderController.php └── Infrastructure/ ← 基础设施层最外层 ├── Persistence/ │ └── EloquentOrderRepository.php └── Payment/ └── AlipayPaymentService.php ---5. DDD — 用业务语言写代码 大白话 代码结构跟着业务走开发和业务说同一种语言代码里的词和业务里的词一模一样。 --- 实体 vs 值对象 // 实体有唯一IDID相同就是同一个东西 class User{publicfunction__construct(private UserId$id, // 有ID private string$name, private Email$email,){}publicfunctionequals(User$other): bool{return$this-id-equals($other-id);// 用ID判断是否同一个}}// 值对象没有ID值相同就是同一个东西不可变 class Money{publicfunction__construct(privatereadonlyint$amount, privatereadonlystring$currency,){}publicfunctionadd(Money$other): self{if($this-currency!$other-currency){throw new\DomainException(货币类型不同不能相加);}// 返回新对象不修改自身不可变returnnew self($this-amount $other-amount,$this-currency);}publicfunctionequals(Money$other): bool{// 值相同就相等不看是不是同一个对象return$this-amount$other-amount$this-currency$other-currency;}}// 值对象邮箱 class Email{publicfunction__construct(privatereadonlystring$value){if(!filter_var($value, FILTER_VALIDATE_EMAIL)){throw new\InvalidArgumentException(邮箱格式不对: {$value});}}publicfunctionvalue(): string{return$this-value;}}--- 聚合根 — 一组对象的老大 大白话 一堆相关对象打包成一组只能通过老大聚合根操作外部不能直接改里面的小弟。 // Order 是聚合根OrderItem 是小弟 class Order{private OrderId$id;private OrderStatus$status;private array$items[];// OrderItem 集合 private Money$totalAmount;// 外部只能通过聚合根的方法操作 publicfunctionaddItem(ProductId$productId, int$qty, Money$price): void{if($this-status!OrderStatus::DRAFT){throw new\DomainException(只有草稿状态才能加商品);}$this-items[]new OrderItem($productId,$qty,$price);$this-recalculateTotal();// 内部自己维护一致性}publicfunctionsubmit(): void{if(empty($this-items)){throw new\DomainException(订单没有商品);}$this-statusOrderStatus::PENDING;}privatefunctionrecalculateTotal(): void{$this-totalAmountarray_reduce($this-items, fn(Money$carry, OrderItem$item)$carry-add($item-subtotal()), new Money(0,CNY));}}// OrderItem 不能被外部直接 new 和修改 // 只能通过 Order::addItem()操作 class OrderItem{publicfunction__construct(private ProductId$productId, private int$quantity, private Money$unitPrice,){}publicfunctionsubtotal(): Money{returnnew Money($this-unitPrice-amount()*$this-quantity,CNY);}}--- 领域服务 vs 应用服务 // 领域服务业务规则但不属于某个实体 // 场景转账涉及两个账户放哪个账户都不合适 class TransferDomainService{publicfunctiontransfer(Account$from, Account$to, Money$amount): void{if($from-balance()-lessThan($amount)){throw new InsufficientBalanceException();}$from-debit($amount);$to-credit($amount);}}// 应用服务编排流程调用领域对象和领域服务 // 不包含业务规则只负责做什么的流程 class TransferApplicationService{publicfunction__construct(private AccountRepository$accounts, private TransferDomainService$transferService, private EventDispatcherInterface$dispatcher,){}publicfunctiontransfer(TransferCommand$cmd): void{//1. 取数据$from$this-accounts-findOrFail($cmd-fromId);$to$this-accounts-findOrFail($cmd-toId);//2. 调领域服务执行业务规则$this-transferService-transfer($from,$to, new Money($cmd-amount,CNY));//3. 保存$this-accounts-save($from);$this-accounts-save($to);//4. 发事件$this-dispatcher-dispatch(new TransferCompleted($cmd));}}区别一句话 - 领域服务业务规则转账规则、定价规则 - 应用服务流程编排取数据→执行→保存→通知 --- 限界上下文 — 划清地盘 大白话 同一个词在不同业务场景下意思不一样划清边界各自定义自己的模型。 电商系统里的用户 ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ 用户上下文 │ │ 订单上下文 │ │ 营销上下文 │ │ │ │ │ │ │ │ User{│ │ Customer{│ │ Member{│ │id│ │id│ │id│ │ name │ │ name │ │ level │ │ email │ │ address │ │ points │ │ password │ │ phone │ │ coupons │ │}│ │}│ │}│ │ │ │ │ │ │ │ 关注登录认证 │ │ 关注收货信息 │ │ 关注积分优惠 │ └─────────────────┘ └─────────────────┘ └─────────────────┘ // 目录结构按限界上下文划分 app/ ├── UserContext/ // 用户上下文 │ ├── Domain/ │ │ └── User.php // 用户关注认证 │ └── Application/ │ └── AuthService.php ├── OrderContext/ // 订单上下文 │ ├── Domain/ │ │ └── Customer.php // 同一个人但关注收货信息 │ └── Application/ │ └── OrderService.php └── MarketingContext/ // 营销上下文 ├── Domain/ │ └── Member.php // 同一个人但关注积分等级 └── Application/ └── CouponService.php --- 防腐层 — 隔离外部污染 大白话 接入第三方系统时不让外部的数据结构污染自己的领域模型加一层翻译。 // 场景接入第三方物流系统它的数据结构很丑 // 第三方物流返回的数据外部模型不能控制 class SFExpressResponse{public string$mailNo;// 运单号 public string$acceptTime;// 揽收时间字符串格式 public int$routeStatus;//1在途2派送3已签收 public array$routeList;// 路由列表}// 我们自己的领域模型干净的 class Shipment{publicfunction__construct(publicreadonlystring$trackingNo, publicreadonlyShipmentStatus$status, publicreadonly\DateTimeImmutable$acceptedAt, publicreadonlyarray$routes,){}}// 防腐层翻译外部模型 → 内部模型 class SFExpressAntiCorruptionLayer{publicfunctiontoShipment(SFExpressResponse$response): Shipment{returnnew Shipment(trackingNo:$response-mailNo, status:$this-translateStatus($response-routeStatus), acceptedAt: new\DateTimeImmutable($response-acceptTime), routes:$this-translateRoutes($response-routeList),);}privatefunctiontranslateStatus(int$sfStatus): ShipmentStatus{returnmatch($sfStatus){1ShipmentStatus::IN_TRANSIT,2ShipmentStatus::DELIVERING,3ShipmentStatus::DELIVERED, defaultShipmentStatus::UNKNOWN,};}privatefunctiontranslateRoutes(array$sfRoutes): array{returnarray_map(fn($r)new RouteRecord(location:$r[acceptAddress], time: new\DateTimeImmutable($r[acceptTime]), remark:$r[remark],),$sfRoutes);}}// 业务代码只用干净的内部模型不知道顺丰的数据结构 class ShipmentService{publicfunction__construct(private SFExpressClient$sfClient, private SFExpressAntiCorruptionLayer$acl,){}publicfunctiontrack(string$trackingNo): Shipment{$sfResponse$this-sfClient-query($trackingNo);return$this-acl-toShipment($sfResponse);// 翻译一下}}--- 五种架构对比 选型建议 ┌────────────┬──────────────────────┬────────┐ │ 架构 │ 适合场景 │ 复杂度 │ ├────────────┼──────────────────────┼────────┤ │ MVC │ 简单CRUD │ ★ │ ├────────────┼──────────────────────┼────────┤ │ 分层架构 │90%的业务系统 │ ★★ │ ├────────────┼──────────────────────┼────────┤ │ 六边形架构 │ 需要频繁换外部依赖 │ ★★★ │ ├────────────┼──────────────────────┼────────┤ │ 整洁架构 │ 大型系统、长期维护 │ ★★★★ │ ├────────────┼──────────────────────┼────────┤ │ DDD │ 业务复杂、多团队协作 │ ★★★★★ │ └────────────┴──────────────────────┴────────┘ 选型原则 团队3人以下、业务简单 → 分层架构够用 团队5人以上、业务中等复杂 → 分层架构 部分DDD概念实体、值对象 团队10人以上、业务复杂、多个子系统 → DDD 限界上下文 六边形/整洁架构 不要一上来就用最复杂的过度设计比设计不足更难受。