Skip to main content

AI Gateway Service — Domain Model

Status: populated Owner: TBD Last updated: 2026-04-17 Companion: Service Template · 03 platform-services · 02 DDD

1. Aggregates

AggregateRootKey invariantsLifecycle
AIDecisionAIDecisionEvery draft has one AIProvenance; HITL required flag derived from featureKey; cannot transition from rejected or accepted back to draftdraft → under_review → accepted | rejected → archived
AIProvenanceAIProvenanceImmutable once written; always references one AIDecisioncreated only; never mutated
ProviderRoutingRuleProviderRoutingRuleAt least one active rule per active featureKey per tenant; residency constraint honoureddraft → active → deprecated
PromptTemplatePromptTemplateSemver-versioned, tenant-scoped or global; referenced by PromptTemplateRef { key, version }; never inlined in AIDecisiondraft → published → deprecated
TenantQuotaTenantQuotaRolling window enforced; cannot go negative; reset only by scheduleractive
ModerationFindingModerationFindingAlways linked to an AIDecision; category + severity requiredcreated only

2. AIDecision state machine

3. Entities

EntityOwner aggregateDescription
DecisionReviewEventAIDecisionAudit event per reviewer action (accepted/rejected/commented)
ProviderAttemptAIDecisionOne row per upstream provider call (provider, model, latencyMs, outcome)
QuotaWindowTenantQuotaImmutable counter snapshot per rolling bucket
ModerationCategoryModerationFindingCategory, score, threshold

4. Value objects

Value objectFieldsNotes
FeatureKeystring, 3–128 charse.g. patient_chart.note_summary, portal.triage
ProviderIdenum: anthropic, openai, azure_openai, bedrock, onprem_vllm, ollama, mockRegistered in config
ModelVersionprovider:model:versione.g. anthropic:claude-sonnet-4:2026-04-01
PromptTemplateRef{ key: string, version: string }Semver
Residencyenum: AF, AE, EU, US, ON_PREMDrives routing rule filter
ModerationVerdictenum: allow, flag, blockResult of classifier
HITLPolicyenum: none, required, required_for_phi, sampledPer-feature
CorrelationIdUUIDPropagates across services
ProvenanceIdULID, prefix prv_Immutable pointer from clinical artifacts
DecisionIdULID, prefix dec_AIDecision primary key

5. Ubiquitous language

TermMeaning
AssistA single inference call for content generation or classification
DecisionPersisted outcome of an Assist (draft + provenance + optional review)
DraftModel output that is not yet accepted by a clinical owner
HITLHuman-in-the-loop — clinician or reviewer must accept before downstream use
ProvenanceImmutable metadata describing who, what model, when, under which policy, with what moderation outcome, produced a draft
AcceptOwning module signals the draft has been used; triggers ai.decision.accepted
ModerationAutomated safety / PHI / injection check run on prompts and outputs
ProviderExternal or on-prem model endpoint (Anthropic, OpenAI, vLLM, …)
Routing ruleConfig entry that maps (tenant, feature, residency) → ordered provider list
Quota windowRolling time bucket for rate limiting assist calls
Feature keyStable identifier for an AI-enabled capability (e.g. imaging.preread.chest_xray)

6. Domain events

EventAggregateEmitted when
ai_gateway.assist.requested.v1AIDecisionAfter auth + quota consume, before provider call
ai_gateway.assist.completed.v1AIDecisionProvider returned output, provenance stamped
ai_gateway.assist.failed.v1AIDecisionPolicy deny / provider failure / moderation block
ai_gateway.decision.created.v1AIDecisionPersisted in draft state
ai_gateway.decision.hitl_queued.v1AIDecisionEnters under_review; notifies reviewer queue
ai_gateway.decision.accepted.v1AIDecisionOwner module accepted; includes provenanceId
ai_gateway.decision.rejected.v1AIDecisionReviewer rejected or timeout
ai_gateway.moderation.flagged.v1ModerationFindingScore above threshold
ai_gateway.provider.degraded.v1ProviderRoutingRuleCircuit breaker opened
ai_gateway.quota.exceeded.v1TenantQuotaRolling window exceeded

7. Invariants catalogue

#Invariant
INV-01No AIDecision exists without an AIProvenance row
INV-02AIProvenance rows are append-only
INV-03AIDecision.state transitions are one-way per the state machine
INV-04AIDecision.consumerService set at creation and immutable
INV-05A draft cannot be read by any service other than the creator or an authorised reviewer
INV-06HITLPolicy=required_for_phi forces under_review when the feature handles PHI
INV-07Quota consume is atomic with assist request acceptance

8. Open questions

  • Final catalogue of featureKey values per service (tracked via cross-service review).
  • Retention policy for rejected decisions (90 days vs 7 years compliance view).