---1. MVC / MVP / MVVM MVC — 最基础的分层 esc to interrupt 大白话: 把代码分三块,各管各的。 用户请求 → Controller(交通警察) ↓ Model(数据库操作) ↓ View(返回给用户看的) // Hyperf MVC class OrderController{publicfunctionshow(int$id): ResponseInterface{$order=Order::find($id);// Modelreturn$this->response->json($order);// View(JSON)}}问题: 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{$dto=OrderCreateDTO::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{$order=Order::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{$cmd=new 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{$model=OrderModel::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{$order=new self();$order->status=OrderStatus::PENDING;$order->items=$items;$order->amount=self::calculateAmount($items);return$order;}publicfunctionpay(TransactionId$txId): void{if(!$this->status->isPending()){throw new\DomainException('只有待支付订单才能支付');}$this->status=OrderStatus::PAID;}publicfunctiontotalAmount(): Money{return$this->amount;}}// 应用层:编排流程,调用领域对象 class PlaceOrderUseCase{publicfunction__construct(private OrderRepositoryInterface$orders, private PaymentServiceInterface$payment,){}publicfunctionexecute(PlaceOrderRequest$request): PlaceOrderResponse{$order=Order::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{$req=new 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 值对象 // 实体:有唯一ID,ID相同就是同一个东西 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->status=OrderStatus::PENDING;}privatefunctionrecalculateTotal(): void{$this->totalAmount=array_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){1=>ShipmentStatus::IN_TRANSIT,2=>ShipmentStatus::DELIVERING,3=>ShipmentStatus::DELIVERED, default=>ShipmentStatus::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 + 限界上下文 + 六边形/整洁架构 不要一上来就用最复杂的,过度设计比设计不足更难受。hyperf 应用架构方案大全
张小明
前端开发工程师
为什么字节/阿里的AI测试团队都在招“Skill工程师”?
目录一、招聘JD已经变了,但很多人还没看懂变化二、Skill工程师到底在解决什么问题三、Skill工程师的核心能力拆解四、三种模式对比:传统测试、AI测试工程师、Skill工程师五、行业对比:Claude Code / Cursor / OpenClaw的Skill实践六、Skill工…
如何在Windows上使用OpenArk彻底清理隐藏的Rootkit威胁?
如何在Windows上使用OpenArk彻底清理隐藏的Rootkit威胁? 【免费下载链接】OpenArk The Next Generation of Anti-Rookit(ARK) tool for Windows. 项目地址: https://gitcode.com/GitHub_Trending/op/OpenArk 你是否曾怀疑自己的Windows系统被Rootkit悄悄入侵…
Spring Security配置踩坑大全:从CSRF禁用、密码加密到自定义登录页,一次讲清
Spring Security实战避坑指南:CSRF、密码加密与登录页定制深度解析 1. 当POST请求遭遇403:CSRF防护的精准控制策略 那个令人抓狂的403错误页面,可能是大多数开发者首次接触Spring Security时最深刻的记忆。明明在Postman测试正常的API接口&…
TikTokDownload技术方案:解决抖音内容去水印与批量下载的创新方法
TikTokDownload技术方案:解决抖音内容去水印与批量下载的创新方法 【免费下载链接】TikTokDownload 抖音去水印批量下载用户主页作品、喜欢、收藏、图文、音频 项目地址: https://gitcode.com/gh_mirrors/ti/TikTokDownload 在数字内容创作领域,专…
别再手动调格式了!用LaTeX的xcolor宏包5分钟搞定论文重点标注(附完整代码)
别再手动调格式了!用LaTeX的xcolor宏包5分钟搞定论文重点标注(附完整代码) 学术写作中,视觉标注是提升论文可读性的关键技巧。想象一下:当审稿人翻开你的论文,那些用黄色高亮标记的核心发现、红色边框圈出的…
高互动投票制作平台,支持音视频+多客户管理系统
温馨提示:文末有资源获取方式近年来,微信生态中的互动投票依旧是最有效的用户增长方式之一。最近体验了一款全新的投票源码系统V9.8版本,架构全面升级,功能值得一说。源码获取方式在源码闪购网。核心功能亮点多媒体投票支持&#…