Skip to main content

Shared Go Platform Architecture

dx-common-go/platform is the internal platform SDK for the service fleet. It owns cross-cutting mechanics so each application repository contains domain rules, use cases, and adapters rather than its own interpretation of authentication, HTTP envelopes, transactions, events, health, or shutdown.

Status: Partially implemented. Bootstrap, configuration, HTTP, errors, paging, PostgreSQL, caching, events, health, workload identity, and observability foundations are adopted across the core fleet. SDK versioning, complete gRPC migration, conformance automation, and several operational hardening items remain open.

Capability map

CapabilityPlatform responsibilityService responsibilityStatus
HTTP and APIRouter, middleware order, request binding, typed handlers, standard envelope, paging, OpenAPI servingOperation contract, validation rules, business responseImplemented
ConfigurationTyped load, defaults, environment overrides, validation, explicit modesService-specific schema and safe defaultsImplemented
PersistencePool lifecycle, context transaction, query helpers, error mapping, migration runnerData model, versioned migrations, repository queriesImplemented
CachingCache interface, Redis adapter, cache-aside and rate-limit primitivesKey design, TTL bound, invalidation trigger, failure modePartially implemented
MessagingTyped envelope, AMQP adapter, durable topology, outbox, reconnect supervision, healthTopics, event schemas, idempotent handlers, reconciliationPartially implemented
Workers and jobsLifecycle supervision, cancellation, bounded shutdownIdempotent unit of work, durable leases, recovery rulesPartially implemented
SearchElasticsearch client and focused helpersIndex lifecycle, aliases, mappings, query semanticsImplemented without a backend-neutral search abstraction
ObservabilityStructured logs, request IDs, metrics, traces, liveness/readiness contractsDomain metrics, dependency checks, SLOs, actionable alertsPartially implemented
Error handlingStable error taxonomy, HTTP/gRPC mapping, response writerDomain error meaning and safe detailsImplemented
SecurityJWT validation, workload identity, subject context, shared PEP seamsOperation policy, resource extraction, business enforcementPartially implemented
ResilienceRetry and circuit-breaker primitives, timeouts, graceful shutdownIdempotency, retry classification, dependency-specific policyPartially implemented
TestingTest helpers, fake identity provider, real-router patternsDomain, negative tenancy, crash, replica, and contract testsPartially implemented
DeploymentOne container contract, health endpoints, config-check boot modeService image, migrations, resource requirementsPartially implemented

Service shape

cmd/server/main.go composition through platform/bootstrap
internal/domain/ entities and invariants; no infrastructure imports
internal/service/ use cases and consumer-owned interfaces
internal/repository/ adapters for service-owned persistence
internal/api/ route set and typed HTTP handlers
internal/events/ event definitions, publishers, consumers
internal/workflows/ only for durable multi-step processes
db/migrations/ versioned schema changes
openapi/ the public contract and operation metadata
configs/config.yaml development defaults; never production secrets

Dependencies point inward: API and event adapters call use cases; use cases depend on narrow interfaces; infrastructure implements those interfaces. The domain package imports none of them.

HTTP and API conventions

  • Public APIs are HTTP/REST behind dx-gateway-go; internal service calls converge on gRPC.
  • The OpenAPI document owns paths, schemas, operation identifiers, and—at target state—authentication and authorization metadata.
  • The normal response envelope is {type, title, detail, result, context} with pagination metadata when relevant.
  • Standards-native responses remain native where wrapping would break the standard, notably GeoJSON and other OGC documents.
  • Every service exposes /healthz/live, /healthz/ready, /metrics, and a configurable OpenAPI/Swagger surface.

Persistence and migrations

Each service owns its logical data and schema. New deployments use a database or schema per service and apply versioned migrations end to end. One explicitly configured actor applies migrations; ordinary replicas start with schema changes disabled.

Repositories use parameterized queries and allowlisted identifiers. Transactions propagate through context so all repositories in one use case join the same transaction. Network calls, notifications, and goroutines are forbidden inside retryable transaction callbacks; external effects follow commit through an outbox or a subsequent step.

Caching

Caches accelerate reads but never become the source of truth for identity, grants, ownership, or payments. Authorization cache keys include subject, actor, operation, resource, tenant/organization context, relevant attributes, relationship revision, and policy revision. A cached allow cannot outlive the earliest token, grant, policy, or attestation expiry. Revocation invalidates or outranks cached allows.

Redis is also used for rate counters and Agentic Plane hot state. Those uses declare whether Redis is a cache or load-bearing state; the failure mode must be explicit.

Messaging and event delivery

State-changing facts use a transactional outbox: the domain write and outbox row commit together, a dispatcher claims rows exclusively, and RabbitMQ delivers a versioned envelope to idempotent consumers. Durable queues have dead-letter handling; incompatible versions and malformed payloads are quarantined rather than discarded.

Events carry an ID, type, version, occurrence time, correlation/causation context, actor, and organization where applicable. Consumer-first rollout supports the current and immediately previous event schema across rolling updates.

Workers and scheduled jobs

Continuous workers run under the application lifecycle, expose health, reconnect with bounded backoff, and stop within the shutdown budget. Work that must have a single owner uses a durable lease, not a process-local lock. Scheduled work is preferably a one-shot invocation from the scheduler. Every job is idempotent and records enough state to reconcile after a crash.

Search and object storage

Search remains technology-specific because Elasticsearch and PostGIS expose materially different query models. The platform supplies focused clients and instrumentation; service repositories own mappings, index aliases, spatial operators, and query contracts.

Object storage uses an S3-compatible interface. Request paths stream bodies, bound item counts and aggregate bytes, use short-lived presigned access where appropriate, and clean multipart or temporary artifacts on cancellation and failure.

Observability and operations

  • A request/correlation ID propagates across HTTP, gRPC, database, and broker boundaries.
  • Logs are structured and exclude credentials, personal data, full object keys, and raw policy documents.
  • Authorization emits stable reason codes, decision IDs, engine revisions, cache outcomes, and enforcement results.
  • Liveness is process-only. Readiness checks every dependency required by enabled routes and reports bounded, non-sensitive detail.
  • Graceful shutdown stops ingress, drains workers, closes broker/database clients, and force-terminates at a configured deadline.

Testing and release conventions

Unit tests cover domain rules; route tests exercise the real router and response mapper; integration tests use real PostgreSQL/Redis/RabbitMQ where semantics matter; end-to-end smoke tests traverse the gateway. Security-sensitive services add negative ownership and organization-isolation cases, malformed-event tests, replica races, crash recovery, and failure-injection tests.

The target release model uses tagged SDK versions and blocks incompatible changes through fleet compilation, contract checks, generated conformance checks, and rendered-environment boot tests.

For Developers

Low-level package APIs, dependency rules, and adoption checklists live in the source set: