One row per (templateKey, statusVariant) variant of a transactional email —
26 today. Rows are seeded by migration from the code registry
(@cfxlabsinc/notification-services/email-templates); there is no create
method, because an operator may only author copy for a variant the payload
union declares. Adding one is a registry change plus a migration, and
packages/notification-services/test/emailTemplateSchema.test.ts fails if the
two ever disagree.
Follows the product_route / product_fee_rule identity+version shape, with
three deliberate differences (RFC §4.2):
Two pointers, not one. Those tables carry a single currentVersionId
because an edit is immediately live. Email copy is reviewed before it
sends, so a save moves draftVersionId and only activation moves
liveVersionId.
No deferred FK. Theirs is DEFERRABLE INITIALLY DEFERRED because
currentVersionId is NOT NULL while the identity row is inserted before
its first version exists. Both of ours are nullable and variants seed with
no versions at all, so there is no cycle to defer.
A human-facing version counter on the version table, because the
editor has to say "live v3, draft v7".
Mutable on liveVersionId, draftVersionId and deletedAt only.
One row per
(templateKey, statusVariant)variant of a transactional email — 26 today. Rows are seeded by migration from the code registry (@cfxlabsinc/notification-services/email-templates); there is nocreatemethod, because an operator may only author copy for a variant the payload union declares. Adding one is a registry change plus a migration, andpackages/notification-services/test/emailTemplateSchema.test.tsfails if the two ever disagree.Follows the
product_route/product_fee_ruleidentity+version shape, with three deliberate differences (RFC §4.2):currentVersionIdbecause an edit is immediately live. Email copy is reviewed before it sends, so a save movesdraftVersionIdand only activation movesliveVersionId.DEFERRABLE INITIALLY DEFERREDbecausecurrentVersionIdis NOT NULL while the identity row is inserted before its first version exists. Both of ours are nullable and variants seed with no versions at all, so there is no cycle to defer.versioncounter on the version table, because the editor has to say "live v3, draft v7".Mutable on
liveVersionId,draftVersionIdanddeletedAtonly.