Ответ
Да, создавал специализированные сервисы для работы с БД в нескольких сценариях:
- Сложная бизнес-логика, которую неудобно размещать в репозиториях
- Транзакционные операции, затрагивающие несколько сущностей
- Оптимизированные запросы, когда ORM генерирует неэффективный SQL
- Миграции данных и пакетная обработка
Пример сервиса для работы с финансовыми транзакциями:
// src/Service/TransactionManager.php
class TransactionManager
{
private EntityManagerInterface $em;
private Connection $connection;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
$this->connection = $em->getConnection();
}
/**
* Выполняет денежный перевод между счетами с проверкой баланса
*/
public function transferFunds(int $fromAccountId, int $toAccountId, float $amount): bool
{
$this->em->beginTransaction();
try {
// Блокируем записи для избежания race condition
$fromAccount = $this->connection->executeQuery(
'SELECT * FROM accounts WHERE id = ? FOR UPDATE',
[$fromAccountId]
)->fetchAssociative();
if (!$fromAccount || $fromAccount['balance'] < $amount) {
throw new InsufficientFundsException();
}
// Выполняем перевод одним запросом для атомарности
$this->connection->executeStatement(
'UPDATE accounts SET balance = CASE
WHEN id = ? THEN balance - ?
WHEN id = ? THEN balance + ?
END WHERE id IN (?, ?)',
[$fromAccountId, $amount, $toAccountId, $amount, $fromAccountId, $toAccountId]
);
// Создаем запись о транзакции
$this->createTransactionRecord($fromAccountId, $toAccountId, $amount);
$this->em->commit();
return true;
} catch (Exception $e) {
$this->em->rollback();
throw $e;
}
}
private function createTransactionRecord(int $fromId, int $toId, float $amount): void
{
$stmt = $this->connection->prepare(
'INSERT INTO transactions (from_account_id, to_account_id, amount, created_at)
VALUES (?, ?, ?, NOW())'
);
$stmt->executeStatement([$fromId, $toId, $amount]);
}
}
Преимущества такого подхода:
- Инкапсуляция сложной логики в одном месте
- Контроль транзакций на уровне бизнес-операции
- Производительность за счет нативных SQL-запросов
- Тестируемость — сервис можно мокать в unit-тестах