Upcasting
There are cases where the already have events in our stream but there is data missing
or not in the right format for our new usecase. Normally you would need to create versioned events for this.
This can lead to many versions of the same event which could lead to some chaos.
To prevent this we offer Upcaster
, which can operate on the payload before denormalizing to an event object.
There you can change the event name and adjust the payload of the event.
Adjust payload
Let's assume we have an ProfileCreated
event which holds an email.
Now the business needs to have all emails to be in lower case.
For that we could adjust the aggregate and the projections to take care of that.
Or we can do this beforehand so we don't need to maintain two different places.
use Patchlevel\EventSourcing\Serializer\Upcast\Upcast;
use Patchlevel\EventSourcing\Serializer\Upcast\Upcaster;
final class ProfileCreatedEmailLowerCastUpcaster implements Upcaster
{
public function __invoke(Upcast $upcast): Upcast
{
// ignore if other event is processed
if ($upcast->eventName !== 'profile.created') {
return $upcast;
}
if (!array_key_exists('email', $upcast->payload) || !is_string($upcast->payload['email'])) {
return $upcast;
}
return $upcast->replacePayloadByKey('email', strtolower($upcast->payload['email']));
}
}
Warning
You need to consider that other events are passed to the Upcaster. So and early out is here endorsed.
Adjust event name
Sometimes your event name was not the best choice and you want to change it.
For this we can use the Upcaster
to change the event name.
use Patchlevel\EventSourcing\Serializer\Upcast\Upcast;
use Patchlevel\EventSourcing\Serializer\Upcast\Upcaster;
final class EventNameRenameUpcaster implements Upcaster
{
/** @param array<string, string> $eventNameMap */
public function __construct(
private readonly array $eventNameMap,
) {
}
public function __invoke(Upcast $upcast): Upcast
{
if (array_key_exists($upcast->eventName, $this->eventNameMap)) {
return $upcast->replaceEventName($this->eventNameMap[$upcast->eventName]);
}
return $upcast;
}
}
Configure
After we have defined the upcasting rules, we also have to pass the whole thing to the serializer. Since we have multiple upcasters, we use a chain here.
use Patchlevel\EventSourcing\Metadata\Event\EventRegistry;
use Patchlevel\EventSourcing\Serializer\DefaultEventSerializer;
use Patchlevel\EventSourcing\Serializer\Upcast\UpcasterChain;
/** @var EventRegistry $eventRegistry */
$upcaster = new UpcasterChain([
new ProfileCreatedEmailLowerCastUpcaster(),
new EventNameRenameUpcaster(['old_event_name' => 'new_event_name']),
]);
$serializer = DefaultEventSerializer::createFromPaths(
['src/Domain'],
$upcaster,
);