Pular para o conteúdo

Padrão Unidade de Trabalho

O padrão Unidade de Trabalho mantém uma lista de objetos afetados por uma transação de negócio e coordena a escrita de mudanças. Garante que todas as mudanças relacionadas sejam confirmadas juntas ou revertidas em caso de falha.

  1. Gerenciamento de Transação - Agrupar operações relacionadas
  2. Rastreamento de Mudanças - Rastrear entidades modificadas
  3. Operações em Lote - Otimizar escritas de banco de dados
  4. Consistência - Garantir integridade de dados
<?php
declare(strict_types=1);
namespace XoopsModules\MyModule\Infrastructure;
interface UnitOfWorkInterface
{
public function begin(): void;
public function commit(): void;
public function rollback(): void;
public function registerNew(object $entity): void;
public function registerDirty(object $entity): void;
public function registerDeleted(object $entity): void;
}
<?php
declare(strict_types=1);
namespace XoopsModules\MyModule\Infrastructure;
final class UnitOfWork implements UnitOfWorkInterface
{
private array $newEntities = [];
private array $dirtyEntities = [];
private array $deletedEntities = [];
private bool $inTransaction = false;
public function __construct(
private readonly \XoopsDatabase $db,
private readonly EntityMapperRegistry $mappers
) {}
public function begin(): void
{
if ($this->inTransaction) {
throw new \RuntimeException('Transaction already started');
}
$this->db->query('START TRANSACTION');
$this->inTransaction = true;
}
public function commit(): void
{
if (!$this->inTransaction) {
throw new \RuntimeException('No transaction in progress');
}
try {
$this->insertNew();
$this->updateDirty();
$this->deleteRemoved();
$this->db->query('COMMIT');
$this->clear();
} catch (\Exception $e) {
$this->rollback();
throw $e;
}
}
public function rollback(): void
{
if ($this->inTransaction) {
$this->db->query('ROLLBACK');
$this->clear();
}
}
public function registerNew(object $entity): void
{
$id = spl_object_id($entity);
$this->newEntities[$id] = $entity;
}
public function registerDirty(object $entity): void
{
$id = spl_object_id($entity);
if (!isset($this->newEntities[$id])) {
$this->dirtyEntities[$id] = $entity;
}
}
public function registerDeleted(object $entity): void
{
$id = spl_object_id($entity);
unset($this->newEntities[$id], $this->dirtyEntities[$id]);
$this->deletedEntities[$id] = $entity;
}
private function insertNew(): void
{
foreach ($this->newEntities as $entity) {
$mapper = $this->mappers->getMapper($entity::class);
$mapper->insert($entity);
}
}
private function updateDirty(): void
{
foreach ($this->dirtyEntities as $entity) {
$mapper = $this->mappers->getMapper($entity::class);
$mapper->update($entity);
}
}
private function deleteRemoved(): void
{
foreach ($this->deletedEntities as $entity) {
$mapper = $this->mappers->getMapper($entity::class);
$mapper->delete($entity);
}
}
private function clear(): void
{
$this->newEntities = [];
$this->dirtyEntities = [];
$this->deletedEntities = [];
$this->inTransaction = false;
}
}
final class ArticleService
{
public function __construct(
private readonly UnitOfWorkInterface $unitOfWork,
private readonly ArticleRepository $articles,
private readonly CommentRepository $comments
) {}
public function publishWithComments(
Article $article,
array $comments
): void {
$this->unitOfWork->begin();
try {
// Marcar artigo como modificado
$article->publish();
$this->unitOfWork->registerDirty($article);
// Adicionar novos comentários
foreach ($comments as $comment) {
$this->unitOfWork->registerNew($comment);
}
// Confirmar todas as mudanças juntas
$this->unitOfWork->commit();
} catch (\Exception $e) {
$this->unitOfWork->rollback();
throw $e;
}
}
}
final class ArticleRepository implements ArticleRepositoryInterface
{
public function __construct(
private readonly UnitOfWorkInterface $unitOfWork,
private readonly ArticleMapper $mapper
) {}
public function add(Article $article): void
{
$this->unitOfWork->registerNew($article);
}
public function update(Article $article): void
{
$this->unitOfWork->registerDirty($article);
}
public function remove(Article $article): void
{
$this->unitOfWork->registerDeleted($article);
}
public function findById(ArticleId $id): ?Article
{
return $this->mapper->findById($id);
}
}
  1. Transações Curtas - Mantenha transações breves
  2. Responsabilidade Única - Uma unidade de trabalho por operação de negócio
  3. Limites Claros - Defina escopo de transação claramente
  4. Tratamento de Erro - Sempre manipule cenários de rollback
  5. Evite Aninhamento - Não aninhe unidades de trabalho
  • Repository-Layer - Padrão repositório
  • Service-Layer - Padrão de serviço
  • ../Database/Database-Schema - Operações de banco de dados
  • Domain-Model - Entidades de domínio