How does EK9 detect circular component injection at compile time?
← DI Validation · Ref: Q797
EK9 detects circular dependency chains in component injection at compile time (E08190). When Component A depends on Component B, and Component B depends on Component A (directly or transitively), neither can be constructed first.
CIRCULAR CHAIN
ComponentA -> needs AbstractB -> provided by ComponentB ComponentB -> needs AbstractA -> provided by ComponentA DEADLOCK: neither can be created first
COMPILE-TIME DETECTION
Unlike Spring (runtime BeanCurrentlyInCreationException), EK9 catches circular dependencies before any code runs. The compiler analyses the full injection graph and rejects cycles.
BREAKING CYCLES
1. One-way dependency: only one component injects the other
2. Extract shared logic into a third component
3. Use an event or callback pattern instead of direct injection
See Q671 for the general circular dependency explanation. See Q673 for missing registration. See Q674 for duplicate registration.
Example
defines module qa.divalidation.circularcompiletime defines component Handler as abstract handleEvent() as abstract -> eventName as String <- handled as Boolean? default operator ? NotificationHandler extends Handler override handleEvent() -> eventName as String <- handled as Boolean: true default operator ? Dispatcher as abstract dispatch() as abstract -> eventName as String <- dispatched as Boolean? default operator ? EventDispatcher extends Dispatcher handler as Handler! override dispatch() -> eventName as String <- dispatched as Boolean? dispatched: handler.handleEvent(eventName) default operator ? defines application OneWayApp register NotificationHandler() as Handler register EventDispatcher() as Dispatcher defines program CircularDiDemo() with application of OneWayApp stdout <- Stdout() dispatcher as Dispatcher! sent <- dispatcher.dispatch("user-login") stdout.println(`Dispatched: ${sent}`)
Common mistakes
E08190 — Injecting Dispatcher into NotificationHandler creates a circular dependency (Handler -> Dispatcher -> Handler) that the compiler rejects at compile time. See ek9 -h E08190 for details.
Incorrect:
NotificationHandler extends Handler dispatcher as Dispatcher! override handleEvent()
Correct:
NotificationHandler extends Handler override handleEvent()
Other ways to ask this
- What triggers E08190 DI circular dependency?
- Why does mutual component injection fail to compile?
- How do I break a circular dependency chain in EK9 DI?
Coming from another language?
Java: Spring throws BeanCurrentlyInCreationException at runtime. Python: no DI framework detection. Go: Wire detects at code generation time. Rust: no standard DI. Kotlin: Koin throws at runtime. EK9: compile-time E08190 rejects circular chains before deployment.
Keywords: injection, cycle, deadlock, DI, circular, compile-time, dependency, component, E08190