Skip to content

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.