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 (E08200)

Every abstract component that is injected must have a concrete implementation registered in the application. Missing registrations trigger E08200.

DEPENDENCY CHAIN

If ComponentA depends on ComponentB, and ComponentB depends on ComponentC, all three must be registered. The compiler walks the entire dependency chain.

ABSTRACT INJECTION RULE

DI only works with abstract components:

  DataAccess as abstract       // Can be injected
  DatabaseAccess extends DataAccess  // Concrete implementation

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.
      Missing any level triggers 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

E50010 — 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. Missing any registration in the chain triggers this error. See ek9 -h E50010 for details.

Incorrect:

IncompleteApp
      register OrderCoordinator() as Coordinator

Correct:

OrderApp
      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: sanitize, inject, application, injection, order, compile-time, DI, dependency, registration, validate, E08200, component