Cross-System Identity¶
Decided, not yet built
This page describes the target architecture for cross-system customer identity — decided, but not yet implemented. It builds on Tenant Model — read that first for the Tenant/SystemConnection/adapter vocabulary and the overall picture — and consolidates two internal design notes, the "Identity Layer" and the "Bridge", into a single model. For the rationale behind the decision, see ADR-0003.
In one sentence¶
Cross-System Identity gives Conflux a way to recognise that the same real customer shows up as separate records in different connected systems, and to link those records together — without merging them into one.
What it's about¶
Picture the same passenger from Tenant Model, Acme GmbH, checking in luggage at two different tracks — once boarding via WooCommerce, once via rapidmail. Today the station has no baggage tag connecting the two suitcases; they're just two unrelated bags that happen to belong to the same traveller. A CustomerIdentity is that baggage tag: it links the two Customer rows as the same real person, scoped to one Tenant, without ever combining the suitcases into one.
Matching only ever happens within one Tenant's scope — see Tenant Model: the two functions of a Tenant for why crossing that boundary would be a data leak.
Glossary¶
| Term | Meaning |
|---|---|
| CustomerIdentity (new) | A tenant-scoped link marking several per-connection Customer rows as the same real person. A linkage, not a merged master record. |
| match_key | The normalised (lower-cased) email address used to decide that two Customer rows are the same person. |
For Tenant, SystemConnection, and Adapter/System, see the Tenant Model glossary.
Entities and data model¶
| Entity | Status | Core | Replaces / relates to |
|---|---|---|---|
CustomerIdentity |
new | tenant_id, match_key (normalised email); links N Customer rows; no golden record |
the reconciled cross-system identity layer |
CustomerIdentity is modelled as a link, not a merged golden record — see ADR-0003 for why.
Details (technical): why the correlation layer sits below the connection
Customer, Order, and Product use the BelongsToSystemConnection trait and hang off system_connection_id, not off App/Tenant. The customers table enforces unique(['system_connection_id','source_id']).
Consequence today: the same real customer present in two connected systems produces two independent Customer rows with no link. CustomerIdentity is the missing link over these rows; it does not change how rows are stored.
Details (technical): CustomerIdentity shape
A separate linking entity, not an identity_id column on Customer. Fields: id (uuid), tenant_id (fk), match_key (normalised, lower-cased email). A Customer joins to it (start with a nullable fk customer_identity_id; move to a pivot only if a row may belong to several identities). Uniqueness: unique(['tenant_id','match_key']), so one real person maps to one identity per tenant.
The DTO layer already carries per-record source identity (CustomerData.sourceId, sourceSystem), so the ingest path has what it needs to compute match_key.