Repository模式在大型PHP项目中非必需,仅当出现重复查询、多数据源、测试耦合、新老结构并存等明确痛点时才真正必要;其价值源于接口+依赖注入+运行时替换的组合,而非模式本身。

Repository 模式在大型 PHP 项目中不是“必不可少”,而是当出现明确耦合痛点时最可控的解耦手段;它和工厂模式配合使用时,真正起作用的不是模式本身,而是接口 + 依赖注入 + 运行时替换这三者的组合效果。
什么时候Repository才真有必要?
别一上来就建 app/Repositories 目录。先看业务是否已暴露以下信号:
- 同一个模型在多个 Service 中重复写
User::where(...)->with(...)->orderBy(...),且条件逻辑分散、难以复用 - 需要对接不止一种数据源(比如用户信息既查 MySQL,又同步到 Elasticsearch,还可能 fallback 到 Redis 缓存)
- 测试时发现 Service 层必须连数据库才能跑通,
phpunit启动慢、状态难隔离 - 团队正在迁移旧系统,新老数据结构并存,
User模型要同时支持 legacy_id 和 uuid 字段,但控制器/Service 不该感知这些细节
满足其中任意一条,Repository 才开始产生实际价值。否则就是提前抽象,徒增跳转层级。
Repository 接口怎么设计才不翻车?
关键不是方法多,而是方法名是否反映业务意图,而不是 SQL 动词:
立即学习“PHP免费学习笔记(深入)”;
- 避免
getUserById($id)—— 这是数据库视角,应改为findActiveUser($id)或findUserForDashboard($id) - 拒绝把 Eloquent 构建器(
Builder)直接暴露出去,比如不要写public function query(): Builder,否则调用方又绕过接口去拼 where - 复杂查询别硬塞进 Repository 方法里,改用
Criteria对象传入:$repo->search(new UserActiveCriteria())->search(new UserByRegionCriteria('sh')) - 方法返回类型统一用具体模型或集合(
User/Collection),而不是mixed或Model,PHPStan 和 IDE 补全才靠谱
工厂模式怎么配合 Repository 实例化?
工厂在这里只干一件事:根据运行时配置决定用哪个 Repository 实现,而不是封装 new 逻辑:
- 不要写静态工厂类
RepositoryFactory::make('user'),而是在服务提供者中绑定:$this->app->bind(UserRepository::class, fn() => match (config('repo.user.driver')) { 'eloquent' => new EloquentUserRepository(), 'api' => new ApiUserRepository($this->app->get(HttpClient::class)), default => throw new InvalidArgumentException(...) }); - 工厂类本身不应持有配置判断逻辑,而是由容器或配置驱动——Laravel 的
config()+ 服务绑定才是自然解法 - 如果真要手写工厂(比如集成第三方 SDK),确保它接受依赖(如
HttpClient、CacheInterface)通过构造注入,而不是在方法里new HttpClient() - 别让工厂承担 Repository 的缓存逻辑或重试策略,那是 Repository 实现内部的事;工厂只负责“选谁”,不负责“怎么用”
最容易被忽略的是:Repository 接口的方法签名一旦发布,就几乎无法安全修改。新增方法可以,但改参数、删方法、变返回类型都会导致所有实现类和调用方集体报错。所以第一次定义接口时,宁可少,不可滥。



















