Skip to main content

Backend Gateway ↔ Child Association

Association authority is server-side. Firmware consumes only the effective current state through onboarding/sync.

Canonical model

GatewayChildAssociation in IOT.App:

Id                    Guid
ProjectId Guid (real relational FK)
GatewayDeviceSerialId Guid (cross-service reference)
ChildDeviceSerialId Guid (cross-service reference)
BoundAtUtc DateTime
UnboundAtUtc DateTime?
UnbindReason bounded string
ConcurrencyToken rowversion

GatewayDeviceSerialId and ChildDeviceSerialId reference IOT.Center.DeviceSerial.Id. Because service databases are separate, these are not cross-database SQL foreign keys.

Active association

UnboundAtUtc == null

A SQL Server filtered unique index guarantees at most one active Gateway per Child:

builder.HasIndex(x => x.ChildDeviceSerialId)
.IsUnique()
.HasFilter("[UnboundAtUtc] IS NULL");

Routing

Child DeviceSerial
→ active GatewayChildAssociation
→ GatewayDeviceSerialId
→ Physical Gateway DeviceSerial
→ dn/cmd

Never route using the latest historical row.

Delete and reassociation

  • unbind closes the row by setting UnboundAtUtc/reason; it does not delete history
  • reassociation closes the old row and creates a new one
  • deleting a Gateway never deletes Child identity/history
  • local Gateway factory reset does not mutate Backend association by itself