Какие паттерны проектирования знаете для организации слоя работы с базой данных?

Ответ

В PHP-экосистеме распространены три основных паттерна для работы с БД, и я применял каждый в зависимости от контекста проекта:

1. Active Record Использовал в Laravel с Eloquent. Объект модели инкапсулирует и данные, и поведение для работы с ними. Паттерн прост для CRUD-операций.

// Модель User наследует IlluminateDatabaseEloquentModel
$user = new User();
$user->name = 'John';
$user->email = 'john@example.com';
$user->save(); // Выполняет INSERT

$foundUser = User::find(1); // Выполняет SELECT

Плюсы: Быстрое прототипирование, интуитивно понятный синтаксис. Минусы: Нарушает принцип единой ответственности (SRP), модель знает о БД, что усложняет тестирование.

2. Data Mapper Работал с ним через Doctrine ORM. Здесь модель (сущность) — это простой POPO (Plain Old PHP Object), а за отображение её состояния в БД отвечает отдельный класс-маппер.

// Сущность — простой класс
class User {
    private ?int $id = null;
    private string $name;
    // ... геттеры и сеттеры
}

// Маппер (в Doctrine это скрыто) отвечает за сохранение/загрузку
$entityManager->persist($user);
$entityManager->flush();

Плюсы: Чистая модель, независимая от слоя хранения. Минусы: Более высокая сложность и порог входа.

3. Repository Часто комбинирую его с Active Record или Data Mapper. Репозиторий абстрагирует все запросы к конкретной сущности, предоставляя коллекционный интерфейс.

class UserRepository {
    public function __construct(private User $model) {}

    public function findById(int $id): ?User {
        return $this->model->find($id);
    }
    public function findByEmail(string $email): ?User {
        return $this->model->where('email', $email)->first();
    }
}
// В сервисе
$user = $userRepository->findByEmail('test@example.com');

Плюсы: Централизация логики запросов, упрощение тестирования и смены реализации. Минусы: Добавляет слой абстракции, который может быть избыточным для простых задач.

На больших проектах предпочитаю комбинацию Data Mapper + Repository, а для средних и малых — Active Record с репозиториями для сложных запросов.

Ответ 18+ 🔞

Давай разберём эту тему, а то тут, бля, как будто на дворе 2002-й год, и все спорят, какой хуй в пальто лучше. В PHP-экосистеме, ёпта, три основных подхода к базе данных, и я каждый из них на своей шкуре прочувствовал, так что доверия ебать ноль к тем, кто говорит, что есть только один правильный путь.

1. Active Record Применял в Laravel с этим самым Eloquent. Суть простая: твой объект модели — это и данные, и методы для работы с ними в одном флаконе. Для CRUD-операций — просто сказка, не надо мозг выносить.

// Модель User наследует IlluminateDatabaseEloquentModel
$user = new User();
$user->name = 'John';
$user->email = 'john@example.com';
$user->save(); // Выполняет INSERT

$foundUser = User::find(1); // Выполняет SELECT

Плюсы: Быстро пишешь, быстро получаешь результат, синтаксис понятный даже мартышлюшке. Минусы: Но тут, сука, нарушается принцип единой ответственности. Модель знает о базе всё, и когда начинаешь тесты писать — волнение ебать, потому что всё завязано на БД. Получается пиздопроебибна архитектура, если не контролировать.

2. Data Mapper С ним работал через Doctrine ORM. Тут уже интереснее: модель — это просто POPO (Plain Old PHP Object), обычный класс, а за то, чтобы его запихнуть в базу или достать оттуда, отвечает отдельный маппер. Это как разделить мух и котлеты.

// Сущность — простой класс, без намёка на базу
class User {
    private ?int $id = null;
    private string $name;
    // ... геттеры и сеттеры
}

// А маппер (в Doctrine это скрыто под капотом) делает всю грязную работу
$entityManager->persist($user);
$entityManager->flush();

Плюсы: Модель чистая, независимая, её можно тестировать в изоляции, не дергая базу. Минусы: Но сложность, бля, возрастает овердохуища. Порог входа выше, и иногда кажется, что ты настраиваешь космический корабль, чтобы яйцо сварить.

3. Repository Его часто комбинирую с предыдущими двумя. Репозиторий — это такая хитрая жопа, которая абстрагирует все запросы к одной сущности. Вместо того чтобы размазывать where по всему коду, ты держишь всё в одном месте.

class UserRepository {
    public function __construct(private User $model) {}

    public function findById(int $id): ?User {
        return $this->model->find($id);
    }
    public function findByEmail(string $email): ?User {
        return $this->model->where('email', $email)->first();
    }
}
// А в сервисе уже работаешь с репозиторием
$user = $userRepository->findByEmail('test@example.com');

Плюсы: Логика запросов централизована, тестировать проще, и если завтра надо сменить реализацию — ты не обосрёшься. Минусы: Добавляет ещё один слой, который для простых задач выглядит как мартышкин труд. Иногда это overengineering в чистом виде.

Итог, ёпта: на больших, серьёзных проектах, где терпения ноль ебать на поддержку, я предпочитаю Data Mapper + Repository. А для средних и мелких, где надо быстро и без геморроя — Active Record, но с репозиториями для сложных запросов, чтобы не превратить код в свалку. Выбор, в общем, зависит от того, готов ли ты сейчас тратить время, чтобы потом не охуеть от legacy-кода.