Skip to content

Commit

Permalink
when EventSourcedAggregate factory method does not return events, Sav…
Browse files Browse the repository at this point in the history
…eAggregateService should not try to save it (#151)

* dropping behat tests in DBAL Module by rewriting them with Ecotone Test Support

* dropping behat tests in AMQP Module by rewriting them with Ecotone Test Support

* dropping behat in Module Template

* when EventSourcedAggregate factory method does not return events, SaveAggregateService should not try to save it
in addition
- removing AggregateFactory attrobute due its deprecation
  • Loading branch information
unixslayer authored Jun 22, 2023
1 parent a3a52b9 commit b552479
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 41 deletions.
10 changes: 0 additions & 10 deletions packages/Ecotone/src/Modelling/Attribute/AggregateFactory.php

This file was deleted.

53 changes: 34 additions & 19 deletions packages/Ecotone/src/Modelling/SaveAggregateService.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,32 +77,17 @@ public function save(Message $message, array $metadata): Message
{
$metadata = MessageHeaders::unsetNonUserKeys($metadata);

$aggregate = $message->getHeaders()->get(AggregateMessage::AGGREGATE_OBJECT);
$events = [];
$events = $this->resolveEvents($message, $metadata);

if ($this->isEventSourced) {
$events = $message->getPayload();
Assert::isIterable($events, "Return value Event Sourced Aggregate {$this->aggregateInterface} must return array of events");
} elseif ($this->aggregateMethodWithEvents) {
$events = call_user_func([$aggregate, $this->aggregateMethodWithEvents]);
if ($this->isEventSourced && $events === []) {
return MessageBuilder::fromMessage($message)->build();
}

$events = array_map(function ($event) use ($metadata): Event {
if (! is_object($event)) {
$typeDescriptor = TypeDescriptor::createFromVariable($event);
throw InvalidArgumentException::create("Events return by after calling {$this->aggregateInterface} must all be objects, {$typeDescriptor->toString()} given");
}

$metadata = RevisionMetadataEnricher::enrich($metadata, $event);
$metadata = MessageHeaders::unsetTransportMessageKeys($metadata);

return Event::create($event, $metadata);
}, $events);

$versionBeforeHandling = $message->getHeaders()->containsKey(AggregateMessage::TARGET_VERSION)
? $message->getHeaders()->get(AggregateMessage::TARGET_VERSION)
: null;

$aggregate = $message->getHeaders()->get(AggregateMessage::AGGREGATE_OBJECT);
if ($this->aggregateVersionProperty && $this->isAggregateVersionAutomaticallyIncreased) {
$this->propertyEditorAccessor->enrichDataWith(
PropertyPath::createWith($this->aggregateVersionProperty),
Expand Down Expand Up @@ -210,4 +195,34 @@ private function getAggregateIds(array $aggregateIds, object $aggregate, bool $t

return AggregateIdResolver::resolveArrayOfIdentifiers(get_class($aggregate), $aggregateIds);
}

/**
* @return array<Event>
*/
private function resolveEvents(Message $message, array $metadata): array
{
$events = [];

if ($this->isEventSourced) {
$events = $message->getPayload();
Assert::isIterable($events, "Return value Event Sourced Aggregate {$this->aggregateInterface} must return array of events");
}

if ($events === [] && $this->aggregateMethodWithEvents) {
$aggregate = $message->getHeaders()->get(AggregateMessage::AGGREGATE_OBJECT);
$events = call_user_func([$aggregate, $this->aggregateMethodWithEvents]);
}

return array_map(function ($event) use ($metadata): Event {
if (! is_object($event)) {
$typeDescriptor = TypeDescriptor::createFromVariable($event);
throw InvalidArgumentException::create("Events return by after calling {$this->aggregateInterface} must all be objects, {$typeDescriptor->toString()} given");
}

$metadata = RevisionMetadataEnricher::enrich($metadata, $event);
$metadata = MessageHeaders::unsetTransportMessageKeys($metadata);

return Event::create($event, $metadata);
}, $events);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@

namespace Test\Ecotone\Modelling\Fixture\IncorrectEventSourcedAggregate\NoIdDefinedAfterCallingFactory;

use Ecotone\Modelling\Attribute\AggregateFactory;
use Ecotone\Modelling\Attribute\AggregateEvents;
use Ecotone\Modelling\Attribute\AggregateIdentifier;
use Ecotone\Modelling\Attribute\CommandHandler;
use Ecotone\Modelling\Attribute\EventSourcingAggregate;
use stdClass;

#[EventSourcingAggregate]
class NoIdDefinedAfterCallingFactoryExample
class NoIdDefinedAfterRecordingEvents
{
#[AggregateIdentifier]
private $id;

#[CommandHandler]
public static function create(CreateNoIdDefinedAggregate $command): array
{
return [new stdClass()];
return [];
}

#[AggregateFactory]
public static function factory(array $events): self
#[AggregateEvents]
public function recordedEvents(): array
{
return new self();
return [new stdClass()];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Test\Ecotone\Modelling\Fixture\NoEventsReturnedFromFactoryMethod;

use Ecotone\Modelling\Attribute\AggregateIdentifier;
use Ecotone\Modelling\Attribute\CommandHandler;
use Ecotone\Modelling\Attribute\EventSourcingAggregate;
use Ecotone\Modelling\Attribute\EventSourcingHandler;
use Ecotone\Modelling\WithAggregateVersioning;

#[EventSourcingAggregate]
final class Aggregate
{
use WithAggregateVersioning;

#[AggregateIdentifier]
private int $id;

#[CommandHandler(routingKey: 'aggregate.create')]
public static function create(): array
{
return [];
}

#[EventSourcingHandler]
public function when(AggregateCreated $event): void
{
$this->id = $event->id;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace Test\Ecotone\Modelling\Fixture\NoEventsReturnedFromFactoryMethod;

final class AggregateCreated
{
public function __construct(public int $id)
{
}
}
17 changes: 17 additions & 0 deletions packages/Ecotone/tests/Modelling/Unit/ModellingEcotoneLiteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Test\Ecotone\Modelling\Fixture\HandlerWithAbstractClass\TestAbstractHandler;
use Test\Ecotone\Modelling\Fixture\HandlerWithAbstractClass\TestCommand;
use Test\Ecotone\Modelling\Fixture\HandlerWithAbstractClass\TestHandler;
use Test\Ecotone\Modelling\Fixture\NoEventsReturnedFromFactoryMethod\Aggregate;
use Test\Ecotone\Modelling\Fixture\Outbox\OutboxWithMultipleChannels;

/**
Expand Down Expand Up @@ -140,4 +141,20 @@ public function test_event_flow_with_event_sourcing_aggregate()
->sendQueryWithRouting('order_dispatch.getStatus', metadata: ['aggregate.id' => '1'])
);
}

public function test_factory_method_of_event_sourced_aggregate_can_return_no_events(): void
{
$ecotoneLite = EcotoneLite::bootstrapFlowTesting(
classesToResolve: [Aggregate::class],
configuration: ServiceConfiguration::createWithDefaults()
->withSkippedModulePackageNames(ModulePackageList::allPackages())
);

self::assertEquals(
[],
$ecotoneLite
->sendCommandWithRoutingKey('aggregate.create')
->getRecordedEvents()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
use Test\Ecotone\Modelling\Fixture\CommandHandler\Aggregate\Notification;
use Test\Ecotone\Modelling\Fixture\CommandHandler\Aggregate\Order;
use Test\Ecotone\Modelling\Fixture\CommandHandler\Aggregate\OrderWithManualVersioning;
use Test\Ecotone\Modelling\Fixture\IncorrectEventSourcedAggregate\NoIdDefinedAfterCallingFactory\NoIdDefinedAfterCallingFactoryExample;
use Test\Ecotone\Modelling\Fixture\IncorrectEventSourcedAggregate\NoIdDefinedAfterCallingFactory\NoIdDefinedAfterRecordingEvents;
use Test\Ecotone\Modelling\Fixture\IncorrectEventSourcedAggregate\PublicIdentifierGetMethodForEventSourcedAggregate;
use Test\Ecotone\Modelling\Fixture\IncorrectEventSourcedAggregate\PublicIdentifierGetMethodWithParameters;
use Test\Ecotone\Modelling\Fixture\Ticket\Ticket;
Expand Down Expand Up @@ -396,7 +396,7 @@ function () {
public function test_throwing_exception_if_aggregate_before_saving_has_no_nullable_identifier()
{
$aggregateCallingCommandHandler = SaveAggregateServiceBuilder::create(
ClassDefinition::createFor(TypeDescriptor::create(NoIdDefinedAfterCallingFactoryExample::class)),
ClassDefinition::createFor(TypeDescriptor::create(NoIdDefinedAfterRecordingEvents::class)),
'create',
InterfaceToCallRegistry::createEmpty(),
SaveAggregateService::NO_SNAPSHOT_THRESHOLD,
Expand All @@ -421,7 +421,7 @@ public function test_throwing_exception_if_aggregate_before_saving_has_no_nullab

$aggregateCommandHandler->handle(
MessageBuilder::withPayload([])
->setHeader(AggregateMessage::AGGREGATE_OBJECT, new NoIdDefinedAfterCallingFactoryExample())
->setHeader(AggregateMessage::AGGREGATE_OBJECT, new NoIdDefinedAfterRecordingEvents())
->setReplyChannel(NullableMessageChannel::create())
->build()
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@
use App\Microservices\BackofficeService\Domain\Ticket\Event\TicketWasAssigned;
use App\Microservices\BackofficeService\Domain\Ticket\Event\TicketWasCancelled;
use App\Microservices\BackofficeService\Domain\Ticket\Event\TicketWasPrepared;
use Ecotone\Modelling\Attribute\AggregateFactory;
use Ecotone\Modelling\Attribute\AggregateIdentifier;
use Ecotone\Modelling\Attribute\CommandHandler;
use Ecotone\Modelling\Attribute\Distributed;
use Ecotone\Modelling\Attribute\EventSourcingAggregate;
use Ecotone\Modelling\Attribute\EventSourcingHandler;
use Ecotone\Modelling\WithAggregateVersioning;
use InvalidArgumentException;
use Ramsey\Uuid\Uuid;

#[EventSourcingAggregate]
Expand Down Expand Up @@ -61,4 +59,4 @@ public function applyTicketWasCancelled(TicketWasCancelled $event): void
{
$this->isCancelled = true;
}
}
}

0 comments on commit b552479

Please sign in to comment.