Does EK9 support DI scopes like singleton, prototype, and request?
← Dependency Injection · Ref: Q330
EK9 has exactly one DI scope: singleton for the program lifetime. There are no prototype, request, session, or application scopes. This is a deliberate design decision.
WHY SINGLETON ONLY
1. SCOPE MISMATCH BUGS: In Spring, injecting a request-scoped bean into a singleton silently breaks. This is one of the most common and hardest-to-diagnose DI bugs. EK9 eliminates this entire bug category.
2. SIMPLICITY: One scope means one mental model. No scoped proxies, no scope resolution strategies, no lifecycle callbacks per scope.
3. PREDICTABILITY: Every injection point references the same instance throughout program execution.
WHAT ABOUT PROTOTYPE SCOPE?
Spring's prototype scope creates a new instance per injection point. In EK9, if you need fresh instances, create them explicitly in your code. This makes object creation visible rather than hidden behind framework magic.
WHAT ABOUT REQUEST SCOPE?
For web services, EK9 creates request-specific state explicitly within the service handler. The service component is a singleton, but it creates per-request state using local variables and function calls.
HOW TO HANDLE DIFFERENT LIFETIMES
Use explicit construction for short-lived objects:
service.handle(request) localState <- RequestState(request) processWithState(localState)
The component is a singleton; the request state is a local variable. No scope annotation needed.
See Q234 for component lifecycle. See Q117 for singleton pattern. See Q227 for compile-time validation. See Q329 for Guice scope comparison.
Example
defines module qa.di.scopes defines component RequestHandler as abstract handle() as abstract -> path as String <- response as String? default operator ? <?- The handler is a singleton component. Per-request state is created as local variables. -?> WebHandler is RequestHandler override handle() -> path as String <- response <- String() // Per-request state created locally, not scope-managed requestId <- "REQ-" + path timestamp <- "2026-02-26" response: `${requestId} at ${timestamp}` default operator ? defines application SingletonApp register WebHandler() as RequestHandler defines program ScopeDemo() with application of SingletonApp stdout <- Stdout() // === SINGLETON COMPONENT, LOCAL STATE PER REQUEST === handler as RequestHandler! // Simulate multiple requests to same singleton handler response1 <- handler.handle("/users") stdout.println(response1) response2 <- handler.handle("/orders") stdout.println(response2) stdout.println("Singleton component handles all requests") stdout.println("Per-request state uses local variables")
Common mistakes
E08150 — Injection fields must use abstract types. EK9 has singleton scope only, and the abstract type is the contract through which the singleton instance is accessed. See ek9 -h E08150 for details.
Incorrect:
handler as WebHandler!
Correct:
handler as RequestHandler!
E50001 — Renaming the variable means later references to 'response1' become unresolved, triggering E50001. See ek9 -h E50001 for details.
Incorrect:
response1XYZ <- handler.handle("/users")
Correct:
response1 <- handler.handle("/users")
Other ways to ask this
- Can I create request-scoped or prototype-scoped beans in EK9?
- Why does EK9 only have singleton scope?
- How do I handle different component lifetimes in EK9?
Coming from another language?
Java Spring: singleton (default), prototype, request, session, application, websocket scopes. Scope mismatch is a common bug source. Guice: unscoped, @Singleton, @RequestScoped, custom scopes via Scope interface. .NET: transient, scoped, singleton. Quarkus: @ApplicationScoped, @RequestScoped, @Dependent. EK9: singleton only, explicit construction for short-lived objects, no scope mismatch bugs possible, local variables for per-request state.
Keywords: transient, request, migrate, mismatch, singleton, lifetime, dependency, proxy, prototype, inject, scope, simplicity, session