Side-partner architecture¶
This page covers the runtime architecture of the wes-{partner}-adapter — the per-partner outbound translator FG.AI ships under Path C. It complements the overview: the overview describes Path C as an integration topology; this page describes what the actual adapter looks like, how it is deployed, and what guarantees it provides.
For onboarding and operational concerns, see side-partner-onboarding and side-partner-operations.
Where the adapter sits¶
Partner WMS FG.AI WMS wes-{partner}-adapter
───────────── ───────── (per-(partner, tenant)
Helm release)
┌──────────────────┐
▲ │ pim/v1/skus │
│ │ pim/v1/locations │ ◄─────┐ Partner pushes
Partner outbound │ wms/v1/work-… │ │ master + WO.
endpoints │ wms-partners/v1/ │ │ UAT bridge: JWT
▲ │ inventory-delta│ │ for everything.
│ │ authenticate │ ◄─────┘ Post-retirement:
│ │ │ mTLS prod / API
│ │ │ key dev for
│ │ │ writes; JWT for
│ │ │ reads only.
│ └────────┬─────────┘
│ │
│ ▼
│ ┌──────────────────┐
│ │ Kafka cluster │
│ │ wms.{tenant}.* │
│ │ wes.{tenant}.* │
│ └────────┬─────────┘
│ │
│ ▼
│ ┌──────────────────┐
└───────────────────────────────│ wes-{partner}- │
outbound POST + Authorization: │ adapter │
Bearer {partner-issued token, │ (one Helm release│
cached, refreshed on expiry} │ per tenant) │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Postgres │
│ wms_partners DB, │
│ schema per │
│ (partner, tenant)│
└──────────────────┘
Inbound from the partner (master + work orders + inventory query) hits FG.AI WMS public REST directly. Outbound TO the partner (typically per-WO completion postback + inventory delta postback) is the adapter’s job — it subscribes to FG.AI Kafka, aggregates state, translates payload shapes, and posts.
Deployment topology — one Helm release per (partner, tenant)¶
Each (partner, tenant) pair is its own Helm release in a shared partner-adapters k8s namespace. For a partner serving N FG.AI tenants, FG.AI runs N pods of wes-{partner}-adapter, each bound to one tenant.
Aspect |
Per- |
Shared |
|---|---|---|
Helm release name |
|
— |
Pod labels |
|
|
Kafka consumer-group |
|
— |
Postgres schema |
|
|
k8s namespace |
— |
|
Partner credentials |
SecretsManager entry per tenant |
— |
DotID service account |
per tenant with |
— |
NetworkPolicy |
per release; egress allow to partner host CIDR; ingress none |
— |
This matches FG.AI’s overall multi-tenant canon (per-tenant Kafka prefix per WES ADR-0020; per-tenant connector isolation per WMS ADR-0024 §5) and makes the eventual FlexSync migration a 1:1 swap when the partner onboards as a FlexSync connector post-FlexSync-v1 GA.
For a partner reading this: when your engagement adds a new FG.AI tenant, FG.AI provisions an additional Helm release for the new tenant. No code change on either side.
Inheritance chain¶
Every wes-{partner}-adapter is a Spring Boot 4 service inheriting from a chain of parent POMs:
spring-boot-starter-parent:4.0.6
│
▼ extends
com.flexgalaxy:fgai-service-template-parent (FG.AI canon: Spring Boot 4,
│ Java 21, protobuf-java pin,
│ testing BOM, JaCoCo gate)
▼ extends
com.flexgalaxy:wes-partner-adapter-parent (partner-adapter canon:
│ Resilience4j, OTel tracing,
│ wes-partner-adapter-common)
▼ extends
{partner-base-package}:wes-{partner}-adapter (per-partner repo)
Bug fixes in scaffolding classes (token cache, Kafka consumer base, etc.) propagate to all partner adapters via a parent-POM-bump on wes-partner-adapter-parent followed by per-adapter rebuild. Spring Boot version bumps cascade from fgai-service-template-parent through the chain.
Scaffolding inherited from wes-partner-adapter-common¶
The common library ships these reusable classes — every wes-{partner}-adapter declares it as a dependency and uses or extends them:
Class |
Role |
|---|---|
|
In-memory + Postgres-backed bearer-token cache. Refreshes proactively before expiry; invalidates on observed 401s and retries once with a fresh token. |
|
Per-partner implementation wires the partner’s |
|
Spring |
|
Abstract Kafka consumer enforcing dedup + retry + audit envelope discipline. Every adapter’s |
|
Per-partner implementations translate FG.AI source events to the partner’s outbound payload shape. |
|
|
|
W3C |
|
|
ArchUnit gates¶
Every wes-{partner}-adapter inherits three ArchUnit gates from the common library — they run as part of mvn verify and fail the build on violation:
Rule |
Why |
|---|---|
No DotID JWT in outbound flow. Classes under |
Partner outbound uses bearer-token auth via |
Kafka consumers extend the base. Classes annotated |
Enforces dedup + retry/DLT + audit envelope discipline for every consumer. |
No direct partner host strings outside |
Partner URLs come from |
Event flow — what hits the adapter¶
The adapter consumes a partner-specific subset of FG.AI Kafka topics:
Topic family |
Partner-relevant content |
|---|---|
|
Document state transitions on the fulfillment side (Shipper closed, etc.) |
|
Every WMS inventory movement; aggregated for inventory-delta postbacks |
|
Executor-side confirmations (per-shift summaries, per-task confirmations) |
|
Lifecycle signals ( |
The specific subset for each adapter is declared in its application.yml under partner-adapter.kafka.subscription-topics. Aggregation state (per-WO line accumulation, per-shift-window inventory transitions) lives in the per-(partner, tenant) Postgres schema. An outbound_dedup table prevents duplicate POSTs after retry. The token_cache table persists the partner bearer token + expiry so JVM restart does not force re-auth.
What the adapter does NOT do¶
Does not receive inbound from the partner. Partner-side pushes (master data + work orders) hit FG.AI WMS public REST (
pim/v1/,wms/v1/) directly — the adapter is outbound-only. Inbound auth is stage-specific: during the UAT bridge period, writes use the partner-bearer-JWT (transitional convenience — same JWT covers reads + writes againstwms-partners-bridge-serviceinfg-ai-dev-services). Post-bridge-retirement (when WMS Phase 1 + Phase 2 ship), inbound write auth follows the canonical FG.AI scheme — mTLS in prod-intl + prod-cn, API key bearer in dev — handled at the WMS partner-registry layer, not at the adapter. Thewms-partners/v1/partner-bearer-JWT applies canonically to reads +/authenticateonly post-retirement. See side-partner-interface-mapping § Authentication for inbound writes.Does not serve
wms-partners/v1/inventory-delta. That endpoint lives in the WMS reporting service, not the adapter.Does not implement reconciliation. FlexSync v1 ships reconciliation as a framework capability; Path C does not. Partners reconcile by polling
inventory-delta.Does not round-trip partner pushes back to the partner. Each leg is one-way — partner→FG.AI inbound is not echoed back; FG.AI→partner outbound treats the synchronous HTTP response (status code + response-body business code, per interface-mapping § Error code envelope conventions) as the application-level ack — there is no separate async confirmation message expected from the partner after the POST completes.
Does not invent data. If a required outbound field is missing from the FG.AI event (e.g., operator ID is null when the partner spec requires it), the adapter skips that line and emits an audit event. It does not substitute defaults.
Retirement criterion¶
When the partner onboards as a FlexSync connector post-FlexSync-v1 GA, the wes-{partner}-adapter is decommissioned per the Path C agreement. The partner_tenant_binding row transitions active → migrating → retired driving the Helm release decommission via an orchestration handler. Per-tenant Postgres schemas are dropped after a backup-confirmation window; per-tenant SecretsManager entries are deleted with a 30-day recovery window; per-tenant Terraform terraform destroy removes the IAM role + Kafka ACLs.
When the last tenant for the partner transitions to retired, the per-partner GitHub repo is archived (not deleted — history preserved) and the MAX_SIDE_PARTNERS=2 budget reclaims the slot (the partner is no longer in PATH_C topology; FlexSync continues serving them).