Kanject EventHub
A strongly-typed, serverless event bus on AWS — publish typed events and fan them out to subscribers over SNS + SQS, without hand-writing topic and queue wiring. Deployed into your own account.
- Let EventHub create its tables and SNS topics in your AWS account
- Register the client with
AddServerlessEventHubClient - Publish typed
[HubEvent]events - Subscribe over HTTPS with an
IEventHubRequestHandler<T>, or through an SQS queue - Run subscribers on Lambda or in a container
Provision
EventHub creates its DynamoDB tables in your account when EnableSetup is true, and an SNS topic for each registered event topic when SyncEventTopics is true.
Register it in your service
Register the client with AddServerlessEventHubClient(serviceName, config). The serviceName scopes this service's topics and queues on the shared bus; the transport is SNS + SQS fan-out (EventBridge is used only for scheduled events).
using Kanject.ServerlessEventHub.Provider.AwsSns.Abstractions.Config;using Kanject.ServerlessEventHub.Provider.AwsSnsV4.Extensions;var builder = WebApplication.CreateBuilder(args);// serviceName scopes this service's topics/queues on the shared bus.builder.Services.AddServerlessEventHubClient( serviceName: "orders", new AwsServerlessEventHubConfiguration { AwsRegionEndpoint = appSettings.AwsRegion, /* schema names, … */ });var app = builder.Build();app.Run(); Publish typed events
Events are C# classes marked with [HubEvent(Topic, Version)]. Inject IServerlessEventHubClient (or IPublishEventAction) and call PublishAsync — the event goes to its SNS topic, and subscribed SQS queues fan out from there.
using Kanject.ServerlessEventHub.Abstractions.Attributes;// Events are typed classes, marked with a topic + version.[HubEvent(Topic = "order.completed", Version = 1)]public sealed class OrderCompleted{ public string OrderId { get; set; } = ""; public string UserId { get; set; } = ""; public decimal Amount { get; set; }}// Publish: inject IServerlessEventHubClient (or IPublishEventAction).public class OrderService(IServerlessEventHubClient hub){ public Task PublishAsync(OrderCompleted evt) => hub.PublishAsync(evt); // → SNS topic; SQS subscribers fan out} Subscribe
There are two ways to receive, and both are explicit wiring. A handler implements IEventHubRequestHandler<TEventArgs> and is registered with AddEventHubHandler<,>(): SNS delivers to your service over HTTPS, at the route UseServerlessEventHubRoutes() maps, and the signature is checked before the handler runs. Or SubscribeQueueToTopic gives a consumer its own SQS queue on the topics it reads; SyncQueueTopicsSubscription() subscribes it at startup, and your queue consumers drain it.
// Over HTTPS: implement IEventHubRequestHandler<TEventArgs> and register it.// SNS delivers to your service's URL.public sealed class LedgerProjector : IEventHubRequestHandler<OrderCompletedArgs>{ public string Topic { get; set; } = "order.completed"; public string[] Parameters { get; set; } = []; public OrderCompletedArgs Payload { get; set; } = new(); public void OnIncomingRequest(object sender, EventHubRequestHandlerArgs e) { // handle the delivered event (e.Topic, e.Message) }}// Composition root:services.AddEventHubHandler<LedgerProjector, OrderCompletedArgs>();app.UseServerlessEventHubRoutes(); // the route SNS delivers to// Or through a queue: SNS → SQS, read by your queue consumers.services.SubscribeQueueToTopic("orders-ledger", "order.completed");app.SyncQueueTopicsSubscription(); // subscribes the queue at startup Run it on Lambda or in a container
EventHub runs inside your service, so it goes wherever the service goes: a Lambda function, or your own container on ECS Fargate, EKS or App Runner. Publishing is the same PublishAsync call in both. What changes is how a queue is read:
- On Lambda — point an SQS trigger at the queue. The generated
RouteSqsEventTo…Async(sqsEvent)sends each record to the consumer for its topic and returns anSQSBatchResponse, so only the records that failed are retried. - In a container — set
WatchQueue = trueon the queue's configuration. The same consumers long-poll the queue for as long as the service runs; nothing else changes. - Over HTTPS — handlers need a route SNS can reach: a container behind a load balancer, or a Lambda behind a function URL or API Gateway.
Deploy either one with kanject aws deploy: see the deployment guide for Lambda and ECS Fargate for a container.
What you get
- Typed events — a C# class per event, marked
[HubEvent(Topic, Version)]. - SNS + SQS fan-out — publish to a topic; each subscriber drains its own SQS queue.
- Explicit subscriptions — an
IEventHubRequestHandler<T>over HTTPS, or a queue fromSubscribeQueueToTopic. - Lambda or container — queue consumers run from an SQS trigger, or poll with
WatchQueue. - Service scoping —
serviceNamenamespaces each service's topics and queues on the shared bus. - Scheduled events — an EventBridge scheduling extension for time-based publishes.
- Register with
AddServerlessEventHubClient(serviceName, config);EnableSetupandSyncEventTopicscreate its tables and topics. - The real package is
Kanject.ServerlessEventHub(brand: EventHub); transport is SNS + SQS fan-out. - Publish
[HubEvent]classes viaIServerlessEventHubClient.PublishAsync. - Subscribe over HTTPS with
IEventHubRequestHandler<T>+AddEventHubHandler<,>(), or through a queue withSubscribeQueueToTopic. - Queue consumers run on Lambda from an SQS trigger, or in a container with
WatchQueue = true.