Component & Connector Views¶
Four views are needed because one picture cannot mix call-return and publish-subscribe without becoming unreadable. Each view keeps a single connector style and a single concern.
Status: current implementation view, verified 2026-08-01. The source of all four images is component-and-connector.drawio; make docs-diagrams regenerates them. Environment-specific timings are labeled in the diagram and prose.
View 1: Public edge¶

Open the public-edge diagram at full size.
The only entry points into the system are the two SvelteKit clients and the GS1 Digital Link resolver, and all three arrive at api-gateway. The gateway is the public edge rather than a bounded context: no domain, no use cases, no data store. Each arrow out of it is labeled with the public routes it proxies, so the whole external HTTP surface is visible in one place. Scanning a barcode opens the resolver directly on the gateway and Passport answers with an HTTP 302 into the Web App, which is why that arrow starts at the Consumer and not at the client.
The diagram starts at the application boundary behind Caddy. In deployed environments Caddy is the public network endpoint that serves the static clients and proxies application routes to the gateway; it is omitted here only to keep this view focused on application routing.
View 2a: Internal collaboration¶

Open the internal-collaboration diagram at full size.
Behind the gateway, synchronous service collaboration uses internal REST. Asynchronous facts use Kafka, as shown in View 3; no service imports another service's domain or application code. The tagged values carry the resilience properties that make these calls safe. Passport calls Personalization with a 2 second timeout and degrades to an unauthenticated passport on failure, the Open Food Facts client sits behind a circuit breaker with a persistent cache, and the brand producer simulators are polled out of band every 30 seconds so producers never observe an individual scan. The deployment manifests set that interval to 30 seconds; the service's standalone default is one minute.
View 2b: Shared data¶

Open the shared-data diagram at full size.
One Postgres instance holds one schema per data-bearing service, and each service connects with its own role granted access to its own schema only. The isolation is enforced by the database rather than by convention: an arrow to a foreign schema cannot be drawn because the grant does not exist. Brand ids are shared references agreed across schemas instead of foreign keys, and every brand-owned record carries brand_id.
View 3: Integration facts and the privacy wall¶

Open the events-and-privacy diagram at full size.
The asynchronous view uses publish-subscribe only. Five services publish versioned integration- fact topics. In local Compose, measurement-pipeline subscribes to the Product and Verdict topics and aggregates over a 30-second demonstration window; the service default is seven days. It publishes BrandMetricBatchPublished only when the group reaches the minimum size. That single connector is the only one crossing the privacy wall, which is what keeps per-scan and per-visitor facts on the consumer side. Personalization consumes ConsentRevoked from the Identity topic for profile erasure. Measurement consumption of the Fridge, Shopping List and visitor-link facts is decided but not yet built.