Module map
This map assigns current backend capabilities to their intended module owners. It guides incremental migration; it does not imply that the packages have already moved.
Proposed modules
| Module | Owns | Notes |
|---|---|---|
identity | Authentication, users, invitations, global roles, access tokens, refresh sessions, token rotation and revocation behaviour | Migrated. Uses Valkey-backed session, rotation, replay-detection, revocation, and security-counter ports. |
organizations | Organizations, organization membership, organization-level imports and organization context | Migrated. |
facilities | Facilities, facility membership, departments, locations and facility context | Migrated. |
governance | Governance profiles, capabilities, approval policies, policy documents and governance authorization | Migrated. Exposes GovernanceCapabilityAuthorizer from its root package; re-exported capability constants live in shared/governance to break import cycles. |
devices | Device catalog, manufacturers, brands, models, device records, images, imports, utilization and device lifecycle | Migrated. |
lifecycle | Asset lifecycle events: recalls, incidents, transfers, refurbishments, retirement reviews and disposals | Migrated. |
maintenance | Fault reports, work orders, preventive-maintenance schedules and maintenance operations | Migrated. |
procurement | Need requests, option appraisals, acquisition records, commercial records and donations | Migrated. |
counterparties | Counterparty records and vendor contracts | Migrated. |
billing | Plans, subscriptions, billing transactions, payment provider integration and payment webhook retention | Migrated. Payment provider adapter lives in adapters/payment. |
notifications | Notification records, notification preferences or delivery requests, and coordination of email delivery | Migrated. |
audit | Audit event recording, audit queries and retention policy | Migrated. |
reference | Stable reference and lookup data used across modules | Migrated from legacy lookup domain. |
Devices terminology and scope
devices is the canonical module name. It owns two related but distinct concepts:
Device: a catalog definition, such as a manufacturer/model/type of medical equipment.DeviceRecord: a physical device managed by an organization or facility.
The module also owns device lifecycle operations such as utilization, retirement, transfer, recall, and refurbishment.
Acquisition is a workflow owned by procurement. When acquisition results in a physical device being registered, Procurement calls the public Devices API to create a DeviceRecord. Procurement must not insert directly into Devices-owned tables.
Reference-data ownership
The reference module should only own genuinely shared, stable vocabularies. Lookup data that represents a specific module's business rules belongs to that module even if it is exposed through a lookup-style endpoint.
Assigned module-owned reference data includes:
- work-order statuses, priorities, and types:
maintenance; - failure codes:
maintenance; - device types, categories, functions, and application risks:
devices; - electrical safety classes, applied-part types, power sources, backup-power sources, and regulatory classes:
devices; - location categories:
facilities; - organization types:
organizations; - subscription plans:
billing; - user roles:
identity; - generic statuses: temporary
referenceownership until status families are separated by business meaning.
Existing /api/v1/lookup routes may remain stable while ownership moves behind them.
Infrastructure layers
The following are not business modules. They form four distinct layers:
platform/ — infrastructure ports and generic utilities
Provider-neutral interfaces and cross-cutting utilities. Modules depend on these interfaces, never on concrete implementations.
| Package | Responsibility |
|---|---|
platform/config | Typed process configuration (API, worker, scheduler) |
platform/database | Pool lifecycle, migrations and tenant-schema mechanics |
platform/cache | Provider-neutral cache interface (Get/Set/Delete) |
platform/session | Refresh-session store: activate, rotate, revoke |
platform/idempotency | Distributed in-flight reservation (Acquired/InProgress/Conflict) |
platform/ratelimit | Distributed rate-limiter port (Allow → Decision) |
platform/lease | Distributed lease coordination for background workers |
platform/securitycounter | Distributed abuse counters (failed logins, lockout state) |
platform/distributedstate | Facade aggregating all Valkey-backed capabilities |
platform/messaging | Durable job queue (transactional outbox + Valkey signal) |
platform/logger | Context-aware slog wrapper with request/user/facility correlation |
platform/metrics | Prometheus HTTP, DB pool, and auth instrumentation |
platform/retry | Exponential backoff retry with context support |
platform/circuitbreaker | Circuit-breaker pattern for external service calls |
platform/observability | Logging, metrics and tracing integration points |
adapters/ — concrete provider implementations
Implement platform interfaces using specific vendors. Constructed in app and injected into modules as narrow interface types.
| Package | Responsibility |
|---|---|
adapters/mail | Resend (production) and NoOp email implementations |
adapters/storage | Cloudflare R2 and NoOp object-storage implementations |
adapters/payment | Paystack (production) and NoOp payment-provider implementations |
adapters/fx | Exchange-rate fetcher with circuit breaker, retry, and cache |
common/ — Ovahol-specific shared application code
Packages that encode Ovahol business policy or application conventions. Not generic infrastructure; not a dumping ground.
| Package | Responsibility |
|---|---|
common/errors | AppError type and predefined HTTP-mapped error taxonomy |
common/roles | RBAC role constants and ValidRoles lookup map |
common/plans | Subscription plan code constants and family normalization |
common/features | Feature-flag resolver tied to plan entitlements and roles |
common/config | Auth config loader (JWT secrets, lockout thresholds, seed admin) |
common/response | APIResponse JSON envelope for all HTTP responses |
common/utils | General helpers: fingerprinting, password validation, slice ops |
common/pagination | Limit/Offset DTO with normalization defaults |
shared/ — cycle-breaking value types
A type belongs here only when it is stable, has no clear module owner, and would otherwise create an import cycle. Keep this package intentionally small.
| Package | Responsibility |
|---|---|
shared/governance | GovernanceActorScope and capability constants used across lifecycle, procurement, and governance without creating cycles |
Boundary questions to resolve during extraction
The following decisions should be made from actual transactional and change patterns while modules are extracted:
- whether governance later needs policy roles distinct from Identity-owned authentication and membership roles;
- whether device dictionary recommendations remain within
devicesor become a separately owned integration capability; - whether notification persistence and delivery remain one module or delivery becomes a worker adapter;
- whether organization imports belong to
organizationswhile device imports remain indevices; - whether audit retention is owned by
auditor implemented as an application-level operational job.
These open questions do not block the initial module structure. Until decided, new code should follow the owner named in the table above.