Skip to content

Containers

The fifteen deployable units, their ports, and the calls between them (C4 level 2).

Two boundaries. store-core runs once per environment: six containers and one Postgres. store-pod runs once per pod: nine containers, one Postgres and one object store. Every pod is a complete, isolated copy; nothing in a pod is shared with another pod. The shapes follow the legend.

Diagram C2

What the edges mean:

  • console-ui → gateway. The console is served by the gateway's catch-all route and calls the platform APIs on the same origin under /tenancy/**, /billing/**, /pod-registry/** and /uaa/**. It holds no token; the gateway's session does.
  • gateway → tenancy, billing, pod-registry, uaa. GatewayRouteLocatorImpl strips the prefix and relays the signed-in user's token (tokenRelay()). The /uaa/** route alone keeps its prefix and adds X-Forwarded-Prefix: /uaa.
  • gateway → spg. PodClient builds one route per pod at runtime: /spg/** with store= and pod= query parameters, StripPrefix=1, TokenRelay. This is how a merchant edits a product that lives in any pod through one origin. See Gateway routing.
  • spg → pod services and landing-ui. The Caddyfile path-routes /content*, /merchant*, /inventory*, /catalog*, /checkout*, /cua*, /payment*; everything else goes to landing-ui after domain_lookup has injected the store headers. See store-pod and Edge and custom domains.
  • landing-ui → spg (server-side). The storefront's server renders read merchant, content, catalog, checkout and inventory through the pod's own spg (INTERNAL_SPG), so a server-side read and a browser-side call take the same route.
  • pod services → uaa. Every pod service is a JWT resource server that accepts tokens from uaa (staff) and cua (shoppers), and authenticates to its peers with a client_credentials client against uaa. See Authentication.
  • catalog → billing. catalog-core guards product writes with StoreEntitlements from billing-external-api. It is the one call from a pod back into store-core.
  • payment → Stripe, billing → Stripe. Store payments and platform subscriptions; each receives Stripe's webhooks on its own public endpoint.

The fifteen containers

ContainerLayerPortRuntimeOwnsFronted by
store-core-gatewaystore-core8000Spring Cloud Gateway (WebFlux)The browser session, the OAuth2 client login against uaa, token relay, the per-pod route tableitself (gateway.com)
uaastore-core8001Spring Boot, embeds Angular uaa-feStaff and merchant identity: OAuth2 authorization server and OIDC provider, users, roles, clients, brokered login, its admin appstore-core-gateway (/uaa/**); its own host uaa.gateway.com
console-uistore-core8011Angular 20 SSRThe merchant and platform-admin consolestore-core-gateway (catch-all)
tenancystore-core8020Spring BootOrganizations, stores, members, sign-up, the store → pod binding, store provisioning through the outboxstore-core-gateway (/tenancy/**)
billingstore-core8021Spring BootPlans, per-store subscriptions, the Stripe webhook, entitlements and store quotasstore-core-gateway (/billing/**)
pod-registrystore-core8022Spring BootThe pod catalog: identity, endpoint, health, capacity, placementstore-core-gateway (/pod-registry/**)
spgstore-pod80 / 443CaddyOn-demand TLS for custom domains, domain → store lookup, path routing into the poditself (the store's host)
landing-uistore-pod8110Next.js 16 / React 19The storefront: one app, every theme, the page cachespg (fall-through)
merchantstore-pod8120Spring BootStore configuration: languages, currency, domains, address; the routing hooks spg callsspg (/merchant*)
contentstore-pod8121Spring BootPages, posts, banners, FAQ, policies, menus, media library, site settings and appearancespg (/content*)
catalogstore-pod8122Spring BootProducts, variants, options, categories, brands, product types and groups, imagesspg (/catalog*)
checkoutstore-pod8123Spring BootCart, orders, customers, countriesspg (/checkout*)
cuastore-pod8124Spring BootShopper identity: a headless OAuth2 authorization server, registration, social loginspg (/cua*, prefix kept)
paymentstore-pod8125Spring BootPayment provider configuration per store, payment execution, provider webhooksspg (/payment*)
inventorystore-pod8126Spring BootStock, prices and reservations, keyed by skuspg (/inventory*)

Ports, hostnames and the fronting gateway (gateway-service-name) are recorded per service in common-config.yml. A service is reachable on its own port only inside its namespace (store-core.cvhome.lcl, store-pod-<id>.cvhome.lcl); from anywhere else it is a path on its gateway, with the prefix stripped (except /cua and /uaa).

Data

Each Spring Boot service owns one Postgres schema, named after the application (pod_registry, content, payment, ...), and ships its own DDL as a schema.sql. Foreign keys never cross a schema. store-core shares one Postgres; each pod has its own. content, merchant and payment configure an S3 client for files (MinIO locally; S3 behind CloudFront on AWS), and spg keeps its certificates in an S3 bucket.

Shared libraries

store-commons/ is libraries only; nothing in it is deployed. Every service inherits its cross-cutting behavior by depending on these modules:

ModuleWhat it gives a service
commonsThe value objects used everywhere in place of raw strings: StoreMerchantId, ManagerOrgId, PodId, LanguageCode, Pod, PodEndpoint, Theme, ...
errorsThe shared error catalog and ProblemDetail handling
autoconfigureMulti-issuer JWT decoding, the permission evaluator, web clients, and the shared YAML (common-config.yml, lcl-config.yml, fargate-config.yml)
uaa-client, uaa-client-implA typed SDK for uaa's admin API
sso/sso-core, sso/sso-eventsThe authorization-server core that uaa and cua are both built on
secret-crypto/*Encryption of stored secrets (payment keys, social-login credentials): local AES or AWS KMS, with a caching decorator
ecs-commons/*Cloud Map service discovery and ECS task metadata for Fargate; inert locally
test-supportTestcontainers, test JWT signing and the integration-test annotations; never on a production classpath
ui-kitThe Angular component kit console-ui and uaa-fe share

A second module is also called store-commons: store-pod/commons/store-commons is the pod-scoped shared domain. Refer to either by its Gradle path.

Images

Container images are named <layer>/<service>: store-core/uaa, store-core/console-ui, store-pod/catalog, store-pod/spg, and so on. Nothing deploys from the application repository's CI; images build in CodeBuild and Terraform applies them. See Deployment: AWS and Deployment: local.


Source of truth: cvhome store-commons/autoconfigure/src/main/resources/common-config.yml, store-pod/spg/Caddyfile, store-core/gateway/gateway-service/src/main/java/com/asrevo/cvhome/gateway/config/GatewayRouteLocatorImpl.java, store-core/gateway/gateway-service/src/main/java/com/asrevo/cvhome/gateway/client/PodClient.java, .claude/skills/project-structure/SKILL.md and references/{store-core,store-pod,shared-libraries,database-schemas,service-to-service}.md, store-pod/catalog/catalog-core (StoreEntitlements), store-pod/landing-ui/libs/services/src/store-context-ssr-utils.ts.