What is the EK9 equivalent of Spring @Component and @Service?

← Dependency Injection · Ref: Q325

EK9 replaces Spring's @Component and @Service with the 'defines component' construct. Components in EK9 are types that can be registered in applications and injected into programs.

SPRING ANNOTATIONS VS EK9 CONSTRUCTS

Spring uses @Component/@Service annotations on classes that are auto-scanned:

  @Service public class OrderService { ... }

EK9 uses the 'defines component' section to declare injectable types:

  defines component
    OrderService as abstract ...
    DefaultOrderService is OrderService ...

NO AUTO-SCANNING

Spring scans the classpath for annotated classes. EK9 requires explicit registration in the application definition. This is intentional: explicit registration is auditable, predictable, and compile-time verified.

ABSTRACT + CONCRETE PATTERN

EK9 components follow the abstract/concrete pattern: define an abstract component (the contract), then implement concrete components. Register the concrete as the abstract in the application. Programs inject the abstract type.

WHY NO @SERVICE/@REPOSITORY DISTINCTION

Spring has @Component, @Service, @Repository, and @Controller. These are functionally identical (all are component stereotypes). EK9 has one construct: component. Semantic distinctions belong in naming, not annotations.

See Q111 for component basics. See Q227 for compile-time validation. See Q324 for @Autowired equivalent. See Q326 for @Bean/@Configuration equivalent.

Example

defines module qa.di.spring.component

  defines component

    <?-
      Abstract component is the contract (like Spring interface).
    -?>
    NotificationService as abstract
      notify() as abstract
        -> message as String
        <- confirmation as String?

      default operator ?

    <?-
      Concrete component is the implementation (like @Service class).
    -?>
    EmailNotifier is NotificationService
      override notify()
        -> message as String
        <- confirmation as String: "Email sent: " + message

      default operator ?

    AuditService as abstract
      record() as abstract
        -> action as String
        <- entry as String?

      default operator ?

    SimpleAuditService is AuditService
      notifier as NotificationService!

      override record()
        -> action as String
        <- entry <- String()
        entry: notifier.notify("Audit: " + action)

      default operator ?

  defines application

    ComponentApp
      register EmailNotifier() as NotificationService
      register SimpleAuditService() as AuditService

  defines program

    ComponentServiceDemo() with application of ComponentApp
      stdout <- Stdout()

      // === COMPONENTS REPLACE @Component AND @Service ===

      audit as AuditService!

      result <- audit.record("user.login")
      stdout.println(result)

      stdout.println("Components defined in 'defines component' section")

Common mistakes

E08150 — Injection fields must declare the abstract component type. The concrete EmailNotifier is registered against the abstract NotificationService in the application, not injected directly. See ek9 -h E08150 for details.

Incorrect:

notifier as EmailNotifier!

Correct:

notifier as NotificationService!

E08200 — SimpleAuditService injects NotificationService, so NotificationService must be registered before SimpleAuditService. Reversing the order causes a missing dependency at that point in the registration sequence. See ek9 -h E08200 for details.

Incorrect:

register SimpleAuditService() as AuditService
      register EmailNotifier() as NotificationService

Correct:

register EmailNotifier() as NotificationService
      register SimpleAuditService() as AuditService

E50001 — Renaming the variable means later references to 'result' become unresolved, triggering E50001. See ek9 -h E50001 for details.

Incorrect:

resultXYZ <- audit.record("user.login")

Correct:

result <- audit.record("user.login")
Other ways to ask this
  • How do I define injectable components in EK9?
  • What replaces @Component and @Service annotations in EK9?
  • How does EK9 component scanning work?

Coming from another language?

Java Spring: @Component, @Service, @Repository, @Controller stereotypes with classpath scanning. Guice: @Provides methods in Module classes. .NET: services.AddTransient/AddScoped/AddSingleton in ConfigureServices. Python: no language-level component declaration. Go: no language-level DI. EK9: 'defines component' section with abstract/concrete pattern, explicit registration in application, no classpath scanning.

Keywords: injectable, define, register, annotation, construct, inject, spring, component, migration, stereotype, dependency, service