Есть вещи, которые я использую почти в каждом проекте. Репозитории - одна из них. Порой приходится жертвовать этим паттернов в угоду скорости реализации, но при первой возможности я сразу его внедряю. Люблю этот паттерн. Он создает прослойку между твоим кодом и хранилищем данных. Будь то MySQL, Redis, API или даже просто файл.
Что даёт репозиторий
Простой пример. Контроллеры и сервисы не знают, где лежат данные. В БД, в Redis, во внешнем API. Им всё равно.
// Сервис не знает, откуда берутся пользователи
class RegisterService
{
public function __construct(
private UserRepositoryInterface $users
) {}
public function register(string $email): void
{
$user = $this->users->findByEmail($email);
// ...
}
}
Создаем сервис, определяем в качестве аргумента UserRepositoryInterface и у нас теперь есть абстракция, не привязанная к конкретному хранилищу данных. Теперь нам нужно реализовать репозиторий, подходящий под этот интерфейс.
// Так
class MysqlUserRepository implements UserRepositoryInterface { ... }
// Или так
class RedisUserRepository implements UserRepositoryInterface { ... }
Решили переехать с MySQL на PostgreSQL? Или добавить кэш через Redis? Меняешь реализацию репозитория в одном месте. Сервисы не трогаешь.
Использование репозиториев позволяет:
- Легко менять хранилище
- Упрощаешь тестирование
- Соблюдаешь единую ответственность
Важные правила
1. Репозиторий всегда делается под интерфейс
Никогда не привязывайся к конкретной реализации. Код должен работать с UserRepositoryInterface, а не с MysqlUserRepository
// Плохо - привязка к конкретной реализации
class RegisterService
{
public function __construct(
private MysqlUserRepository $users
) {}
}
// Хорошо - работа через интерфейс
class RegisterService
{
public function __construct(
private UserRepositoryInterface $users
) {}
}
2. Только стандартные операции с сущностью
interface UserRepositoryInterface
{
public function findById(int $id): ?User;
public function findByEmail(string $email): ?User;
public function findAll(): array;
public function save(User $user): void;
public function delete(User $user): void;
public function existsById(int $id): bool;
}
❌ Не стоит (слишком специфичные запросы)
interface UserRepositoryInterface
{
// Плохо — бизнес-логика в репозитории
public function findActiveUsersWithOrders(): array;
public function getTop10(): array;
public function findUsersWhoBoughtProduct(int $productId): array;
}
🤔 Как быть, если нужен getTop10?
Рекомендую добавить метод с критериями. Вот как это может выглядеть.
interface UserRepositoryInterface
{
public function findByCriteria(
array $criteria,
array $orderBy = [],
int $limit = 100
): array;
}
// В сервисе
class UserService
{
public function getTop10(): array
{
return $this->users->findByCriteria(
criteria: ['status' => 'active'],
orderBy: ['rating' => 'DESC'],
limit: 10
);
}
}
Использование критериев позволяет использовать один метод вместо условных 10. Просто меняй критерии и получай нужные данные.
Итог
Репозитории - это не серебряная пуля. Но для большинства проектов они делают код чище и гибче.
| Что даёт репозиторий | Как это помогает |
|---|---|
| Отделение данных от логики | Легко менять хранилище |
| Единое место для запросов | Не дублируешь код |
| Упрощение тестов | Моки вместо реальной БД |
| Чёткая ответственность | Код становится понятнее |
Обсудить пост:
- Telegram → https://t.me/buriy_dev
- ВКонтакте → https://vk.com/buriy_dev
- Max → https://max.ru/id616507661604_biz
🔥 И не забудь подписаться :)