Names as federation vocabulary¶
Family, Role, and Model names are not just labels. Each of these aggregates derives its identity from its name (or its vendor key), so the same concept resolves to the same id at every facility. A name is therefore a cross-facility contract: rename a Family and you mint a new identity that no longer matches the same Family elsewhere. This page is the system those names follow.
The one principle¶
A name-keyed aggregate's id is
uuid5(namespace, the aggregate's federation-stable natural key).
The natural key differs because the things differ:
| Aggregate | Natural key the id is derived from | Is the name the key? |
|---|---|---|
Family |
the name | yes |
Role |
the name | yes |
Model |
the vendor key (manufacturer, part_number) |
no: the name is a label |
A Family or Role has no identifier deeper than its name: "Camera" is the identity. A Model is a real-world vendor product that already has a globally stable identifier (its SKU), so its id is derived from (manufacturer, part_number) and the name rides on top as a human handle. It is one principle, applied to each thing's truest facility-independent identifier.
Practical consequence:
- Renaming a
FamilyorRolechanges its id. That is a federation break and a data migration, not a cosmetic edit. - Renaming a
Modelnameis harmless: identity is the vendor key, so the label can change freely.
Three axes¶
The three aggregates are orthogonal axes, and an Asset sits at their intersection: it IS-A Family (anatomy), can present as a Role (function), and may bind a Model (vendor).
| Aggregate | Axis | Answers | Grammar | Casing |
|---|---|---|---|---|
Family |
anatomy | what it IS (device class) | thing-noun, singular, noun-LAST | PascalCase |
Role |
function | what contract it fulfills | agent-noun (-er / -or) | PascalCase |
Model |
vendor | what product it is | manufacturer_<model-or-line> |
lower_snake |
The grammar is the tell. Camera, Scintillator, Hexapod, Housing are thing-nouns (Families: what the device is). Detector, Positioner, Controller, Sensor are agent-nouns (Roles: the job a thing does). When you are unsure which aggregate a new name belongs to, the part of speech usually decides it.
Per-aggregate rules¶
Family (anatomy)¶
- PascalCase, singular, a thing-noun.
Camera, notCameras, notDetector(an agent-noun, which names a Role). - Anatomical, not vendor / substrate / deployment / content. A
Familynames what the device IS, device-agnostic across facilities. Vendor identity lives on the boundModel; substrate (FPGA, VME card) lives insettings; deployment context never enters the name. This is whyTriggerFPGAbecameTimingControllerandOpticalHousingbecameHousing("Optical" named the contents, not the chassis's own nature). - Noun LAST (R3). A qualifier precedes the family noun:
RotaryStage,LinearStage,MotionController. Single-word is preferred; a compound is justified only when the qualifier names the device's own intrinsic nature (how it moves, what signal it generates), not its contents or the assembly it serves. <Domain>Controllerfor any separately-modelled, field-replaceable control-electronics box (MotionController,TimingController); the driven device carries acontroller_idback-reference. The same<Domain>Controllerform also names a continuous-setpoint actuator that itself performs (rather than supervising a subordinate):TemperatureControllerpresents theRegulatorRole and carries theSettableaffordance, not theControllerRole. The vendor-idiomatic noun ("temperature controller") wins the read-aloud test; the presented Role is the disambiguator (see theControllervsRegulatornote under Role).- A
Familymust have, or plausibly have, instances. AFamilythat exists only to be a binding target is a presenter Family, an anti-pattern (see Deprecations).
Role (function)¶
- PascalCase, singular, an agent-noun:
Detector,Positioner,Controller,Sensor,Regulator. ARolenames the job, not the device. - A
Roleis the functional binding contract aMethodtargets throughpresents_asandRoleRequirement. It carries affordances,produces,consumes, and a docstring; it has no settings, no instances, and no ports. Controlleris the bareRole(the function);<Domain>Controlleris theFamily(the box). The qualification keeps the two distinct. The lexemeControllercan sit on both axes: a supervisory<Domain>ControllerFamily (MotionController,TimingController) presents theControllerRole, while the actuatorTemperatureControllerFamily presentsRegulator; the presented Role, not the Family name, is the disambiguator.Regulatoris the bareRolefor a device that drives a continuous process variable to a commanded setpoint (it performs, unlike the supervisoryController; it actuates, unlike the read-onlySensor). It requires theSettableaffordance.
Model (vendor)¶
- Identity is
(manufacturer, part_number), never thename. Thenameis a human handle and a cross-reference slug (the deployment descriptor'smodel:fields point at it). - Handle form:
<manufacturer-slug>_<recognizable model or product line>,lower_snake. The full SKU lives inpart_number, so the handle does not mash it, and it carries no function word, deployment token, or placeholder cruft. Example:flir_oryxwithpart_number = ORX-10G-51S5M-C, notflir_oryx_orx_10g_51s5m_c. - Unconfirmed products: a
Modelwhosepart_numberis theunknown-pending-confirmationsentinel gets a random id, so two genuinely-unidentified units stay distinct; it re-registers under its derived id once the real part number is confirmed. Such aModel's name is provisional and is a beamline-staff question, not a guess.
Canonicalization contract¶
Because the name is the key for Family and Role, the derivation canonicalizes so the same concept cannot fork:
- Case-fold. The name is lower-cased before hashing, so
Cameraandcameraare one identity. Case is presentation;PascalCaseis the display convention. - NFC-normalize. Composed versus decomposed Unicode (an accent written as one character versus a letter plus a combining mark) is folded to one form before hashing, so a name that renders identically cannot fork across facilities.
Modelnormalizes its manufacturer key the same way. - Per-aggregate namespace.
Family,Role, andModeleach derive in their own namespace UUID, so aCameraFamily and a hypotheticalCameraRole never collide in id space.
Deciding which aggregate a new name belongs to¶
- Is it a vendor product (a thing you buy, with a manufacturer and a part number)? Then a
Model. - Is it a binding contract a
Methodneeds (a job, an agent-noun)? Then aRole. - Is it what a device fundamentally IS (a thing-noun, substrate-agnostic, reusable across facilities)? Then a
Family. - Is it a variant of an existing one along a tunable axis (a faster camera, a thicker scintillator)? Then it is not a new name at all: it is
settingson the existing one (settings over subtypes).
Deprecations and smells¶
- Presenter Families. A
Familythat exists only as a presenter target, with no instances of its own, is a transitional shim from before Roles existed. The forward path is aRolepluspresents_as: frozenset[RoleId]. The last presenter-Family shim has been retired:Imagerwas removed from the catalog, and detector Assemblies now present theDetectorRole viapresents_as. Binding aMethodto a real, instancedFamily(needed_family_ids = {Camera}) stays valid; only the instance-less contract proxy is the anti-pattern. - A bare agent-noun Family. A bare
ControllerFamily, or any agent-noun used as a Family name: agent-nouns name functions (Roles), not device classes. Qualify it (MotionController) or move it to aRole. (Imager, now retired, was an example of this smell.) - A Model
nametreated as identity. It is a label. Identity is(manufacturer, part_number).
See also¶
- Equipment module: the concrete Family corpus, the
<Domain>Controllerconvention, and the function-by-anatomy matrix. - Conventions: identifiers, units, and the wider repo conventions.