Registration Order
Trax's services.AddX() extensions are meant to be callable in any order, with one exception
that is enforced loudly. Where order does matter, the host fails at startup with a message
naming the call to move. Nothing about registration is allowed to fail silently.
What order does matter
| Requirement | What happens if you get it wrong |
|---|---|
AddTrax() before AddTraxGraphQL() / AddTraxDashboard() | Throws immediately, naming the missing call. |
An authorization posture in AddTraxDashboard() options before UseTraxDashboard() | UseTraxDashboard() throws, naming RequirePolicy, RequireRoles and AllowAnonymousDashboard. |
Trains registered before AddTraxGraphQL() | The host refuses to start, naming each [TraxQuery], [TraxMutation] or [TraxBroadcast] train registered afterwards. AddTraxGraphQL() checks each train's authorization posture and name and builds the schema roots from the trains registered before it. |
builder.Services.AddTrax(trax => trax.AddEffects(...).AddMediator(...));
builder.Services.AddTraxJwtAuth(...);
builder.Services.AddTraxGraphQL(graphql => graphql.AddDbContext<AppDbContext>());What order does not matter
Everything else, and deliberately so:
@authorizefrom[TraxAuthorize]is attached to the schema, so query and mutation gating behaves identically whichever way round the host is composed.- Services the GraphQL components depend on are resolved on first use, not when
AddTraxGraphQL()runs, soAddAuthentication()andAddAuthorization()may come after it. - Repeated
AddTraxJwtAuth(...)calls accumulate into one registry regardless of order. - Auth schemes (
AddTraxJwtAuth,AddTraxApiKeyAuth,AddTraxJwtDispatcher) may come before or afterAddTraxGraphQL(). The subscription interceptor reads them from the finished container on the first connection.
Contributing to Trax
Reading the IServiceCollection inside a registration extension answers "is this registered
yet", not "will this be registered". The extension runs partway through the host's startup
code, so any behaviour derived from the answer changes with the caller's ordering.
That has shipped as a bug twice: once leaving an interceptor unable to activate (a 500 on every request), and once leaving subscription auth unwired entirely, which accepted every connection anonymously while HTTP kept working.
Inspecting the collection is not banned. Silence is. A site is acceptable only if it is:
- An idempotency guard, asking "have I already registered my own thing?" It cannot change consumer-visible behaviour.
- A precondition that throws, where ordering matters and the consumer is told how to fix it.
- A decision paired with a startup validator: wire what you can, then assert from an
IHostedService, where the container is complete, that the result is coherent, and throw naming the call to move.
Prefer restructuring so the question disappears. Registering a factory that resolves on first use makes the ordering question moot rather than merely detected.
NoSilentRegistrationOrderDependenceTests fails the build on any new introspection site.
Making one safe and recording why is the way past it; raising its count is not.