/

CrowdVision · source-available, not open source · © 2026 Nicolò Ghignatti

Naming Conventions

Consistent naming lets a reader infer an artefact’s role from its name alone. CrowdVision spans several languages and layers, so the conventions are defined per layer below. They are enforced partly by ESLint and partly by review; treat them as mandatory.


Services and repository layout

A service carries one short name across every surface it appears in — no -service suffix anywhere. The suffix is redundant inside backend/, and having it on some surfaces but not others is how a service_key ends up disagreeing with a directory.

SurfaceExample
Directorybackend/telemetry, backend/digital-twin
Cargo crate and compiled binarytelemetry, digital-twin
Docker imagecrowdvision-telemetry
Container name / hostname other services resolvetelemetry:3000
Environment variableTELEMETRY_URL, DIGITAL_TWIN_URL
.github/services.json key, moon project, CI legtelemetry
Kubernetes manifest, documentation pagek8s/deployments/telemetry.yml, services/telemetry.qd
Diagram assetarchitecture/images/cc-telemetry.svg, architecture/images/cc-digital-twin.svg

claims-gateway and provisioner are named for what they are — a gateway, a reconciler — rather than <noun>-service. That is deliberate, not an oversight.

Top-level placement follows the same principle: the directory says what kind of thing it holds.

PathHolds
backend/<name>/A deployable service.
backend/libs/Shared Go modules embedded into services, never deployed.
backend/acceptance/The cross-service acceptance suite. Each service keeps its own tests/ for its own suite.
schemas/Every shape that crosses a service boundary: json/ specs, fixtures/, and the Rust binding crates.

Files and directories

Every Node service follows the same internal layout, so the file you want is always in the same place.

PathHolds
src/router.tsRoute declarations.
src/controller/HTTP adapters (one file per concern).
src/services/Business logic.
src/models/Schemas and shared types.
src/middlewares/Cross-cutting middleware.
src/config/Environment accessors and client setup.
__tests__/The service’s test suite.

Backend — Node.js / TypeScript

ArtefactConventionRecommendedAvoid
Controller / service filescamelCase, layer as suffix where it adds claritybuildings.ts, notificationService.tstwin_ctrl.ts, Notification.ts
Model filescamelCase file nameaccount.ts, buildingThreshold.tsAccount.ts, building_threshold.ts
Exported model and interfacePascalCase; interfaces prefixed Iexport interface IAccount, export const Account = model(...)export const account, export interface Account
Functions and variablescamelCasegenerateStandardToken, allowedColumnsGenerateToken, allowed_columns
ConstantsUPPER_SNAKE_CASEROLE_WEIGHTS, COOKIE_NAMEroleWeights
Database collectionsplural, lowercaseaccounts, buildingsAccount, building

Frontend — Vue 3 / TresJS

ArtefactConventionRecommendedAvoid
ComponentsPascalCase, multi-word (to avoid clashing with HTML elements), file and tagBuildingCard.vue, NotificationDropdown.vue, LeftMenu.vuecard.vue, header.vue
Views / pages (top-level routes)PascalCase with a View suffixDashboardView.vue, AdministrationView.vueDashboard.vue
ComposablescamelCase, prefixed useuseBuildingModel.ts, useAuth.ts, useSceneControls.tsbuildingModel.ts, getAuth.ts
Utilities / helperscamelCasemetrics.ts, formatTimestamp.tsMetrics.ts

Rust — dashboard & digital-twin

Both Rust services follow idiomatic Rust style.

ArtefactConventionExample
Modules and filessnake_casetunnel.rs, discovery.rs
Functionssnake_casestart_telemetry_tunnel, metric_is_allowed
Types (structs, enums)PascalCaseAppState, PreferenceDocument
ConstantsSCREAMING_SNAKE_CASERAW_CHANNEL, DEFAULT_COLUMNS

Go — Identity & Tenancy Control Plane

claims-gateway, tenancy, registry, and provisioner follow idiomatic Go style (gofmt, checked by go vet in just lint).

ArtefactConventionExample
Packages and filessnake_case or single lowercase word, matching the package nameinternal/api/handler.go, internal/store/
Exported functions and typesPascalCasefunc Mount(...), type Config struct
Unexported functions, types, and variablescamelCase (Go’s own export rule: capitalisation is visibility, not a separate style choice)func writeDomain(...), type handler struct, func isValidAccountID(...)
ConstantsPascalCase or camelCase per Go convention (no SCREAMING_SNAKE_CASE — that’s not idiomatic Go)as appropriate to exported/unexported

Package layout follows the standard Go project layout: internal/api (HTTP handlers), internal/service (business logic), internal/store (persistence, often with a storefake sibling for tests), internal/events (Redis Stream consumers/producers), internal/config.


Python — agent

The agent follows PEP 8: snake_case for modules, functions, and variables; PascalCase for classes; SCREAMING_SNAKE_CASE for constants. (The agent is maintained separately; these conventions are listed for completeness.)


Branches

Branch names mirror the commit types, so the branch announces its intent.

<type>/<short-kebab-description>
TypeExample
featfeat/private-domains
fixfix/jwt-expiry-off-by-one
chorechore/bump-dependencies
docsdocs/telemetry-api-reference
refactorrefactor/extract-token-service

Commit scopes

Conventional-commit scopes use the service or area name, so history can be filtered by component.

feat(twin): add per-room colour overrides
fix(gateway): reject expired refresh tokens
docs(telemetry): document the ingestion fast path
chore(ci): pin the kubeconform version

Common scopes: gateway (claims-gateway), tenancy, registry, provisioner, twin, telemetry, notification, socket, contracts, client, agent, ci, docs, k8s. The full commit format is covered in Commits & Release Please.


Other identifiers

ArtefactConventionExample
Environment variablesUPPER_SNAKE_CASEGATEWAY_ISSUER, TELEMETRY_URL
Docker imagescrowdvision-<service> under the org namespaceghcr.io/nickghignatti/crowdvision-twin
Workflow files<category>-<name> with a yml extension (ci-, cd-, maint-)ci-twin.yml, cd-release.yml, maint-cleanup.yml
Documentation pageskebab-case with a qd extensionbuilding-data.qd, naming-conventions.qd

Renaming carries data with it

A rename is never only a rename: several identifiers derived from a service’s name own state, and changing one silently starts from empty rather than failing.

IdentifierWhat a rename actually does
Compose volumeCompose derives the real volume name from the project plus the declared name, so renaming contracts-data to dashboard-data mounts a new, empty volume and leaves the old one orphaned with all the data still in it. Migrate the contents in the same change, or the service comes up looking healthy and blank. This is what happened to the dashboard’s saved column preferences when contracts became dashboard.
Kafka consumer groupA group with no committed offsets plus auto.offset.reset=earliest re-reads the whole topic — a replay, not a reset. Idempotent for the registration groups; for alerts it re-delivers historical breaches, collapsed only by the Redis cooldown.
Database nameRenaming a database is a migration. chatdb, twindb and contractsdb keep their names for that reason, and none carried a -service suffix to begin with.
System identity at a trust boundarysystem:notification-service keeps the old name on purpose: it is pinned byte-for-byte by a test, and it reads as a stable source in a central log regardless of the directory name.

Check the orphans after a rename

docker volume ls lists every volume for the project. Anything whose name no longer appears in a docker-compose YAML file is orphaned — either migrate it or delete it deliberately, but do not leave it to be rediscovered as “the data disappeared”.