How do components inject other components in EK9?

← Dependency Injection · Ref: Q232

Components inject other components using the '!' suffix on fields, exactly like programs inject components. The compiler resolves these transitive chains at compile time.

FIELD INJECTION IN COMPONENTS

A concrete component can declare injection fields:

  ConcreteService is AbstractService
    repo as Repository!
    override doWork()
      <- rtn as String: repo.find(1)

The '!' on 'repo' tells the compiler this field is injected, not constructed locally.

TRANSITIVE CHAINS

Multi-layer dependency chains are common:

  Repository depends on nothing
  Service depends on Repository
  Controller depends on Service

The compiler traces the entire chain and verifies every link is registered.

REGISTRATION ORDER

Register in dependency order (leaves first, roots last):

  register ConcreteRepo() as Repository
  register ConcreteService() as Service
  register ConcreteController() as Controller

ALL COMPILE-TIME

Every field injection in every component is verified before code runs. The compiler sees the full dependency graph across all components and programs.

See Q228 for registration ordering. See Q111 for component basics. See Q227 for compile-time validation.

Example

defines module qa.di.chains

  defines component

    // === LAYER 1: Repository (no dependencies) ===

    Repository as abstract
      find() as abstract
        -> id as Integer
        <- result as String?

      default operator ?

    InMemoryRepo is Repository
      override find()
        -> id as Integer
        <- result as String: "Record-" + $id

      default operator ?

    // === LAYER 2: Service (depends on Repository) ===

    DataService as abstract
      process() as abstract
        -> id as Integer
        <- output as String?

      default operator ?

    DefaultDataService is DataService
      repo as Repository!

      override process()
        -> id as Integer
        <- output <- String()
        raw <- repo.find(id)
        output: "Processed: " + raw

      default operator ?

    // === LAYER 3: Controller (depends on Service) ===

    ApiController as abstract
      handle() as abstract
        -> id as Integer
        <- response as String?

      default operator ?

    DefaultApiController is ApiController
      service as DataService!

      override handle()
        -> id as Integer
        <- response <- String()
        result <- service.process(id)
        response: "Response: " + result

      default operator ?

  defines application

    ChainedApp
      // Register in dependency order: leaves first
      register InMemoryRepo() as Repository
      register DefaultDataService() as DataService
      register DefaultApiController() as ApiController

  defines program

    ComponentChainDemo() with application of ChainedApp
      stdout <- Stdout()

      // === THREE-LAYER INJECTION CHAIN ===

      controller as ApiController!

      response <- controller.handle(42)
      stdout.println(response)

      stdout.println("Repository -> Service -> Controller chain resolved at compile time")

Common mistakes

E08150 — Injection fields must use abstract types. Concrete types like InMemoryRepo cannot be injection targets because DI resolves through abstract-to-concrete registration. See ek9 -h E08150 for details.

Incorrect:

repo as InMemoryRepo!

Correct:

repo as Repository!
Other ways to ask this
  • Can EK9 components depend on other components?
  • How do transitive dependency chains work in EK9?
  • How does field injection work between components?

Coming from another language?

Java Spring: @Autowired field injection between @Component classes, resolved at runtime via container. Guice: @Inject field injection, resolved at Injector creation. .NET: constructor injection between registered services. Python: no built-in component injection. Go: manual struct composition. Rust: no built-in DI. EK9: '!' field injection between components, transitive chains resolved at compile time, registration order enforced.

Keywords: transitive, injection, component, field, depend, service, controller, layer, chain, repository, inject