Symfony bundle for EventSauce (WIP)

Overview

Symfony EventSauce (WIP)

This bundle provides the basic and extended container configuration of symfony for the EventSauce library. Before using it, I strongly recommend that you read the official documentation.

Supports

  • Doctrine event message repository
  • All events in table per aggregate type
  • Message Outbox
  • Symfony messenger
  • Symfony serializer
  • Snapshot doctrine repository
  • Snapshot versioning
  • Snapshot store every n event
  • Automatic generate migration for aggregate
  • Message upcasting

Requirements

  • PHP ^8.1
  • Symfony ^6.0

Installation

composer require andreo/event-sauce-bundle

Verify that the bundle has been added the config/bundles.php file

return [
    Andreo\EventSauceBundle\AndreoEventSauceBundle::class => ['all' => true],
];

Timezone

You probably want to set your time zone.

andreo_event_sauce:
    time:
        recording_timezone: Europe/Warsaw # default is UTC

Message dispatching

Defaults EventSauce to dispatch events use SynchronousMessageDispatcher. An example configuration is as follows

andreo_event_sauce:
    message:
        dispatcher:
            chain:
                - fooBus
                - barBus

Defining the example message consumer is as follows

use EventSauce\EventSourcing\MessageConsumer;
use Andreo\EventSauceBundle\Attribute\AsMessageConsumer;

#[AsMessageConsumer(dispatcher: fooBus)]
final class SomeProjection implements MessageConsumer {

    public function handle(Message $message): void {
        // do something
    }
}

Message dispatching with symfony messenger

You need install the package (recommend reading doc).

composer require andreo/eventsauce-messenger

An example configuration is as follows

andreo_event_sauce:
    message:
        dispatcher:
            messenger: true
            chain:
                fooBus: barBus # message bus alias from messenger config
                bazBus: quxBus

Message dispatching mode

The mode option is a way of dispatch messages. Available values:

event (default)

  • Event is only dispatch to the handler that supports the event type
  • Doesn't dispatch headers

event_and_headers

  • Event is dispatch to the handler that supports the event type
  • Receive of message headers in the second handler argument

message

  • Message is dispatch to the any handler that supports the Message type. You have to manually check event type
  • Message object includes the event and headers

Change the default event mode

andreo_event_sauce:
    message:
        dispatcher:
            messenger:
                mode: event_and_headers # default is event
            chain:
                fooBus: barBus

Event Dispatcher

Sometimes you may want to use dispatch messages in a context other than aggregate. You can do this with the Event Dispatcher

To enable it use this configuration

andreo_event_sauce:
    message:
        dispatcher:
            event_dispatcher: true # default is false
            chain:
                - fooBus

Then you can inject the event dispatcher based on the alias and dedicated interface

use EventSauce\EventSourcing\EventDispatcher;
use Symfony\Component\DependencyInjection\Attribute\Target;

final class SomeContext {

   public function __construct(
        #[Target('fooBus')] private EventDispatcher $fooBus
    ){}
}

Aggregates

About Aggregates

An example configuration for two aggregates is as follows

andreo_event_sauce:
    aggregates:
        foo:
            class: App\Domain\Foo
        bar:
            class: App\Domain\Bar

Then you can inject the repository based on the alias and dedicated interface. By default, alias is created automatically by convention "${name}Repository"

use EventSauce\EventSourcing\AggregateRootRepository;
use Symfony\Component\DependencyInjection\Attribute\Target;

final class SomeHandler {

   public function __construct(
        #[Target('fooRepository')] private AggregateRootRepository $fooRepository
    ){}
}

Message dispatching

By default, messages are dispatch by all dispatchers, but you can specify them per aggregate.

andreo_event_sauce:
    message:
        dispatcher:
            chain:
                - fooBus
                - barBus
    aggregates:
        foo:
            class: App\Domain\Foo
            dispatchers:
                - barBus # dispatch only by barBas

Outbox

About the Outbox

You need install the package (recommend reading doc).

composer require andreo/eventsauce-outbox
andreo_event_sauce:
    outbox: true # enable outbox and register its services
    aggregates:
        foo:
            class: App\Domain\Foo
            outbox: true # register doctrine transactional repository and outbox relay per aggregate

Outbox Back-off Strategy

About the Back-off Strategy

By default, this bundle uses ExponentialBackOffStrategy. You can change it. An example configuration is as follows

andreo_event_sauce:
    outbox:
        back_off:
            fibonacci: # default is exponential. More data in config reference
                initial_delay_ms: 100000
                max_tries: 10

Outbox Commit Strategy

By default, this bundle uses MarkMessagesConsumedOnCommit strategy You can change it. An example configuration is as follows

andreo_event_sauce:
    outbox:
        relay_commit:
            delete: true # default is mark_consumed

Outbox repository

By default, outbox messages are stored in a database. If you want to store them in a memory, add the following configuration

andreo_event_sauce:
    outbox:
        repository: 
            memory: true # default is doctrine

Outbox messages dispatching command

php bin/console andreo:event-sauce:outbox-process-messages

Snapshotting

About Snapshotting

andreo_event_sauce:
    snapshot: true # enable snapshot and register its services
    aggregates:
        bar:
            class: App\Domain\Bar
            snapshot: true # register snapshot repository per aggregate

Then you can inject the repository based on the alias and dedicated interface

use Symfony\Component\DependencyInjection\Attribute\Target;
use EventSauce\EventSourcing\Snapshotting\AggregateRootRepositoryWithSnapshotting;

final class SomeHandler {

   public function __construct(
        #[Target('barRepository')] private AggregateRootRepositoryWithSnapshotting $barRepository
    ){}
}

Snapshotting extended components

You need install the package (recommend reading doc).

composer require andreo/eventsauce-snapshotting

Snapshot Doctrine repository

By default, snapshots are stored in a memory. If you want to store them in a database with doctrine, add the following configuration

andreo_event_sauce:
    snapshot:
        repository:
            doctrine: true # default is memory
    aggregates:
        foo:
            class: App\Domain\Foo
            snapshot: true

Snapshot versioning

andreo_event_sauce:
    snapshot:
        versioned: true # default is false
    aggregates:
        foo:
            class: App\Domain\Foo
            snapshot: true

Snapshot store strategy

andreo_event_sauce:
    snapshot:
        store_strategy:
            every_n_event: # Store snapshot every n event
                number: 200
            custom: # Or you can set your own strategy
    aggregates:
        foo:
            class: App\Domain\Foo
            snapshot: true

Upcasting

About Upcasting

andreo_event_sauce:
    upcast: true # enable upcast and register its services
    aggregates:
        foo:
            class: App\Domain\Foo
            upcast: true # register  upcaster chain per aggregate

Defining the upcaster is as follows

use Andreo\EventSauceBundle\Attribute\AsUpcaster;
use EventSauce\EventSourcing\Upcasting\Upcaster;

#[AsUpcaster(aggregate: 'foo', version: 2)]
final class SomeEventV2Upcaster implements Upcaster {

    public function upcast(array $message): array
    {
        // do something
    }
}

Upcaster context

By default, this library uses the payload context according to the EventSauce implementation. If you want to upcasting on the message object context, you need install the package (recommend reading doc).

composer require andreo/eventsauce-upcasting

and use the following configuration.

andreo_event_sauce:
    upcast:
        context: message # default is payload
    aggregates:
        foo:
            class: App\Domain\Foo
            upcast: true

Defining the upcaster is as follows

use Andreo\EventSauce\Upcasting\Event;
use Andreo\EventSauce\Upcasting\MessageUpcaster;
use EventSauce\EventSourcing\Message;

#[AsUpcaster(aggregate: 'foo', version: 2)]
final class SomeEventV2Upcaster implements MessageUpcaster {

    #[Event(event: SomeEvent::class)] // guess event
    public function upcast(Message $message): Message
    {
        // do something
    }
}

Message decorating

About Message Decorator

Defining the message decorator is as follows

use Andreo\EventSauceBundle\Attribute\AsMessageDecorator;
use EventSauce\EventSourcing\Message;
use EventSauce\EventSourcing\MessageDecorator;

#[AsMessageDecorator]
final class SomeDecorator implements MessageDecorator
{
    public function decorate(Message $message): Message
    {
        // do something
    }
}

By default, message decoration is enabled. You can it disabled.

andreo_event_sauce:
    message:
        decorator: false

Message decorating context

In this bundle, messages can be decorated at the aggregate level, or at a completely different level of the event dispatcher. You can specify the context in which the decorator is to be used

use Andreo\EventSauceBundle\Attribute\AsMessageDecorator;
use EventSauce\EventSourcing\Message;
use EventSauce\EventSourcing\MessageDecorator;
use Andreo\EventSauceBundle\Attribute\MessageContext;

#[AsMessageDecorator(context: MessageContext::EVENT_DISPATCHER)]
final class SomeDecorator implements MessageDecorator
{
    public function decorate(Message $message): Message
    {
        // do something
    }
}

Database Structure

About Database Structure

This bundle uses the all events in table per aggregate approach. Event messages, outbox messages, and snapshots are stored in a separate table per aggregate type

To simplify the creation of migrations, I have created a package that allows you to automatically generate migrations based on the name of the aggregate. More information in the documentation

composer require andreo/eventsauce-generate-migration

For example, to generate migrations for the following configuration

andreo_event_sauce:
    outbox: true
    aggregates:
        foo:
            class: App\Domain\Foo
            outbox: true

Execute the following command

php bin/console andreo:event-sauce:doctrine:migration:generate foo --schema=event --schema=outbox 

and default doctrine migration command

php bin/console d:m:m

Serialization

Symfony payload serializer

The default serializer is ConstructingPayloadSerializer

If you don't want to build payload yourself, you can use the symfony serializer

You need install the package.

composer require andreo/eventsauce-symfony-serializer

and add the following configuration

andreo_event_sauce:
    payload_serializer: Andreo\EventSauce\Serialization\SymfonyPayloadSerializer # or your custom serializer

Message serializer for MySQL8

andreo_event_sauce:
    message:
        serializer: EventSauce\EventSourcing\Serialization\MySQL8DateFormatting # or your custom serializer
You might also like...
A Symfony bundle that provides #StandWithUkraine banner and has some built-in features to block access to your resource for Russian-speaking users.
A Symfony bundle that provides #StandWithUkraine banner and has some built-in features to block access to your resource for Russian-speaking users.

StandWithUkraineBundle На русском? Смотри README.ru.md This bundle provides a built-in StandWithUkraine banner for your Symfony application and has so

OpenAPI(v3) Validators for Symfony http-foundation, using `league/openapi-psr7-validator` and `symfony/psr-http-message-bridge`.

openapi-http-foundation-validator OpenAPI(v3) Validators for Symfony http-foundation, using league/openapi-psr7-validator and symfony/psr-http-message

Fork of Symfony Rate Limiter Component for Symfony 4

Rate Limiter Component Fork (Compatible with Symfony =4.4) The Rate Limiter component provides a Token Bucket implementation to rate limit input and

Enter-to-the-Matrix-with-Symfony-Console - Reproduction of the
Enter-to-the-Matrix-with-Symfony-Console - Reproduction of the "Matrix characterfall" effect with the Symfony Console component.

Enter to the Matrix (with Symfony Console) Reproduction of the "Matrix characterfall" effect with the Symfony Console component. Run Clone the project

Copy of hautelook/alice-bundle v2.9.0

AliceBundle A Symfony bundle to manage fixtures with nelmio/alice and fzaninotto/Faker. The database support is done in FidryAliceDataFixtures. Check

The maker bundle allows you to generate content elements, front end modules

Contao 4 maker bundle The maker bundle allows you to generate content elements, front end modules, event listener, callbacks and hooks using interacti

The NelmioApiDocBundle bundle allows you to generate a decent documentation for your APIs

NelmioApiDocBundle The NelmioApiDocBundle bundle allows you to generate a decent documentation for your APIs. Migrate from 3.x to 4.0 To migrate from

The news bundle adds news functionality to Contao 4

Contao 4 news bundle The news bundle adds news functionality to Contao 4. Contao is an Open Source PHP Content Management System for people who want a

SAML bundle for gateway, self-service, and ra

SURFnet SamlBundle A bundle that adds SAML capabilities to your application using simplesamlphp/saml2 Developed as part of the SURFnet Stepup Gateway

Comments
  • Cannot install this bundle via composer

    Cannot install this bundle via composer

    As mentioned in title. If I'm doing something wrong, please tell me.

    dev@c3775cef6f40:/application$ composer require andreo/event-sauce-bundle
    [InvalidArgumentException]                                                                                                                                         
      Could not find a matching version of package andreo/event-sauce-bundle. Check the package spelling, your version constraint and that the package is available in   
      a stability which matches your minimum-stability (dev).                                                                                                            
    

    I tried to search it. All your packages are visible except this one

    dev@c3775cef6f40:/application$ composer search andreo
    andreo/eventsauce-messenger          Integration symfony messenger with the EventSauce.
    andreo/eventsauce-upcasting          Extended upcasting components of the EventSauce.
    andreo/eventsauce-symfony-serializer Custom symfony payload serializer of the EventSauce.
    andreo/eventsauce-snapshotting       Extended snapshot components of the EventSauce.
    andreo/eventsauce-outbox             Extended message outbox components of the EventSauce.
    andreo/eventsauce-messenger          Integration symfony messenger with the EventSauce.
    andreo/eventsauce-generate-migration Command that generates doctrine migrations per aggregate for the EventSauce.
    andreo/eventsauce-aggregate          Extended aggregate components of the EventSauce.
    
    
    opened by lordcysiek 4
  • Odchudzić config

    Odchudzić config

    • memory repo jako default bez konfiguracji
    • outbox strategy jako serwis id
    • proste klasy, które można nadpisac w services
    • facl filter strategy na poziomie dispatchera
    opened by andrzepakula 0
  • Asynchronous projections handling.

    Asynchronous projections handling.

    Hello,

    Can You share Your vision of asynchronous projections handling? One of the reasonable solutions is to use Consumer handler to propagate event to async queue as new Command. But it implies some additional artifacts - Consumer -> Command -> Handler for each projection. I was thinking about centralized, configurable multiple bus propagation for events. Maybe I'm over engineering this?

    opened by lordcysiek 1
Releases(2.0.1)
Owner
Andrew Pakula
Andrew Pakula
This bundle provides tools to build a complete GraphQL server in your Symfony App.

OverblogGraphQLBundle This Symfony bundle provides integration of GraphQL using webonyx/graphql-php and GraphQL Relay. It also supports: batching with

Webedia - Overblog 720 Dec 25, 2022
Pure PHP implementation of GraphQL Server – Symfony Bundle

Symfony GraphQl Bundle This is a bundle based on the pure PHP GraphQL Server implementation This bundle provides you with: Full compatibility with the

null 283 Dec 15, 2022
DataTables bundle for Symfony

Symfony DataTables Bundle This bundle provides convenient integration of the popular DataTables jQuery library for realtime Ajax tables in your Symfon

Omines Internetbureau 199 Jan 3, 2023
GraphQL Bundle for Symfony 2.

Symfony 2 GraphQl Bundle Use Facebook GraphQL with Symfony 2. This library port laravel-graphql. It is based on the PHP implementation here. Installat

Sergey Varibrus 35 Nov 17, 2022
An Unleash bundle for Symfony applications to provide an easy way to use feature flags

Unleash Bundle An Unleash bundle for Symfony applications. This provide an easy way to implement feature flags using Gitlab Feature Flags Feature. Ins

Stogon 7 Oct 20, 2022
Symfony Health Check Bundle Monitoring Project Status

Symfony Health Check Bundle Version Build Status Code Coverage master develop Installation Step 1: Download the Bundle Open a command console, enter y

MacPaw Inc. 27 Jul 7, 2022
A bundle providing routes and glue code between Symfony and a WOPI connector.

WOPI Bundle A Symfony bundle to facilitate the implementation of the WOPI endpoints and protocol. Description The Web Application Open Platform Interf

Champs-Libres 5 Aug 20, 2022
Mediator - CQRS Symfony bundle. Auto Command/Query routing to corresponding handlers.

Installation $ composer require whsv26/mediator Bundle configuration // config/packages/mediator.php return static function (MediatorConfig $config)

Alexander Sv. 6 Aug 31, 2022
Symfony bundle integrating server-sent native notifications

Symfony UX Notify Symfony UX Notify is a Symfony bundle integrating server-sent native notifications in Symfony applications using Mercure. It is part

Symfony 6 Dec 15, 2022
Bundle to integrate Tactician with Symfony projects

TacticianBundle Symfony2 Bundle for the Tactician library https://github.com/thephpleague/tactician/ Installation Step 1: Download the Bundle Open a c

The League of Extraordinary Packages 240 Jan 4, 2023