Schema Translation via Adjoint Triple
The Problem
Different bounded contexts use different schemas. The EHR system has fields (npi, full_name, office_address) while the directory uses (provider_id, name, address, license). How do you safely move data between these schemas without creating dangling references or losing information?
The Adjoint Triple
A schema morphism
(left adjoint / pushforward) — pushes data forward along (pullback / reindexing) — pulls data back along (right adjoint) — right Kan extension (not implemented here)
The adjunction
| Direction | Functor | Safety | Data Coverage |
|---|---|---|---|
| Pull back | Safe — never creates dangling references | Only fields in the image of | |
| Push forward | Potentially unsafe — target fields not in | All source data transferred |
: The Safe Direction
deltaF reindexes a target instance along a schema morphism. It only reads existing data through the morphism, so it can never produce values that don't exist in the source:
import { deltaF } from '@ddt-ct/implementation'
const morphism = {
npi: 'provider_id',
full_name: 'name',
office_address: 'address'
}
const directoryRecord = {
provider_id: 'NPI-123',
name: 'Dr. Jane Doe',
address: '100 Downtown Ave',
license: 'MD-98765'
}
const ehrView = deltaF(morphism, directoryRecord)
// ehrView = { npi: 'NPI-123', full_name: 'Dr. Jane Doe', office_address: '100 Downtown Ave' }
: The Forward Direction
sigmaF pushes data forward. Fields in the target schema that aren't in the morphism's image get undefined:
import { sigmaF } from '@ddt-ct/implementation'
const ehrRecord = {
npi: 'NPI-123',
full_name: 'Dr. Jane Doe',
office_address: '100 Downtown Ave'
}
const directoryView = sigmaF(morphism, ehrRecord)
// directoryView = { provider_id: 'NPI-123', name: 'Dr. Jane Doe', address: '100 Downtown Ave' }
// Note: 'license' is undefined — not in the morphism's image
Constraint Preservation
preservesConstraint verifies that a morphism preserves referential integrity constraints — if the source schema has a foreign key from field A to field B, the morphism must map both fields:
import { preservesConstraint } from '@ddt-ct/implementation'
preservesConstraint(morphism, 'npi', 'full_name') // true — both mapped
preservesConstraint(morphism, 'npi', 'license') // false — 'license' not in morphism
Key Takeaway
Schema translation isn't string replacement — it's a functorial operation with formal safety guarantees.