How does EK9 validate transitive component dependencies?

← DI Validation · Ref: Q675

EK9 validates the ENTIRE dependency graph at compile time. If Program injects ComponentA, and ComponentA injects ComponentB, then BOTH must have registrations in the application.

TRANSITIVE VALIDATION

The compiler walks the full injection chain:

  Program -> ServiceA -> RepositoryB -> (no more deps)

All three levels must have registered implementations.

FAILURE AT ANY LEVEL

If RepositoryB has no registration, the compiler reports E08210 even though the program only directly injects ServiceA. The transitive chain is validated.

COMPLETE WIRING

  defines application
    FullApp
      register ConcreteRepoB() as RepositoryB
      register ConcreteServiceA() as ServiceA

Both registrations satisfy the transitive chain.

See Q673 for missing registration. See Q671 for circular dependencies. See Q666 for pure context.

Example

defines module qa.divalidation.transitive

  defines component

    <?-
      Layer 1: Abstract data access.
      Bottom of the dependency chain.
    -?>
    DataAccess as abstract

      readRecord() as abstract
        -> recordKey as String
        <- recordData as String?

      default operator ?

    <?-
      Concrete data access implementation.
    -?>
    DatabaseAccess extends DataAccess

      override readRecord()
        -> recordKey as String
        <- recordData as String: ""

        recordData: "db:" + recordKey

      default operator ?

    <?-
      Layer 2: Abstract business logic.
      Depends on DataAccess (injection).
    -?>
    BusinessLogic as abstract

      executeRule() as abstract
        -> ruleInput as String
        <- ruleOutput as String?

      default operator ?

    <?-
      Concrete business logic.
      Injects DataAccess (transitive dependency).
    -?>
    OrderLogic extends BusinessLogic

      dataAccess as DataAccess!

      override executeRule()
        -> ruleInput as String
        <- ruleOutput as String: ""

        rawData <- dataAccess.readRecord(ruleInput)
        if rawData?
          ruleOutput: "Processed: " + rawData

      default operator ?

  defines application

    <?-
      Application must register ALL levels of the dependency chain.
      Missing any level triggers E08210.
    -?>
    TransitiveApp
      register DatabaseAccess() as DataAccess
      register OrderLogic() as BusinessLogic

  defines program

    TransitiveDiDemo() with application of TransitiveApp
      stdout <- Stdout()

      logic as BusinessLogic!
      result <- logic.executeRule("order-123")
      stdout.println(result)

      stdout.println("Transitive chain validated at compile time:")
      stdout.println("  Program -> BusinessLogic -> DataAccess")

Common mistakes

E50010 — The compiler validates the entire dependency graph. OrderLogic injects DataAccess, so even though the program only directly injects BusinessLogic, the application must also register a DataAccess implementation. Missing any level in the transitive chain triggers this error. See ek9 -h E50010 for details.

Incorrect:

IncompleteApp
      register OrderLogic() as BusinessLogic

Correct:

TransitiveApp
      register DatabaseAccess() as DataAccess
      register OrderLogic() as BusinessLogic
Other ways to ask this
  • Does EK9 check nested injection chains at compile time?
  • How deep does DI validation go?
  • What if a component's dependency has unregistered dependencies?

Coming from another language?

Java: Spring validates at startup (runtime). Python: runtime discovery. Go: compile-time (manual). Rust: compile-time (no DI). EK9: full compile-time validation of transitive dependency chains.

Keywords: validate, transitive, compile-time, registration, dependency, component, DI, E08210, validation, chain, inject