Porting Guide

Status: DRAFT v0.1

Notes for implementing Benzene in another language. The strategy is spec-first: a port implements core-concepts.md, wire-contracts.md, and transport-bindings.md idiomatically — it does not translate the C# API. Interop with .NET Benzene services comes from the wire contracts, and is the first thing a port should prove, not the last.

1. What is concept vs C# idiom

.NET mechanism The concept underneath Idiomatic equivalent elsewhere
[Message("topic")] attribute + assembly scanning Explicit handler registration records (core-concepts §9) Explicit registration calls (Go, Rust); decorators (TS/Python); codegen
IMessageHandler<TRequest,TResponse> generic interface handle: TRequest → Result<TResponse> A function type / single-method interface; generics where available, per-registration types where not
MS DI + IBenzeneServiceContainer adapter Registration + per-invocation scope + overridable defaults (core-concepts §8) A context/registry object (Go); constructor injection frameworks where cultural (Java/TS)
IMiddleware<TContext> + Func<Task> next Ordered onion pipeline with short-circuit (core-concepts §4) The language's standard middleware shape (Go http-style wrappers, Express/Koa, Python ASGI-like)
IAsyncEnumerable<T> streaming handlers Async stream of items, one pipeline run per call (core-concepts §3) Channels (Go), async generators (TS/Python), Stream (Rust)
GrpcServerCallAccessor scoped accessor Invocation-scoped facts available to handler code without transport coupling Context values (Go context.Context), ALS (Node), contextvars (Python)
Reflection-cached protobuf Descriptor/Parser lookups proto3-JSON bridging rule (wire-contracts §6) Each language's protobuf library exposes the same JSON mapping natively
BenzeneStartUp abstract class The three-phase lifecycle + platform no-op rule (core-concepts §7) A builder or plain functions; the ordering and no-op semantics are what must survive

Rule of thumb: if removing a mechanism would change what's on the wire or what a handler observes, it's a concept and it's in the spec. Otherwise it's an idiom — do what's natural in the target language.

2. Suggested porting order

  1. Wire contracts first: envelope + status vocabulary + header conventions, verified against a running .NET Benzene service (send/receive the envelope, assert statuses round-trip). Cross-language interop is the product; prove it in week one.
  2. Result type, topic, registry, pipeline (with short-circuit + scope semantics).
  3. One inbound binding (HTTP is the cheapest) end-to-end, including status mapping and the correlation/trace middleware.
  4. One outbound client + decorators.
  5. Health checks (reserved topic + response format).
  6. Further bindings by demand — each is additive.

3. Conformance testing

A language-neutral test suite that every implementation runs:

Conformance comes in two levels (cloud-service-profile.md): the fixtures above establish Benzene Core. A port that also wants its services to claim the Cloud Service Profile additionally implements the service-side mesh feeds and passes mesh-descriptor-cases.json and mesh-trace-cases.json. Plan for the profile from the start if the port's services should appear in a mesh — it is the difference between "interoperates" and "fully tool-operable".

4. Known .NET-isms that must NOT leak into the spec

Recorded so they don't get accidentally specified: