Inject services via async-graphql Data context
Context
Resolvers need access to services (database pools, external API clients, domain services). Options: a DI framework, a global singleton, or async-graphql's built-in Data container.
Decision
Construct all services in build_graphql_data() at startup and insert them into the GraphQL schema's Data container. Resolvers access services via extension traits on async_graphql::Context:
// startup/router.rs
let authz_service = AuthzService::new(state.db_pool.clone());
data.insert(authz_service);
// In any resolver
let authz = ctx.authz_service();
Each domain defines a *ServiceExt trait for its service, keeping the access pattern consistent across ~11 domains.
Consequences
No external DI crate. Services are request-scoped (shared reference per request). The pattern is visible and greppable — ctx.modification_service() tells you exactly what's being used. Adding a new service is: construct in build_graphql_data, insert, define ext trait.
The cost is that all services are constructed at startup regardless of whether a given request needs them, and the ext traits are boilerplate (though small).