How does EK9 validate DI component registration and dependency ordering?
← DI Validation · Ref: Q691
EK9 validates the complete dependency injection graph at compile time. All components must be registered in the application, and the compiler checks that all dependencies can be satisfied.
REGISTRATION REQUIREMENT (E08210)
Every abstract component that is injected must have a concrete implementation registered in the application. A missing registration triggers E08210 ('... which is never registered in this application').
DEPENDENCY CHAIN
If ComponentA depends on ComponentB, and ComponentB depends on ComponentC, all three must be registered. The compiler walks the entire dependency chain.
REGISTRATION ORDER (E08200)
Registrations are made in the order they are written, so each dependency must be registered BEFORE the component that injects it. Registering a dependent first triggers E08200 ('... which is not yet registered, reorder registrations'):
register OrderCoordinator() as Coordinator // E08200: needs OrderService, not yet registered register DefaultOrderService() as OrderService // E08200: needs Repository, not yet registered register InMemoryRepository() as Repository
Fix: register bottom-up - Repository, then OrderService, then Coordinator - as the example below does.
ABSTRACT INJECTION RULE
DI only works with abstract components:
defines component DataAccess as abstract load() as abstract <- rtn as String? default operator ?
DatabaseAccess extends DataAccess override load() <- rtn as String: "row" default operator ?
DataAccess can be injected; DatabaseAccess is the concrete implementation registered for it.
Concrete components cannot be directly injected.
APPLICATION WIRING
The application block registers concrete implementations:
defines application MyApp register DatabaseAccess() as DataAccess register OrderLogic() as BusinessLogic
See Q675 for transitive DI validation. See Q673 for missing registration. See Q674 for duplicate registration.
Example
defines module qa.divalidation.registrationorder defines component <?- Layer 1: Abstract repository. Bottom of the dependency chain. -?> Repository as abstract findByKey() as abstract -> lookupKey as String <- record as String? default operator ? <?- Concrete repository implementation. -?> InMemoryRepository extends Repository override findByKey() -> lookupKey as String <- record as String: "" record: "record:" + lookupKey default operator ? <?- Layer 2: Abstract service. Depends on Repository (injected). -?> OrderService as abstract processOrder() as abstract -> orderId as String <- confirmation as String? default operator ? <?- Concrete service implementation. Injects Repository (must be registered). -?> DefaultOrderService extends OrderService repository as Repository! override processOrder() -> orderId as String <- confirmation as String: "" if lookupResult <- repository.findByKey(orderId) confirmation: "Confirmed: " + lookupResult default operator ? <?- Layer 3: Abstract coordinator. Depends on OrderService (injected). -?> Coordinator as abstract coordinate() as abstract -> taskId as String <- outcome as String? default operator ? <?- Concrete coordinator implementation. Injects OrderService (transitive chain). -?> OrderCoordinator extends Coordinator orderService as OrderService! override coordinate() -> taskId as String <- outcome as String: "" if result <- orderService.processOrder(taskId) outcome: "Coordinated: " + result default operator ? defines application <?- Application must register ALL levels of the dependency chain (a missing one is E08210), each dependency before the component that injects it (out of order is E08200). -?> OrderApp register InMemoryRepository() as Repository register DefaultOrderService() as OrderService register OrderCoordinator() as Coordinator defines program DiRegistrationOrderDemo() with application of OrderApp stdout <- Stdout() coordinator as Coordinator! if outcome <- coordinator.coordinate("ORD-001") stdout.println(outcome) stdout.println("All three layers registered and validated at compile time")
Common mistakes
E08210 — The compiler validates that all components in the dependency chain have registrations. OrderCoordinator depends on OrderService, which depends on Repository. All three must be registered in the application; a missing registration in the chain is E08210. See ek9 -h E08210 for details.
Incorrect:
OrderApp
register OrderCoordinator() as Coordinator
Correct:
OrderApp
register InMemoryRepository() as Repository
register DefaultOrderService() as OrderService
register OrderCoordinator() as Coordinator
E08200 — Each dependency must be registered before the component that injects it. Registering OrderCoordinator before OrderService (and DefaultOrderService before Repository) is E08200: reorder the registrations bottom-up. See ek9 -h E08200 for details.
Incorrect:
register OrderCoordinator() as Coordinator register DefaultOrderService() as OrderService register InMemoryRepository() as Repository
Correct:
register InMemoryRepository() as Repository register DefaultOrderService() as OrderService register OrderCoordinator() as Coordinator
Other ways to ask this
- What is E08200 DI registration order error?
- Does EK9 validate dependency injection at compile time?
- How must components be registered in an application?
- What happens if a dependency is registered after its dependent?
Coming from another language?
Java: Spring validates at startup (runtime). Python: runtime discovery. Go: manual wiring (compile-time). Rust: no DI framework. Kotlin: Koin validates at runtime. EK9: full compile-time DI validation of the complete dependency graph.
Keywords: injection, dependency, validate, component, order, registration, E08200, E08210, compile-time, application, inject, sanitize, DI