What are components in EK9?

← Classes and OOP · Ref: Q111

Components in EK9 are constructs designed for dependency injection (DI). They are registered in application definitions and injected into programs and other components using the '!' suffix.

COMPONENT BASICS

Define a component under 'defines component':

  defines component
    Repository as abstract
      findAll() as abstract
        <- rtn as List of String?
      default operator ?

ABSTRACT COMPONENTS

Define abstract components as contracts. Concrete implementations extend them:

  InMemoryRepository extends Repository
    override findAll()
      <- rtn as List of String: ["item1", "item2"]

INJECTION

Use the '!' suffix to inject a registered component:

  repository as Repository!

The runtime resolves which concrete implementation was registered.

APPLICATION REGISTRATION

Register components in an application definition:

  defines application
    MyApp
      register InMemoryRepository() as Repository

PROGRAM WITH APPLICATION

Link a program to an application for injection:

  Program1 with application of MyApp
    repository as Repository!
    items <- repository.findAll()

COMPONENT VS CLASS

Components are for DI-managed singletons and services. Classes are for regular objects created directly.

See Q93 for class basics. See Q103 for abstract classes. See Q112 for services. See Q113 for text constructs. See Q114 for aspects. See Q119 for constructs overview. See Q227 for compile-time DI validation. See Q228 for registration ordering. See Q231 for program-application linking.

See Q324 for @Autowired equivalent. See Q325 for @Component equivalent.

See Q667 for abstract injection only. See Q668 for injectable contexts. See Q672 for program-application link.

Example

defines module qa.oop.components

  defines component

    Repository as abstract
      findAll() as abstract
        <- rtn as List of String?

      default operator ?

    InMemoryRepository extends Repository
      override findAll()
        <- rtn <- List() of String
        rtn += "Alice"
        rtn += "Bob"
        rtn += "Charlie"

      default operator ?

  defines application

    MyApp
      register InMemoryRepository() as Repository

  defines program

    ComponentDemo() with application of MyApp
      stdout <- Stdout()

      // === INJECTION with ! suffix ===

      repository as Repository!

      // === USE INJECTED COMPONENT ===

      items <- repository.findAll()
      for item in items
        stdout.println(`Item: ${item}`)

      stdout.println(`Total: ${length items}`)

Common mistakes

E05120 — When implementing an abstract method from the parent component, the 'override' keyword is mandatory. InMemoryRepository must use 'override findAll()'. See ek9 -h E05120 for details.

Incorrect:

findAll()

Correct:

override findAll()

E50040 — Without 'as abstract', Repository contains abstract method 'findAll()' but the component itself is not marked abstract. The compiler reports E50040 — cannot be abstract in a non-abstract construct. See ek9 -h E50040 for details.

Incorrect:

Repository

Correct:

Repository as abstract

E07250 — Component methods cannot use the 'protected' access modifier. Components are DI-managed singletons without subclass hierarchies, so 'protected' is meaningless. Use 'private' or 'public' instead. See ek9 -h E07250 for details.

Incorrect:

override protected findAll()

Correct:

override findAll()

E08150 — Only abstract components can be injected. Injecting a concrete component directly bypasses the DI contract — always inject the abstract type and register the concrete implementation in the application. See ek9 -h E08150 for details.

Incorrect:

repository as InMemoryRepository!

Correct:

repository as Repository!

E50010 — Dependency injection with '!' only works with components. DataProcessor is a class, not a component — classes are constructed directly, not injected. Use 'defines component' for injectable types. See ek9 -h E50010 for details.

Incorrect:

processor as DataProcessor!

Correct:

repository as Repository!

E50020 — Changing 'defines component' to 'defines class' makes Repository a class instead of a component. The register statement requires component types, so the compiler rejects it with E50020 — incompatible genus. Components and classes are distinct constructs in EK9. See ek9 -h E50020 for details.

Incorrect:

defines class

Correct:

defines component

E50080 — Repository is declared 'as abstract' and cannot be instantiated directly. The register statement must use a concrete implementation (InMemoryRepository), not the abstract base type. See ek9 -h E50080 for details.

Incorrect:

register Repository() as Repository

Correct:

register InMemoryRepository() as Repository
Other ways to ask this
  • How does dependency injection work in EK9?
  • What is the component construct in EK9?
  • How do I use IoC containers in EK9?

Coming from another language?

Java: Spring @Component/@Service with @Autowired injection, CDI @Inject. Python: no built-in DI, frameworks like dependency-injector or FastAPI Depends. Rust: no built-in DI, manual wiring or shaku crate. Go: no built-in DI, wire or dig packages. Kotlin: Koin or Dagger for DI, no language-level support. EK9: 'defines component' is a language-level construct, 'register X as Y' in application, '!' suffix for injection, compile-time validated DI.

Keywords: injection, dependency, component, register, service, inversion, object-oriented, application, inject, abstract, ioc