Technology Stack

This page records the implementation technology decided in the architecture workshop (2026-06-11). The structural choices are justified in ADR-007 through ADR-011; this page is the practical reference for building the services. Each individual technology is explained in depth in the Technologies section.

1. Topology

All seven services from the decomposition deploy independently from the start (ADR-007). The fleet is polyglot by fit (ADR-008): Go where proxying, resilience and event throughput dominate; TypeScript where auth tooling, fast-iterating rules and query-shaped work dominate.

flowchart TB SPA["SvelteKit static SPA<br/>(consumer + /brand routes)"] subgraph EDGE["Edge"] GW["api-gateway · Go<br/>routing, OIDC validation, rate limit,<br/>privacy-wall enforcement, SLO histogram"] end KC["Keycloak<br/>(OIDC)"] subgraph GO["Go services"] PPS["passport-service"] FRS["fridge-service"] MP["measurement-pipeline"] end subgraph TS["TypeScript services"] IDS["identity-service"] PVS["★ personalization-service"] BAS["brand-analytics-service"] end KAFKA[("Apache Kafka<br/>(KRaft)")] PG[("PostgreSQL<br/>6 schemas · 6 roles")] SPA --> GW GW --> PPS & FRS & IDS & PVS & BAS GW -.->|"token validation"| KC PPS & FRS & MP & IDS & PVS --> KAFKA KAFKA --> MP MP -->|"k-anonymous batches<br/>across the privacy wall"| KAFKA PPS & FRS & MP & IDS & PVS & BAS --> PG classDef core fill:#fff3bf,stroke:#e8a500,stroke-width:3px,color:#000 class PVS core

2. Stack by service

Service Language Why this language
api-gateway Go Reverse proxy performance, tiny static image, hand-built per Part II §6 — routing, Keycloak token validation, Visitor-ID handling, rate limiting, brand-scope enforcement and the SLO-1 latency histogram are the deliverable.
passport-service Go I/O-heavy ACL toward Open Food Facts/Agribalyse: circuit breakers, caching and progressive assembly (QAS-1/QAS-2).
fridge-service Go Event-sourcing fold over the append-only stream; explicit state machines read well in Go.
measurement-pipeline Go Kafka consumer throughput and windowed aggregation before the privacy wall.
identity-service TypeScript Visitor identities, linking, consent ledger, Brand — sits next to Keycloak; Node's session/web tooling fits.
personalization-service TypeScript Versioned rule policies iterate fast; verdict types stay close to the SPA's language.
brand-analytics-service TypeScript CQRS read models and dashboard-shaped queries.

Go services

Concern Choice
HTTP routing chi — stdlib-compatible router, middleware for auth/logging/metrics
Postgres access pgx + sqlc — hand-written SQL compiled to type-safe Go
Migrations golang-migrate, per service schema
Kafka client franz-go
Testing stdlib testing + testify

TypeScript services

Concern Choice
HTTP framework Fastify — schema-validated routes (JSON Schema, same tech as the event contracts)
Postgres access Kysely over pg — type-safe SQL, the TS analogue of sqlc
Kafka client @confluentinc/kafka-javascript — maintained librdkafka client
Testing Vitest

Shared infrastructure

Concern Choice
Database One PostgreSQL instance, six schemas (passport, personalization, fridge, identity, measurement, brand_analytics), six GRANT-restricted roles — isolation enforced by the database (ADR-009)
Messaging Apache Kafka, KRaft mode, single node — asynchronous domain facts only; synchronous calls stay internal REST (ADR-010)
Auth Keycloak (OIDC) — gateway validates tokens (ADR-011)
Event contracts Versioned JSON Schemas in contracts/ (envelope + payloads only), code generated for Go and TS; consumers tolerate unknown optional fields
Observability Prometheus metrics + /health (liveness/readiness) per service (Part II §7)
Frontend The existing SvelteKit static SPA (client/web-app), served behind the gateway, calling /api/v1

3. Service code structure

Every service uses the same hexagonal layout, so the domain never depends on HTTP, Postgres or Kafka:

services/<name>/
├── cmd/server/            # Go entry point (TS: src/server.ts)
├── internal/              # TS: src/
│   ├── domain/            # entities, value objects, policies
│   ├── application/       # use cases and ports
│   ├── adapters/
│   │   ├── http/
│   │   ├── postgres/
│   │   └── kafka/
│   └── config/
├── migrations/
├── queries/ + sqlc.yaml   # Go services only
├── go.mod / package.json
└── Dockerfile

4. Repository layout

packytrace/
├── client/web-app/            # SvelteKit SPA (existing)
├── contracts/                 # JSON Schemas + Go/TS codegen config
├── services/
│   ├── api-gateway/
│   ├── passport-service/
│   ├── personalization-service/
│   ├── fridge-service/
│   ├── identity-service/
│   ├── measurement-pipeline/
│   └── brand-analytics-service/
├── deployment/
│   ├── docker-compose.yml     # services + Postgres + Kafka + Keycloak
│   └── k8s/                   # Part III
├── go.work                    # spans the Go modules
└── docs/

5. Local development

One command brings up the full system:

docker compose -f deployment/docker-compose.yml up

That starts Postgres (with the six schemas and roles provisioned by migration), Kafka, Keycloak, and all seven services. The SPA runs against the gateway with npm run dev inside client/web-app during frontend work.