Laravel核心层和基础设施层怎么分

wen PHP项目 2

Laravel核心层与基础设施层:架构边界与协同之道

目录导读

  • 引文:分层不是割裂,而是协作的契约
  • 核心层:业务逻辑的“心脏”
    • 核心层包含哪些组件?
    • 核心层如何保持纯净?
  • 基础设施层:外界的“翻译官”
    • 基础设施层的典型角色
    • 依赖倒置原则的体现
  • 两者如何分?三问法快速判断
  • 常见误区与最佳实践
  • 问答环节:开发者最常问的3个问题

引文:分层不是割裂,而是协作的契约

很多Laravel开发者做完第一个项目后,会陷入一个困惑:控制器里写数据库查询到底好不好?服务类应该放哪里? 这背后其实藏着一个架构核心问题——核心层(Core Layer)与基础设施层(Infrastructure Layer)的边界在哪里?

Laravel核心层和基础设施层怎么分

Laravel本身并没有强制目录结构,但成熟的架构分层能让你在项目膨胀时不至于“改一处崩一片”,根据Google与Bing对技术文章的索引偏好,清晰的、有实践指导的架构分析往往排名更靠前,本文将结合Martin Fowler的分层架构思想与Laravel生态特性,帮你彻底搞懂这两个层的“分”与“合”。


核心层:业务逻辑的“心脏”

核心层是与框架无关、与外部服务无关的纯业务逻辑集合,它只关心“做什么”,而不关心“怎么做”。

核心层包含哪些组件?

  • 实体(Entities):如UserOrder,它们包含业务的约束与行为(订单总额必须大于0”)。
  • 值对象(Value Objects):如EmailMoney,封装对属性的校验与运算。
  • 领域服务(Domain Services):当某个行为不属于单一实体时,如PaymentScheduler(处理订阅扣费时间计算)。
  • 仓库接口(Repository Interfaces)注意,这里只是接口定义,不是具体实现。

核心层如何保持纯净?

  • 零外部依赖:不引用Eloquent、不引用HTTP请求、不引用缓存驱动。
  • 只依赖PHP原生类与你自己定义的接口

一个错误例子:

class Order
{
    public function process()
    {
        // 直接调用Eloquent——核心层污染!
        DB::table('logs')->insert(...);
    }
}

正确做法: 核心层只定义LoggerInterface,由基础设施层去实现它。


基础设施层:外界的“翻译官”

基础设施层负责所有与外部世界的交互:数据库、文件系统、API调用、邮件发送、缓存等,它的任务是将核心层的抽象接口翻译成具体实现。

基础设施层的典型角色

  • Eloquent模型与Repository实现:让核心层通过接口访问数据,而不知晓底层是MySQL还是Redis。
  • 第三方服务封装:比如把Guzzle请求封装成SmsSenderInterface的实现。
  • HTTP控制器与路由:控制器只负责“拿到输入,调用核心层,返回响应”,不写业务逻辑。

依赖倒置原则的体现

核心层定义接口,基础设施层依赖这些接口并实现它们,这就是依赖倒置(DIP)——高层模块(核心层)不依赖低层模块(基础设施层),两者都依赖抽象。

目录结构示意(以领域驱动设计风格为例):

app/
├── Core/
│   ├── Domain/
│   │   ├── User/
│   │   │   ├── User.php (实体)
│   │   │   ├── UserRepositoryInterface.php (接口)
│   │   │   └── UserService.php (领域服务)
│   │   └── ValueObjects/
│   └── Application/ (用例/应用服务)
├── Infrastructure/
│   ├── Persistence/
│   │   └── EloquentUserRepository.php (实现)
│   ├── Payment/
│   └── Mail/
└── Http/
    ├── Controllers/
    └── Middleware/

两者如何分?三问法快速判断

当你犹豫某段代码该放哪一层时,问自己三个问题:

  1. 这段代码是否依赖Laravel的某个门面(Facade)或具体类?
    是 → 大概率属于基础设施层。
    否 → 继续问。

  2. 如果我把数据库从MySQL换成PostgreSQL,这段代码需要改吗?
    需要改 → 基础设施层。
    不需要改 → 核心层。

  3. 这段代码是否直接操作HTTP请求或响应?
    是 → 基础设施层(或HTTP层)。
    否 → 核心层。

经典边界:

  • “计算订单折扣” → 核心层(可单元测试,无外部依赖)。
  • “发送折扣邮件” → 基础设施层(依赖邮件驱动)。
  • “从请求中获取用户ID” → HTTP层(控制器或中间件)。

常见误区与最佳实践

误区1:把所有Service都塞进Infrastructure

业务复杂的Service应属于核心层,比如InvoiceService里计算税额的逻辑,就不应写在基础设施中。

误区2:核心层直接使用Laravel的Collection

Collection虽好用,但它是一个具体类,若核心层引用了它,就与Laravel耦合了,建议核心层使用原生数组自定义集合类

最佳实践:用Laravel的ServiceProvider绑定接口与实现

AppServiceProvider中:

$this->app->bind(UserRepositoryInterface::class, EloquentUserRepository::class);

这样核心层只知道接口,不知道具体实现。


问答环节:开发者最常问的3个问题

Q1:核心层可以用Laravel的Eloquent模型吗?

A: 强烈不建议,Eloquent模型直接继承了Model类,这意味着你的核心层依赖了Laravel ORM,一旦将来你想改用Doctrine或原生SQL,就得重写核心层,核心层应当面向接口编程,数据访问的实现细节交给基础设施层。

Q2:控制器到底算哪一层?

A: 控制器通常属于HTTP层(或适配器层),它是基础设施层的一部分,它的职责是:解析请求、调用应用服务(核心层)、构造响应,如果你发现控制器里写了复杂的业务计算,那就把它移到核心层。

Q3:如果项目很小,有必要分这么细吗?

A: 看规模,如果你做的是只有一个表单的简单页面,确实不必强行分层,但只要有二次开发、团队协作或计划长期维护,分层就是“前期成本,后期收益”,你可以从简单的Repository模式开始,后续逐步细化。


分层不是目的,可维护才是

核心层与基础设施层的划分,本质是让变化与稳定各归其位,核心层足够稳定,成为业务的“宪法”;基础设施层灵活多变,成为适应技术演进的“口岸”,在Laravel中践行这一原则,你的代码将更容易测试、更清晰、更经得起时间考验。

框架是用来实现你的业务,而不是让你的业务去适应框架。

抱歉,评论功能暂时关闭!