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.
General Patterns [GEN]
Section titled “General Patterns [GEN]”Domain Modeling
Section titled “Domain Modeling”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
Ktor Module Wiring
Section titled “Ktor Module Wiring”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, createAuthenticationonce fromcfgProvider.globalAuthConfiguration - Each module is responsible for its own configuration key and route locator setup
Module Organization
Module.ktshould own: Ktor endpoint/service assembly, wiring fromConfigurationProvider(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
ApplicationContextfabrication withAuthPrincipal.Internalat the service layer is a security risk — it bypasses security and tenancy checks. Always rely on theApplicationContextalready 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
Persistence [BE]
Section titled “Persistence [BE]”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
Persistence Gotchas
Section titled “Persistence Gotchas”- Exposed index definitions require Flyway migrations:
index()insideinit {}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 thanChildUniverse.delete()to avoid parent lookup timing mismatches within the same transaction. ChildUniverse.deletereturns a Pair: ReturnsPair<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
compileFilteroverrides: Failing to overridecompileFilterin aScopedUniversalConditionwith custom field mapping can causeNullPointerExceptioninQueryCompiler. universalConditionvisibility: Must beprotected. Overriding withprivatetriggers Kotlin compilation errors.QueryCompilerreuse: Never constructQueryCompiler(table)inline in service methods. Define a module-levellazy valwith the translator in the persistence package, or expose aninternalaccessor on the universe. This ensures the translator is constructed once and shared.as ChildTablecast requires a guard: When binding a translator to a child universe’s table, the cast fromEntityTabletoChildTableis required due to invariant generics. Always guard withcheck(persistence.bt is ChildTable)before the cast.
Denormalization Patterns
Section titled “Denormalization Patterns”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.
Business Logic [BE]
Section titled “Business Logic [BE]”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:
- Wrap in a database transaction via
inTransaction(db) - Use
collectAll()to gather results and short-circuit on first failure - Send notifications only after successful commit
Service Layer [BE]
Section titled “Service Layer [BE]”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.
APIs [GEN]
Section titled “APIs [GEN]”REST APIs [BE]
Section titled “REST APIs [BE]”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
Testing Utilities [GEN]
Section titled “Testing Utilities [GEN]”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
Copyright: © Arda Systems 2025-2026, All rights reserved