Skip to content

Naming Conventions

This document defines the formal naming conventions for all system artifacts in Arda’s platform.

Three standard identifier forms are used throughout the system:

  • lower-kebab-case: a sequence of words in lower case joined with a dash - (e.g., item-data-authority)
  • lowerCamelCase: a sequence of capitalized words joined without a separator, with the first word’s first letter in lower case (e.g., itemDataAuthority)
  • lower_snake_case: a sequence of names in lower case joined with an underscore _ (e.g., item_data_authority)

These conventions are actively created by engineers during design and implementation.

Given a Component C and its constituting Modules M1, M2, M3, …:

  1. The canonical form is lower-kebab-case
  2. The canonical name does not have generic trailing suffixes like -component, -module, or -data-authority
  3. The name aims to be short — one or two nouns

Examples:

  • qr-lookup — correct
  • qrLookup — incorrect (not lower-kebab-case)
  • item-data-authority — incorrect (has generic trailing suffix)

Each Component has exactly one repository with the canonical component name:

  1. The repository contains all source code of the Component (documentation may be separate)
  2. The repository does not contain other Components
  3. In a Kotlin Component, the repository contains one Gradle project with the canonical component name

All Kotlin classes are grouped into packages by the Module they belong to. All Module packages are “under” a package for the Component.

Package naming rules:

  1. Starts with cards.arda
  2. Immediately followed by the lowerCamelCase Component name
  3. Optionally followed by the lowerCamelCase Module name

Examples:

  • cards.arda.pdfRender.shopAccess.PdfRenderModule — correct (class in Module shopAccess within Component pdfRender)
  • cards.arda.accounts.system.tenant.TenantModule — correct (Module tenant under Component accounts)
  • cards.arda.pdfRender.PdfRenderModule — incorrect (class is in Component, not in a Module)
  • cards.arda.pdfRender.shopaccess.PdfRenderModule — incorrect (Module name not lowerCamelCase)

Package naming above says where a type lives. These rules say what it is called.

An interface is named for the concept, never the mechanism. SettingsService, not JdbcSettingsRepository; EmailSender, not PostmarkClient. The interface is the contract its callers reason about, and a caller does not care that today’s implementation speaks JDBC. Implementation classes under lib/infra/ may carry mechanism names — that is where the mechanism is the point.

The interface takes the concept name; the default implementation takes Impl.

  • DemandService / DemandServiceImpl, EmailConfigurationService / EmailConfigurationServiceImpl in operations; DataAuthorityEndpoint / DataAuthorityEndpointImpl, IdempotencyStore / IdempotencyStoreImpl in common-module.
  • operations/.editorconfig encodes it: ij_java_subclass_name_suffix = Impl.
  • Do not invert the pair by naming the interface for its role and the implementation for its concept — Settings (interface) with SettingsService (implementation) reads backwards at every call site, and settings: Settings tells a reader nothing.

*Endpoint is singular; *Routes is plural. The two suffixes name two different things, and knowing which you are building answers the naming question by itself:

SuffixNamesImplementsExample
*Endpoint (singular)A mountable endpoint object, registered with its moduleEndpointConfigurator or DataAuthorityEndpointItemEndpoint, EmailConfigurationEndpoint, KanbanCardEndpoint
*Routes (plural)A route-fragment class contributing nodes to a parent treeexposes fun …Node() = floatingNode(…) { … } factoriesItemPrintRoutes, ItemSearchRoutes, CsvUploadRoutes

There is no plural *Endpoints file and no singular *Route file anywhere in operations. If you are about to write one, you have probably picked the wrong shape rather than the wrong name — see Endpoint Definition DSL.

Persistence types take the role suffix of the bitemporal quartet<Concept>Universe, <Concept>Table, <Concept>Record, <Concept>Payload — as laid out in Universe Design. A type that is none of those four is probably not part of the persistence layer.

File names follow the primary type. One public type per file where practical, and the file takes its name. A file holding a service contract plus two value objects is a file that wants splitting — the contract to service/, the value objects to domain/.

The Ingress is exposed by the Service only to other Services in the Kubernetes cluster.

The ingress path is the combination of:

  • The Module API version
  • A /
  • The lower-kebab-case form of the Module name

The Route is exposed by the API Gateway to other systems. It is the published API of the Component and must only evolve intentionally.

It is recommended, but not mandatory, for the Route to equal the Ingress path. During transitions, multiple Routes may map to the same Ingress.

See also API Design: URL Naming for the full hostname and path convention.

The database name is the lower_snake_case form of the Module name.

These names are derived from primary names by combining Component and Module names according to ecosystem conventions.

The Docker image name is: <component-name>/<module-name>

  • The main Chart name is the Component name
  • The release name is the Component name

Every object has .metadata.namespace set to: <purpose>-<component-name>

As a default, .metadata.name is set to the Component name. Secondary objects of the same kind have appropriate names (conventions to be established).

Example: A namespace has at least two Secrets — one named after the Component (deployment configuration), and one named ghcr-secret (Docker image pull credentials). If a Component deploys multiple Modules requiring different secrets, they are named after their Module.