eventuous icon indicating copy to clipboard operation
eventuous copied to clipboard

Cannot use more than one gateway transformation

Open alex91352 opened this issue 1 year ago • 1 comments

The method GatewayRegistrations.AddGateway<TSubscription, TSubscriptionOptions, TProducer, TProduceOptions, TTransform> registers a GatewayHandler in the SubscriptionBuilder.

Since AddGateway passes the GatewayHandler to AddEventHandler as an IEventHandler, and AddEventHandler tries to register the handler in the service collection as a singleton, this means that only the first GatewayHandler is registered in the service collection. As a result, the first GatewayHandler (and its transformation) is used for all gateway subscriptions.

https://github.com/Eventuous/eventuous/blob/ed2c62edfc6f4b3d1c02e176be2f8d7e6a42cd6c/src/Gateway/src/Eventuous.Gateway/Registrations/GatewayRegistrations.cs#L127-L157

https://github.com/Eventuous/eventuous/blob/ed2c62edfc6f4b3d1c02e176be2f8d7e6a42cd6c/src/Core/src/Eventuous.Subscriptions/Registrations/SubscriptionBuilder.cs#L47-L53

For example, if I register two gateway subscriptions:

services.AddGateway<StreamSubscription, StreamSubscriptionOptions, EventStoreProducer, EventStoreProduceOptions, SellOrderTransformation>("SellOrders", ...);
services.AddGateway<StreamSubscription, StreamSubscriptionOptions, EventStoreProducer, EventStoreProduceOptions, BuyOrderTransformation>("BuyOrders", ...);

then both subscriptions will use SellOrderTransformation.

alex91352 avatar Aug 22 '23 15:08 alex91352

Yeah, that can actually be fixed the same way as event handlers and checkpoint stores are attached to subscriptions. The problem is that gateways DI integration is way less comprehensive than DI stuff for subs as it took me weeks to figure things out there. It definitely worth spending time and figure better DI support for gateways as well.

alexeyzimarev avatar Aug 22 '23 16:08 alexeyzimarev

Hi, any update on this? How do you use multiple gateways? Or do you use just a single gateway for all cases?

A quick workaround we use is to create a custom sub class of TProduceOptions per subscription.

gius avatar May 06 '24 13:05 gius

Can you post the complete code for those?

services.AddGateway<StreamSubscription, StreamSubscriptionOptions, EventStoreProducer, EventStoreProduceOptions, SellOrderTransformation>("SellOrders", ...);
services.AddGateway<StreamSubscription, StreamSubscriptionOptions, EventStoreProducer, EventStoreProduceOptions, BuyOrderTransformation>("BuyOrders", ...);

alexeyzimarev avatar Jun 13 '24 16:06 alexeyzimarev

I managed to create a quick fix. It won't work with functions but it will work with transformer classes. Here's the registration function (it existed before):

    public static IServiceCollection AddGateway<TSubscription, TSubscriptionOptions, TProducer, TProduceOptions, TTransform>(
            this IServiceCollection                                           services,
            string                                                            subscriptionId,
            Action<TSubscriptionOptions>?                                     configureSubscription = null,
            Action<SubscriptionBuilder<TSubscription, TSubscriptionOptions>>? configureBuilder      = null,
            bool                                                              awaitProduce          = true
        )
        where TSubscription : EventSubscription<TSubscriptionOptions>
        where TProducer : class, IEventProducer<TProduceOptions>
        where TProduceOptions : class
        where TTransform : class, IGatewayTransform<TProduceOptions>
        where TSubscriptionOptions : SubscriptionOptions

I will create a PR to support multiple transformers there, but it still has limitations. For example, the same transformer can't be used in more than gateway as the registration function I mentioned above adds a singleton GatewayHandler<TTransform, TProduceOptions> so they must be unique with generic constraints to avoid the same issue.

I need to add a test to the PR before I merge it.

I also think that the registration code for subscriptions can be simplified further as most of the complexity there is to overcome missing keyed dependencies, but this is now solved.

alexeyzimarev avatar Jun 13 '24 16:06 alexeyzimarev

Ok, I managed to change the registration code to use keyed dependencies. It's all a bit simpler now and handlers are completely independent. So, everything should work: functions, classes, whatever.

alexeyzimarev avatar Jun 14 '24 15:06 alexeyzimarev