本文目录导读:

在 PHP 中设计数据层(Data Layer),核心目标是将数据访问逻辑与业务逻辑分离,提高代码的可维护性、可测试性和灵活性,通常遵循“依赖倒置”和“接口隔离”原则。
以下是一个现代、标准的 PHP 数据层分层设计方案,基于 Repository 模式 + 数据映射器模式,适用于 Laravel、Symfony 等框架或自定义架构。
整体分层结构
Project Root/
├── Http/ (控制器层)
├── Models/ (业务模型/实体)
├── Repositories/ (数据仓库层)
├── Services/ (业务逻辑层)
└── Providers/ (服务提供者)
数据层核心职责: 封装对持久化存储(MySQL、Redis、Elasticsearch 等)的增删改查操作,对外提供清晰的接口。
核心组件设计
数据实体(Entity)—— 纯业务对象
不包含任何数据库相关代码,只描述业务数据结构和行为。
// app/Entities/User.php
class User
{
private int $id;
private string $name;
private string $email;
private DateTime $createdAt;
public function __construct(string $name, string $email)
{
$this->name = $name;
$this->email = $email;
$this->createdAt = new DateTime();
}
// Getters & Setters...
public function getId(): int { return $this->id; }
public function setId(int $id): void { $this->id = $id; }
// ...
}
数据访问接口(Repository Interface)—— 定义契约
只声明操作方法,不关心具体实现。
// app/Repositories/Contracts/UserRepositoryInterface.php
interface UserRepositoryInterface
{
public function findById(int $id): ?User;
public function findByEmail(string $email): ?User;
public function save(User $user): User;
public function delete(int $id): bool;
public function findAllPaginated(int $page, int $perPage): array;
}
具体实现(如 EloquentRepository)—— 数据库交互
使用 Laravel Eloquent 或原生 SQL 实现具体数据操作。
// app/Repositories/EloquentUserRepository.php
class EloquentUserRepository implements UserRepositoryInterface
{
private UserModel $model;
public function __construct(UserModel $model)
{
$this->model = $model;
}
public function findById(int $id): ?User
{
$record = $this->model->find($id);
if (!$record) return null;
return $this->toEntity($record);
}
public function save(User $user): User
{
$data = [
'name' => $user->getName(),
'email' => $user->getEmail(),
];
if ($user->getId()) {
$this->model->where('id', $user->getId())->update($data);
} else {
$record = $this->model->create($data);
$user->setId($record->id);
}
return $user;
}
// 将 ORM 模型转换为 Entity
private function toEntity(UserModel $model): User
{
$user = new User($model->name, $model->email);
$user->setId($model->id);
$user->setCreatedAt($model->created_at);
return $user;
}
}
数据转换层(Transformer/DTO)—— 可选
当数据需要前后端分离(如 API 返回 JSON)时,使用 DTO 或 Transformer 将 Entity 转为响应格式。
// app/DTOs/UserDTO.php
class UserDTO
{
public static function fromEntity(User $user): array
{
return [
'id' => $user->getId(),
'name' => $user->getName(),
'email' => $user->getEmail(),
'created_at' => $user->getCreatedAt()->format('Y-m-d H:i:s'),
];
}
}
依赖注入与绑定
在 Laravel 中通过 ServiceProvider 绑定接口与实现:
// app/Providers/RepositoryServiceProvider.php
class RepositoryServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->bind(
UserRepositoryInterface::class,
EloquentUserRepository::class
);
}
}
其他框架(如 Symfony)通过 yaml 或 autowire 实现类似效果。
业务逻辑层调用数据层
// app/Services/UserService.php
class UserService
{
public function __construct(
private UserRepositoryInterface $userRepository
) {}
public function registerUser(string $name, string $email): User
{
// 1. 验证(可抽离到验证层)
if ($this->userRepository->findByEmail($email)) {
throw new \DomainException('Email already exists');
}
// 2. 创建实体
$user = new User($name, $email);
// 3. 持久化(通过数据层)
return $this->userRepository->save($user);
}
public function getUserProfile(int $id): ?User
{
return $this->userRepository->findById($id);
}
}
关键设计原则
| 原则 | 说明 | 例子 |
|---|---|---|
| 依赖倒置 | 高层模块不依赖低层模块,都依赖抽象 | Service 依赖 Interface,不依赖具体实现 |
| 单一职责 | 每个类只负责一件事 | Repository 只关心数据访问,不包含业务逻辑 |
| 接口隔离 | 接口尽量小而精 | 避免大而全的 CRUD 接口,按业务拆分(如 UserReadRepository, UserWriteRepository) |
| 持久化无关 | 数据层可随时替换 | 从 MySQL 切到 MongoDB 只需新增一个实现类 |
高级扩展场景
缓存层(Cache Layer)
使用 Decorator 模式包裹 Repository:
class CachedUserRepository implements UserRepositoryInterface
{
public function __construct(
private UserRepositoryInterface $decorated,
private CacheInterface $cache
) {}
public function findById(int $id): ?User
{
$key = "user_{$id}";
return $this->cache->remember($key, 3600, function () use ($id) {
return $this->decorated->findById($id);
});
}
public function save(User $user): User
{
$result = $this->decorated->save($user);
$this->cache->forget("user_{$user->getId()}");
return $result;
}
}
多种数据源支持
一个 Interface 对应多种实现:
RedisUserRepositoryElasticsearchUserRepositoryMySQLUserRepository
在 Service 中通过配置切换,不需改业务代码。
常见问题与建议
| 问题 | 解决方案 |
|---|---|
| 过度抽象 | 简单 CRUD 项目直接用 ORM(Eloquent/Doctrine)即可,无需 Repository 层 |
| 事务管理 | 在 Service 层开启数据库事务,Repository 方法应幂等 |
| 批量操作 | Repository 不应暴露 query() 或 raw(),批量接口单独定义(如 bulkInsert()) |
| 性能优化 | 对高频查询走缓存,写操作异步化,Repsitory 内部可调用 Query 优化器 |
推荐设计路径:
- 简单项目:ORM + Service 层即可,不强制分层。
- 中型项目:使用 Repository 模式 + 接口,业务逻辑与数据分离。
- 大型/多数据源项目:添加 CQRS(命令查询职责分离)、事件溯源、缓存装饰器。
设计数据层的核心是 可测性(Mock 数据层)、可替换性(切换存储引擎)和 可读性(代码职责清晰),如果团队没有复杂业务需求,保持简单(ORM 原生操作)反而是最佳实践。