neos / Neos.EventSourcing

A library for Event Sourcing and CQRS for Flow projects.
MIT License
44 stars 30 forks source link

Configurable EventNormalizers #318

Open bwaidelich opened 4 months ago

bwaidelich commented 4 months ago

Currently normalizers used in the EventNormalizer are hard-coded, but there is a TODO comment:

// TODO: make normalizers configurable

bwaidelich commented 4 months ago

Implementation idea

1. Change the EventNormalizer constructor from

public function __construct(EventTypeResolverInterface $eventTypeResolver)
{
    $this->eventTypeResolver = $eventTypeResolver;

    // TODO: make normalizers configurable
    $normalizers = [
        new BackedEnumNormalizer(),
        new DateTimeNormalizer(),
        new JsonSerializableNormalizer(),
        new ValueObjectNormalizer(),
        new ProxyAwareObjectNormalizer()
    ];
    $this->serializer = new Serializer($normalizers);
}

to something like

public function __construct(
    private readonly EventTypeResolverInterface $eventTypeResolver,
    NormalizerInterface ...$normalizers
) {
    $this->serializer = new Serializer($normalizers);
}

and remove "scope singleton annotation" from class declaration

2. Add settings for the normalizers

e.g.

Neos:
  EventSourcing:
    eventNormalizers:
      'backedEnumNormalizer':
        objectName: 'Symfony\Component\Serializer\Normalizer\BackedEnumNormalizer'
      'dateTimeNormalizer':
        objectName: 'Symfony\Component\Serializer\Normalizer\DateTimeNormalizer'
      'jsonSerializableNormalizer':
        objectName: 'Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer'
      'valueObjectNormalizer':
        objectName: 'Neos\EventSourcing\EventStore\Normalizer\ValueObjectNormalizer'
      'proxyAwareObjectNormalizer':
        objectName: 'Neos\EventSourcing\EventStore\Normalizer\ProxyAwareObjectNormalizer'
        position: 'end'

3. Add a factory

<?php
declare(strict_types=1);
namespace Neos\EventSourcing\EventStore;

/*
 * This file is part of the Neos.EventSourcing package.
 *
 * (c) Contributors of the Neos Project - www.neos.io
 *
 * This package is Open Source Software. For the full copyright and license
 * information, please view the LICENSE file which was distributed with this
 * source code.
 */

use Neos\EventSourcing\Event\EventTypeResolverInterface;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
use Neos\Utility\PositionalArraySorter;

#[Flow\Scope('singleton')]
final class EventNormalizerFactory
{
    /**
     * @param EventTypeResolverInterface $eventTypeResolver
     */
    public function __construct(
        private array $normalizerSettings,
        private readonly EventTypeResolverInterface $eventTypeResolver,
        private readonly ObjectManagerInterface $objectManager,
    ) {
    }

    public function create(): EventNormalizer
    {
        $normalizers = [];
        foreach ((new PositionalArraySorter($this->normalizerSettings))->toArray() as $name => $options) {
            // TODO: assert $options['objectName'] exists
            $normalizers[$name] = $this->objectManager->get($options['objectName']);
            // TODO: assert $normalizers[$name] instance of \Symfony\Component\Serializer\Normalizer\NormalizerInterface
        }
        return new EventNormalizer($this->eventTypeResolver, ...$normalizers);
    }
}

4. wire it up

In the existing Objects.yaml add:

Neos\EventSourcing\EventStore\EventNormalizerFactory:
  arguments:
    1:
      setting: 'Neos.EventSourcing.eventNormalizers'

Neos\EventSourcing\EventStore\EventNormalizer:
  scope: 'singleton'
  factoryObjectName: 'Neos\EventSourcing\EventStore\EventNormalizerFactory'

Drawbacks

This would affect all event stores in the system as it's a global setting.

Alternatively we could consider adding custom normalizer settings underneath Neos.EventSourcing.EventStore.stores.<storeId>.normalizers. But this would have to be respected in creation of