Why Benzene?
Mix transports without the glue
Serverless ties your logic to its trigger. An SNS function can't also take SQS, and putting a queue in front of an HTTP service is bespoke plumbing. A Benzene handler is plain C# against a topic, so the same logic is reachable over HTTP, SQS, SNS, Kafka, and more at the same time. You add or change a transport in the wiring, never in the handler.
See what every service does
Handlers, topics, payloads, and validation rules are introspectable. Benzene generates OpenAPI and AsyncAPI specs and a live service map straight from your code, so your contract and cross-service topology track what actually runs instead of a hand-drawn diagram that drifts.
Test-first, out of the box
Every transport ships a test host and helpers, so you exercise a handler exactly as SQS, Lambda, or HTTP would invoke it: in memory, in a normal unit test, with no cloud and no emulator. Mock a dependency, send a message, and assert the result.
Cross-cutting concerns, once
Correlation IDs, logging, tracing, validation, retries, and health checks are composable middleware shared across every transport. Write them once instead of scattering them across handlers or re-implementing them per event source.
The core idea
Benzene separates what your service does from how it's invoked. A message handler contains your logic. A transport turns an incoming request into a message and routes it to the matching handler through the middleware pipeline — the same pipeline, whichever transport is on the other end, and however many of them at once.
Quickstart
A message handler, mapped to a topic
[Message("hello:world")]
[HttpEndpoint("GET", "/hello/{name}")]
public class HelloWorldMessageHandler : IMessageHandler<HelloWorldRequest, HelloWorldResponse>
{
public Task<IBenzeneResult<HelloWorldResponse>> HandleAsync(HelloWorldRequest message)
{
return Task.FromResult(BenzeneResult.Ok(new HelloWorldResponse { Message = $"Hello {message.Name}" }));
}
}
Hosted as one AWS Lambda, reached over four transports
public class StartUp : BenzeneStartUp
{
public override void ConfigureServices(IServiceCollection services, IConfiguration config) =>
services.UsingBenzene(x => x.AddMessageHandlers(typeof(HelloWorldMessageHandler).Assembly));
public override void Configure(IBenzeneApplicationBuilder app, IConfiguration config) =>
app.UseAwsLambda(aws =>
{
// One function, the same handler reached four ways:
aws.UseApiGateway(http => http.UseMessageHandlers()); // HTTP
aws.UseSqs(sqs => sqs.UseMessageHandlers()); // SQS queue
aws.UseSns(sns => sns.UseMessageHandlers()); // SNS topic
aws.UseEventBridge(events => events.UseMessageHandlers()); // EventBridge
});
}
public class Function : AwsLambdaHost<StartUp>;
dotnet add package Benzene.Aws.Lambda.ApiGateway --prerelease
Add Benzene.Aws.Lambda.Sqs, .Sns, and
.EventBridge the same way for the other transports —
or a different host entirely.
See the full AWS walkthrough →
And it runs wherever you already are
Cloud portability is the bonus, not the pitch — most teams pick one platform and stay. The point is that Benzene meets whichever one your platform team already chose: the same handlers run unchanged on any of these, so "which host" stays a deployment detail rather than an architecture decision.
Try it live
No sign-up, no install — these are the same self-contained dashboard pages your own Benzene services would serve, running here against sample data.
Mesh UI
A service-mesh dashboard over sample health checks, contract drift, and cross-service traffic.
Spec UI
A Swagger-UI-style browser for a sample Benzene message spec — topics, payloads, and validation rules.
Built for production, not just prototypes
The quickstart is five minutes; the reason to adopt Benzene is what happens after. Three deeper looks, for whoever is asking the question:
Why Benzene
The case for adopting it — lower cost of change, less lock-in, quality by construction, built to last. Read on →
Architecture
Ports and adapters applied honestly: handlers, transports, one pipeline, and a service that describes itself. See how it fits →
Operations
Observability, health, failure handling, and deployment — what it takes to run it, honestly scoped. Run it in production →