Skip to content

Backend Implementation Patterns

This document is the consolidated reference for Arda’s backend implementation patterns, organized by layer. Code path references use /repo-name/... format. “Docs” links point to design documentation; “Code” links point to implementation entry points.

For the conceptual index with brief descriptions, see Design Pattern Index. For FP philosophy, see Functional Programming.


Values vs Entities Model identity-bearing, mutable state as Entities and pure immutable data as Values. Use this split to drive module boundaries, persistence decisions, and API shapes.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/information-model/entities.md, /technical-documentation/contents/2_design/2_functional/general/information-model/value-types.md

Journalled Entities Treat changes as an immutable record lineage to support auditability and “as-of” queries.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/information-model/journaled-entities.md

Inter-Entity Relationship Patterns Choose explicit relationship styles (extension, aggregation, composition/parent-child, association entity) and implement them via references + universes/queries.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/information-model/entities.md

Entity References (Pinned vs Floating) + ADN Standardize cross-module references as (a) structured reference URIs and/or (b) ADN strings. Use pinned references for immutable version addressing and floating references for “latest-as-of context” semantics.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/information-model/entity-references.md, /technical-documentation/contents/2_design/2_functional/general/information-model/arda-domain-name.md

Reference Data Lifecycle + Edit–Draft–Publish Separate draft editing from bitemporal history. Publishing creates new history records.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/information-model/reference-data.md, /technical-documentation/contents/1_specifications/patterns/edit-draft-publish.md

Runtime Component: Wiring and Configuration

Section titled “Runtime Component: Wiring and Configuration”

Runtime Containment Model Structure runtime as Infrastructure → Partition → Component. Use this to reason about isolation and naming.

  • Docs: /technical-documentation/contents/2_design/3_runtime/arda-platform-structure.md, /technical-documentation/contents/2_design/3_runtime/runtime-environments.md

Composite Config Loading Load configuration from a colon-separated set of files/directories (-Darda.config.location=...) layered over Typesafe defaults. Use to compose environment-specific runtime config without rebuilding artifacts.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/util/config/CompositeConfigFactory.kt

Typed Configuration Accessors Centralize reading/validation of component, ktor, auth, and module-specific config blocks. Use to keep module wiring predictable and fail fast on bad config.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/component/ConfigurationProvider.kt

Endpoint Locator Values Carry endpoint identity (baseUrl, version, name, resource) as data plus strictValidate vs floatingValidate. Use to pass around endpoint identity consistently.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/module/EndpointLocator.kt

Ktor Module Composition Compose multiple endpoints under a module root, consistently applying secure vs unsecure routes and installing context plugins for secure routes.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/module/KtorModule.kt

Component Server Bootstrap Install Ktor plugins in a consistent bundle: call-id propagation, request logging, error normalization via StatusPages, JSON config, monitoring plugins, and authentication.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/component/Component.kt

The canonical Module.kt structure:

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"
)
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
}

Notes:

  • Prefer injected* parameters for unit tests to avoid external dependencies
  • In runtime/Main.kt, create Authentication once from cfgProvider.globalAuthConfiguration
  • Each module is responsible for its own configuration key and route locator setup

Module Organization

  • Module.kt should own: Ktor endpoint/service assembly, wiring from ConfigurationProvider (config key + EndpointLocator.Rest)
  • Avoid implicit receiver complexity — moving wiring functions to module packages reduces receiver ambiguity
  • Adding new service dependencies requires updating all downstream tests that manually instantiate the service

Cross-Cutting: Auth, Logging, Serialization

Section titled “Cross-Cutting: Auth, Logging, Serialization”

Authentication Abstraction Unify bearer key and JWT auth under one Authentication interface (with composite auth for “try multiple schemes”).

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/runtime/auth/Authentication.kt, /common-module/lib/src/main/kotlin/cards/arda/common/lib/runtime/auth/BearerKeyAuthn.kt, /common-module/lib/src/main/kotlin/cards/arda/common/lib/runtime/auth/JwtAuthn.kt

Request → ApplicationContext Injection Build an ApplicationContext (call id + AuthPrincipal + ServiceScope) from Ktor call state and inject into the coroutine context for downstream code.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/api/rest/server/security/ServiceContextPlugin.kt

ApplicationContext fabrication with AuthPrincipal.Internal at the service layer is a security risk — it bypasses security and tenancy checks. Always rely on the ApplicationContext already present in the coroutine context.

Standard Error Responses Normalize thrown Throwable to structured HTTP errors via StatusPages and ErrorResponse. See Exception Handling and API Design.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/component/Component.kt

Structured Logging + Trace Correlation Use call IDs + MDC propagation to correlate logs across request handling.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/api/rest/server/monitoring/ServerMDCPropagationPlugin.kt, /common-module/lib/src/main/kotlin/cards/arda/common/lib/api/rest/types/monitoring/CallFieldNames.kt

Performance Logging Emit per-request timing as JSON CallPerfData for lightweight latency visibility.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/api/rest/server/monitoring/ServerPerfMonitoringPlugin.kt, /common-module/lib/src/main/kotlin/cards/arda/common/lib/util/log/PerformanceLogging.kt

Standard JSON Configuration Centralize JSON behaviors (ignore unknown keys, contextual serializers, no discriminator) for consistent wire/persistence encoding.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/lang/serialization/Json.kt

Manual Polymorphic JSON Discriminator When polymorphism is needed, implement explicit serializers that add/remove a discriminator field rather than relying on kotlinx built-in polymorphism. Use for stable wire formats with explicit type control.

  • Code: /operations/src/main/kotlin/cards/arda/operations/system/batch/business/Job.kt

Bitemporal Persistence Store effective + recorded time for each record and query “as-of” any point in either timeline.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/persistence/bitemporal-persistence.md

DBIO Effect Type Represent DB work as composable DBIO/TDBIO values (typealias DBIO<R> = suspend () -> Result<R>) to separate describing from executing and enable transactional composition.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/persistence/dbio-design.md
  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/persistence/types/DBIO.kt
  • See Functional Programming for the FP framing.

Universe Repository Pattern Implement persistence access as a Universe<Payload, Metadata> with persistence mapping + validator + universal condition(s).

  • Docs: /technical-documentation/contents/2_design/2_functional/general/persistence/universe-design.md

Validation Layering Keep invariants close to data (EntityPayload.validate) and collection/scoping rules in the universe validator/orchestrator.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/persistence/universe-design.md

Scoping (Tenant/Parent) as a Universal Condition Enforce scoping by metadata + universal filter + validator checks.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/persistence/universe-design.md

Parent–Child Persistence (Ordered vs Unordered) Implement child collections as universes scoped to their parent. Use ranked ordering only when users need explicit ordering semantics.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/persistence/parent-child-persistence.md

Exposed Mapping Patterns Define persistence with table configuration + record mapping + universe + migrations.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/persistence/creating-table-mappings.md

Draft Persistence (Out-of-Line Drafts Table) Store drafts in a dedicated table with (entity_id, tenant_id) uniqueness and JSON payload/metadata columns to support Edit–Draft–Publish without polluting bitemporal history.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/persistence/drafts/DraftStore.kt

Transaction Bubble Helpers Use inTransaction to create or reuse an Exposed transaction and propagate ApplicationContext across coroutine boundaries. inTransaction(db) is reentrant — when called within an existing transaction it joins rather than opening a new one.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/persistence/rdbms/TransactionExt.kt

DB Bootstrap + Migrations Standardize DB creation via Hikari/Exposed (DataSource.newDb) and migrations via Flyway (DbMigration). Centralize migration config, validation, and structured error logging.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/persistence/rdbms/DataSource.kt, /common-module/lib/src/main/kotlin/cards/arda/common/lib/persistence/rdbms/DbMigration.kt
  • Exposed index definitions require Flyway migrations: index() inside init {} only describes the schema for Exposed’s internal model — it does NOT automatically create the index at runtime.
  • Bitemporal cascade delete: Use recordCompanion.delete() within a parent delete operation rather than ChildUniverse.delete() to avoid parent lookup timing mismatches within the same transaction.
  • ChildUniverse.delete returns a Pair: Returns Pair<BitemporalEntity<PP, PM>, BitemporalEntity<EP, EM>> — parent and child, not just the child.
  • Column names must be lowercase snake_case: While Exposed allows uppercase, the project standard is lowercase to prevent surprises in SQL queries and external tools.
  • Universal conditions require correct compileFilter overrides: Failing to override compileFilter in a ScopedUniversalCondition with custom field mapping can cause NullPointerException in QueryCompiler.
  • universalCondition visibility: Must be protected. Overriding with private triggers Kotlin compilation errors.
  • QueryCompiler reuse: Never construct QueryCompiler(table) inline in service methods. Define a module-level lazy val with the translator in the persistence package, or expose an internal accessor on the universe. This ensures the translator is constructed once and shared.
  • as ChildTable cast requires a guard: When binding a translator to a child universe’s table, the cast from EntityTable to ChildTable is required due to invariant generics. Always guard with check(persistence.bt is ChildTable) before the cast.

Denormalization at Universe Level Denormalized fields (e.g., BusinessRole.name mirroring BusinessAffiliate.name) are best handled in metadataFor(), which has access to both payload and parent entity.

Keep Denormalized Fields Out of API DTOs Exclude denormalized fields from input DTOs to prevent API consumers from providing incorrect data. The source of truth remains the parent entity, managed internally.


Entity Local Behavior Implement validations and small invariants on the payload/metadata types themselves (no external calls).

  • Docs: /technical-documentation/contents/2_design/2_functional/general/persistence/index.md

Domain Lifecycle Modeling Model lifecycles explicitly (states, events, transitions) for entities that have more than CRUD behavior.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/information-model/reference-data.md

Lifecycle Mixins in Services Define lifecycle interfaces (e.g., KanbanCardLifecycle, KanbanCardPrintLifecycle) and provide shared implementations that translate events into bitemporal updates + derived “details” payloads.

  • Code: /operations/src/main/kotlin/cards/arda/operations/resources/kanban/service/KanbanCardService.kt, /operations/src/main/kotlin/cards/arda/operations/resources/kanban/service/LifecycleImpl.kt

Proto-to-Domain Mapping Layer Convert validated protobuf messages into domain objects via explicit mapper functions before invoking business services. Keeps wire/schema evolution decoupled from domain model evolution.

  • Code: /operations/src/main/kotlin/cards/arda/operations/reference/item/service/ItemCsvUploadService.kt

Service Layer Cascade for Transactional Atomicity Implement cascade logic by overriding update() in the service implementation and wrapping in inTransaction, ensuring parent update and child cascades succeed or fail together.

Temporal Consistency in Cascades When cascading on bitemporal entities, use TimeCoordinates.now(effectiveAt) to ensure the cascade occurs at the same logical time as the parent update.

Cycle Prevention in Propagation Propagation paths must deliberately bypass resolution methods to prevent infinite loops. Any modification must maintain this invariant.

Bulk Operations Pattern addBunch and updateBunch follow a consistent pattern:

  1. Wrap in a database transaction via inTransaction(db)
  2. Use collectAll() to gather results and short-circuit on first failure
  3. Send notifications only after successful commit

Layered Module Design (Protocol Adaptor/Service/Persistence/Proxy) Use services as transaction boundaries and orchestration points over one or more universes.

  • Docs: /technical-documentation/contents/2_design/2_functional/general/module-design/data-authority/data-authority-module.md, /technical-documentation/contents/2_design/2_functional/general/module-design/data-authority/layer-responsibilities.md

Data Authority Service Pattern (CRUDQ over a Universe) Provide a consistent service façade that wraps universe operations in transactions and returns stable API DTOs.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/service/DataAuthorityService.kt

Editable Data Authority Service (Draft + Publish Semantics) Require drafts for updates; prevent delete with active draft; close draft on publish.

  • Docs: /technical-documentation/contents/1_specifications/patterns/edit-draft-publish.md
  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/service/EditableDataAuthorityService.kt

Service Notifications and Observers Emit DataAuthorityNotification events on successful mutations and allow observer registration. Use as a lightweight pub/sub for change propagation. See Observer Patterns.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/service/Listener.kt, /common-module/lib/src/main/kotlin/cards/arda/common/lib/service/Observable.kt

Action Abstraction for Workflows Model non-trivial operations as Action/TargetedAction/EntityAction with explicit validation, author/effective time, and optional context wrappers.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/service/actions/Action.kt, /common-module/lib/src/main/kotlin/cards/arda/common/lib/service/actions/EntityAction.kt

Guarded Actions Wrap actions with an explicit guard (precondition) that can short-circuit execution with a controlled failure.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/service/actions/GuardedAction.kt

Reusable State Engine Implement state machines declaratively (states, transitions with guard predicates, entry/exit actions) and generate executors.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/service/stateengine/StateEngine.kt
  • See Functional Programming for the FP framing.

PersistencePreparationResult Pattern The forCreate and forUpdate functions in ItemServiceHelpers.kt return a PersistencePreparationResult containing processed payload and lists of supplies to create/update. This pattern enables transactional creation of parent + children.


Standard REST Endpoint Routing + Docs Mount endpoints under a module root path and expose OpenAPI JSON + Swagger UI + Redoc routes consistently.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/module/KtorModule.kt

Endpoint Configurator Interface Express endpoints as “configure secure/unsecure routes” units for composition and testability.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/api/rest/server/Endpoint.kt

Data Authority REST Endpoint Template Use reify(...) factories to capture serializers at the call site and build consistent CRUDQ routes + OpenAPI specs with minimal boilerplate.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/api/rest/server/dataauthority/DataAuthorityEndpoint.kt, /common-module/lib/src/main/kotlin/cards/arda/common/lib/api/rest/server/dataauthority/EditableDataAuthorityEndpoint.kt

Wire DTOs for Records/Pages/Updates Provide EntityRecord, Page, PageResult, Update as serializable, API-facing shapes distinct from persistence internals.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/module/dataauthority/Record.kt, /common-module/lib/src/main/kotlin/cards/arda/common/lib/module/dataauthority/PageResult.kt

Filtering/Query Endpoint Pattern Provide /query routes using Query DSL with page cursors for stable pagination. See Query DSL.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/lang/query/Query.kt

EntityServiceConfiguration (Structured Locator Translators) Define per-entity EntityServiceConfiguration to introspect @Serializable payload classes and build ExposedLocatorTranslator instances. This enables API clients to use JSON field names (camelCase) as query locators instead of raw database column names. The translator is backward-compatible — it resolves structured paths first, then falls back to exact column name matching. Set the universe’s translator property via lazy { queryConfig.bindToTable(persistence.bt) }. For child universes, guard the as ChildTable cast with check(). Define QueryCompiler instances as module-level lazy vals rather than constructing them inline in services. See Query DSL: EntityServiceConfiguration.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/service/configuration/EntityServiceConfiguration.kt

Declarative Route/Spec Co-Definition (ServiceEndpointDsl) Declare routes as a tree of Group/Node/Leaf with typed request/response shapes and auto-derived OpenAPI operation IDs. Use for larger endpoints.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/api/rest/server/service/ServiceEndpointDsl.kt

Standard Request Parsing (RequestUtils) Use requireHeader/requireBody/requirePathParam etc. returning Result to keep route handlers small and error handling consistent.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/api/rest/server/RequestUtils.kt

OpenAPI Spec Helpers + Standard Headers Use EndpointSpec.withErrorResponsesAnd and StandardHeaders to keep OpenAPI specs consistent across endpoints.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/api/rest/server/openapi/EndpointSpec.kt, /common-module/lib/src/main/kotlin/cards/arda/common/lib/api/rest/types/openapi/StandardHeaders.kt

CSV Upload APIs (Direct-to-S3 + Ingestion) [BE]

Section titled “CSV Upload APIs (Direct-to-S3 + Ingestion) [BE]”

Direct-to-S3 Upload with Presigned PUT URLs Return a presigned PUT URL and require S3 object metadata headers (tenant/author) so uploads do not transit the API server.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/infra/storage/CsvS3ObjectDirectService.kt

Streaming Ingestion from S3 Read CSV rows from S3 as a Flow, chunk into batches, and process/validate each row while accumulating structured row-level errors.

Protobuf CSV Contract + Validation Define CSV row structures as protobuf messages, attach validation rules with buf.validate (field constraints + CEL), validate with protovalidate, then map to domain objects.

  • Code: /operations/src/main/kotlin/cards/arda/operations/reference/item/service/ItemCsvUploadService.kt

Ingestion Job Tracking Model long-running ingestion as a bitemporal Job entity with JobEvent updates and a JobTracker façade. Expose endpoints to get upload URL, trigger ingestion, and poll job status.

  • Code: /operations/src/main/kotlin/cards/arda/operations/system/batch/business/Job.kt, /operations/src/main/kotlin/cards/arda/operations/system/batch/service/JobService.kt

Containerized Postgres for Tests Wrap Testcontainers Postgres and expose Exposed Database and JDBC URI helpers.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/testing/persistence/ContainerizedPostgres.kt

LocalStack Wrapper (MockAWS) Provide a façade for starting LocalStack and creating AWS SDK clients (S3/SQS) with local endpoints.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/testing/localstack/MockAWS.kt

Universe Test Template A reusable Kotest StringSpec template that runs migrations and verifies baseline CRUD/query/history behavior for any AbstractUniverse.

  • Code: /common-module/lib/src/main/kotlin/cards/arda/common/lib/testing/persistence/AbstractUniverseTestTemplate.kt