Skip to content

How to Implement and Wire a New Module in `operations`

This guide shows the minimal steps to add a new module in a component repository, following the patterns used by existing modules (e.g. itemModule, businessAffiliateModule).

Implement Module.kt

  1. Create a Module.kt under a new package (example: operations/src/main/kotlin/cards/arda/operations/<area>/<module>/Module.kt).
  2. Add a module entrypoint function shaped like other modules:
    • It is a Ktor extension on Application.
    • It accepts ComponentConfiguration, EndpointLocator.Rest, ModuleConfig, and Authentication.
    • It optionally accepts injected Universe(s) and/or a Service for tests.
    • It returns the Service instance used by the module.

Example skeleton:

fun Application.myModule(
  inComponent: ComponentConfiguration,
  locator: EndpointLocator.Rest,
  cfg: ModuleConfig,
  authentication: Authentication,
  injectedUniverse: MyUniverse? = null,
  injectedService: MyService? = null,
): MyService {
  val dsCfg = cfg.dataSource ?: throw AppError.ArgumentValidation(
    "cfg.dataSource",
    "DataSource is required for My Module"
  )
  val db: Database = DataSource(dsCfg.db, dsCfg.pool).newDb(dsCfg.flywayConfig)

  val service = injectedService ?: MyService.Impl(injectedUniverse ?: MyUniverse(), db)
  val endpoint = MyEndpoint.Impl(cfg, locator, service)

  MultiEndpointKtorModule(inComponent, cfg, authentication, listOf(endpoint))
    .configureServer(this)

  return service
}

Wire the module in runtime/Main.kt

  1. In your module Module.kt, add a thin wiring function that:
    • Is a Ktor extension on Application (example: fun Application.<moduleName>(cfgProvider: ConfigurationProvider, authentication: Authentication, ...): <Service>).
    • Reads the ModuleConfig from cfgProvider.moduleConfiguration("<module-config-key>").
    • Creates an EndpointLocator.Rest with cfgProvider.component.baseUrl, cfg.version, cfg.name, and the module resource.
    • Calls your module entry point (Application.myModule(...)) and returns the service.
  2. In cards.arda.operations.runtime.Main.kt, keep only generic application bootstrap:
    • Create Authentication once from cfgProvider.globalAuthConfiguration.
    • Install component-wide features (OpenAPI, auth realms, etc.).
    • Call the module wiring functions so each module stays responsible for its own configuration key and route locator setup.

Notes

  • Prefer the injected* parameters in Module.kt for unit tests to avoid requiring external dependencies.
  • If the module needs a DB, use DataSource(...).newDb(...) with the module cfg.dataSource and Flyway configuration, like other modules.
  • For unit tests that rely on a configuration, currently the required values that are provided by the ci/cd environment need to be set in the build.gradle.kts definition. See the operations repo build.gradle.kts for examples.

Copyright: © Arda Systems 2025-2026, All rights reserved

Comments