Skip to content

Commit

Permalink
Fixed re-opening entity manager on domain errors
Browse files Browse the repository at this point in the history
- store `DoctrineEventStore` now uses `ManagerRegistry` instead of `EntityManagerInterface` directly
- repository `DoctrineAggregateRootRepository` now uses `ManagerRegistry` instead of `EntityManagerInterface` directly
  • Loading branch information
tg666 committed Dec 16, 2024
1 parent 4dceb18 commit 649c4ff
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Mapping\MappingException;
use Doctrine\Persistence\ManagerRegistry;
use LogicException;
use SixtyEightPublishers\ArchitectureBundle\Domain\Event\AbstractDomainEvent;
use SixtyEightPublishers\ArchitectureBundle\Domain\ValueObject\CompositeAggregateIdInterface;
Expand All @@ -33,7 +34,7 @@ final class DoctrineEventStore implements EventStoreInterface
public const NAME = 'doctrine';

public function __construct(
private readonly EntityManagerInterface $em,
private readonly ManagerRegistry $managerRegistry,
) {}

/**
Expand All @@ -42,7 +43,8 @@ public function __construct(
*/
public function store(string $aggregateRootClassname, array $events): void
{
$connection = $this->em->getConnection();
$connection = $this->getConnection();
$em = $this->getEntityManager();
$tableName = $this->getTableName($aggregateRootClassname);

foreach ($events as $event) {
Expand All @@ -61,7 +63,7 @@ public function store(string $aggregateRootClassname, array $events): void
Types::JSON,
];

$classMetadata = $this->em->getClassMetadata($aggregateRootClassname);
$classMetadata = $em->getClassMetadata($aggregateRootClassname);
$aggregatedId = $event->getAggregateId();

$identifierColumns = array_combine(
Expand Down Expand Up @@ -111,7 +113,7 @@ public function store(string $aggregateRootClassname, array $events): void

public function get(string $aggregateRootClassname, EventId $eventId): ?AbstractDomainEvent
{
$connection = $this->em->getConnection();
$connection = $this->getConnection();
$tableName = $this->getTableName($aggregateRootClassname);

$query = sprintf(
Expand Down Expand Up @@ -139,7 +141,7 @@ public function get(string $aggregateRootClassname, EventId $eventId): ?Abstract
*/
public function find(EventCriteria $criteria): array
{
$connection = $this->em->getConnection();
$connection = $this->getConnection();
$tableName = $this->getTableName($criteria->getAggregateRootClassname());

$qb = $connection->createQueryBuilder()
Expand Down Expand Up @@ -197,7 +199,7 @@ public function find(EventCriteria $criteria): array
*/
public function count(EventCriteria $criteria): int
{
$connection = $this->em->getConnection();
$connection = $this->getConnection();
$tableName = $this->getTableName($criteria->getAggregateRootClassname());

$qb = $connection->createQueryBuilder()
Expand All @@ -219,8 +221,10 @@ public function count(EventCriteria $criteria): int
*/
private function buildConditions(QueryBuilder $qb, EventCriteria $criteria): QueryBuilder
{
$em = $this->getEntityManager();

if (null !== $criteria->getAggregateId()) {
$classMetadata = $this->em->getClassMetadata($criteria->getAggregateRootClassname());
$classMetadata = $em->getClassMetadata($criteria->getAggregateRootClassname());
$aggregatedId = $criteria->getAggregateId();

$identifierColumns = array_combine(
Expand Down Expand Up @@ -293,11 +297,13 @@ private function buildConditions(QueryBuilder $qb, EventCriteria $criteria): Que
*/
private function getTableName(string $aggregateRootClassname): string
{
$classMetadata = $this->em->getClassMetadata($aggregateRootClassname);
$em = $this->getEntityManager();
$connection = $em->getConnection();
$classMetadata = $em->getClassMetadata($aggregateRootClassname);
$tableName = $classMetadata->getTableName();

try {
$quoteCharacter = $this->em->getConnection()->getDatabasePlatform()->getIdentifierQuoteCharacter();
$quoteCharacter = $connection->getDatabasePlatform()->getIdentifierQuoteCharacter();
} catch (DbalException $e) {
throw EventStoreException::of($e, $e instanceof RetryableException);
}
Expand All @@ -324,4 +330,17 @@ private function hydrateEvent(Connection $connection, array $data): AbstractDoma
$connection->convertToPHPValue($data['parameters'], Types::JSON),
);
}

private function getEntityManager(): EntityManagerInterface
{
$em = $this->managerRegistry->getManager();
assert($em instanceof EntityManagerInterface);

return $em;
}

private function getConnection(): Connection
{
return $this->getEntityManager()->getConnection();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,32 @@
namespace SixtyEightPublishers\ArchitectureBundle\Infrastructure\Doctrine\Repository;

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ManagerRegistry;
use SixtyEightPublishers\ArchitectureBundle\Domain\AggregateRootInterface;
use SixtyEightPublishers\ArchitectureBundle\Domain\ValueObject\AggregateIdInterface;
use SixtyEightPublishers\ArchitectureBundle\Domain\ValueObject\CompositeAggregateIdInterface;
use SixtyEightPublishers\ArchitectureBundle\EventStore\EventStoreException;
use SixtyEightPublishers\ArchitectureBundle\EventStore\EventStoreInterface;
use SixtyEightPublishers\ArchitectureBundle\Infrastructure\Common\EventPublisher\EventPublisherInterface;
use function assert;
use function get_class;

final class DoctrineAggregateRootRepository implements DoctrineAggregateRootRepositoryInterface
{
public function __construct(
private readonly EntityManagerInterface $em,
private readonly ManagerRegistry $managerRegistry,
private readonly EventPublisherInterface $eventPublisher,
private readonly EventStoreInterface $eventStore,
) {}

public function loadAggregateRoot(string $classname, AggregateIdInterface $aggregateId): ?object
{
return $this->em->find(
public function loadAggregateRoot(
string $classname,
AggregateIdInterface $aggregateId,
?string $entityManagerName = null,
): ?object {
$em = $this->resolveEntityManager(entityManagerName: $entityManagerName);

return $em->find(
className: $classname,
id: $aggregateId instanceof CompositeAggregateIdInterface ? $aggregateId->getValues() : $aggregateId,
);
Expand All @@ -32,29 +39,41 @@ className: $classname,
/**
* @throws EventStoreException
*/
public function saveAggregateRoot(AggregateRootInterface $aggregateRoot, ?string $deleteEventClassname = null): void
{
public function saveAggregateRoot(
AggregateRootInterface $aggregateRoot,
?string $deleteEventClassname = null,
?string $entityManagerName = null,
): void {
$em = $this->resolveEntityManager(entityManagerName: $entityManagerName);
$events = $aggregateRoot->popRecordedEvents();
$aggregateRootClassname = get_class($aggregateRoot);
$persist = true;

if (null !== $deleteEventClassname) {
foreach ($events as $event) {
if ($event instanceof $deleteEventClassname && $event->getAggregateId()->equals($aggregateRoot->getAggregateId())) {
$this->em->remove($aggregateRoot);
$em->remove($aggregateRoot);
$persist = false;
}
}
}

if ($persist) {
$this->em->persist($aggregateRoot);
$em->persist($aggregateRoot);
}

$this->eventStore->store($aggregateRootClassname, $events);

$this->em->flush();
$em->flush();

$this->eventPublisher->publish($aggregateRootClassname, $aggregateRoot->getAggregateId(), $events);
}

private function resolveEntityManager(?string $entityManagerName): EntityManagerInterface
{
$em = $this->managerRegistry->getManager($entityManagerName);
assert($em instanceof EntityManagerInterface);

return $em;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@ interface DoctrineAggregateRootRepositoryInterface
/**
* @param class-string $classname
*/
public function loadAggregateRoot(string $classname, AggregateIdInterface $aggregateId): ?object;
public function loadAggregateRoot(
string $classname,
AggregateIdInterface $aggregateId,
?string $entityManagerName = null,
): ?object;

/**
* @param class-string<AbstractDomainEvent>|null $deleteEventClassname
*/
public function saveAggregateRoot(AggregateRootInterface $aggregateRoot, ?string $deleteEventClassname = null): void;
public function saveAggregateRoot(
AggregateRootInterface $aggregateRoot,
?string $deleteEventClassname = null,
?string $entityManagerName = null,
): void;
}

0 comments on commit 649c4ff

Please sign in to comment.