Skip to main content

Audit Service — Domain Model

Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · 02 DDD

1. Aggregates

1.1 AuditEntry (root aggregate — immutable)

Write-once. No methods modify state post-persist. The immutability invariant is enforced at the PostgreSQL role level.

FieldTypeDescription
idAuditEntryIdULID with aud_ prefix
tenantIdTenantId | nullnull = platform-level event
eventTypeAuditEventTypeTaxonomy enum (see §3)
actorIdUserId | nullUser or service account ID
actorTypeActorTypeUSER | SERVICE_ACCOUNT | SYSTEM
resourceTypeResourceTypee.g. USER, PATIENT, TENANT
resourceIdstringID of the affected resource
actionAuditActionCREATE | UPDATE | DELETE | READ | EVALUATE | EXPORT
outcomeAuditOutcomeSUCCESS | FAILURE | PARTIAL
sourceServicestringOriginating service name
sourceEventIdstringCloudEvents id — globally unique, enables dedup
nodeIdNodeId | nullHierarchy node context if applicable
metadataRecord<string, unknown>Additional context (changed fields, IP, purpose)
chainHashChainHashSHA-256 hex 64 chars
occurredAtTimestampWhen the action happened (from event envelope)
recordedAtTimestampWhen Audit Service ingested the entry (server time)

Invariants:

  • sourceEventId is unique; duplicate ingestion is idempotently ACK'd and silently skipped.
  • chainHash = SHA-256(prev_id : sourceEventId : tenantId : occurredAt : resourceId).
  • No UPDATE or DELETE is ever issued against this aggregate.
  • tenantId may only be null for actorType = SYSTEM or platform-level sources.

State machine: None. AuditEntry is a terminal write-once fact.


1.2 AuditExport (aggregate)

Represents an asynchronous export job. Mutable until reaching a terminal state.

FieldTypeDescription
idAuditExportIdULID with exp_ prefix
tenantIdTenantId | nullScope of the export
requestedByUserIdSuper Admin who requested it
statusExportStatusqueued | processing | completed | failed
filtersAuditQueryFiltersImmutable filter bag
formatExportFormatndjson | csv
fileUrlstring | nullSigned URL; set on completion
recordCountnumber | nullSet on completion
createdAtTimestamp
completedAtTimestamp | null

State machine:

Invariants:

  • fileUrl must be set before transitioning to completed.
  • On completed → emit audit.export.completed event.
  • The export request is itself stored as an AuditEntry with type BULK_EXPORT.

2. Value Objects

NameTypeDescription
AuditEntryIdBranded<string, 'AuditEntryId'>ULID with aud_ prefix
AuditExportIdBranded<string, 'AuditExportId'>ULID with exp_ prefix
ChainHashBranded<string, 'ChainHash'>64-char hex SHA-256
AuditQueryFiltersValue objectImmutable filter bag

3. Enums

EnumValues
ActorTypeUSER, SERVICE_ACCOUNT, SYSTEM
AuditActionCREATE, UPDATE, DELETE, READ, EVALUATE, EXPORT
AuditOutcomeSUCCESS, FAILURE, PARTIAL
ExportFormatndjson, csv
ExportStatusqueued, processing, completed, failed

4. Event Type Taxonomy

CategoryTypes
IdentityUSER_CREATED, USER_UPDATED, USER_SUSPENDED, USER_REACTIVATED, USER_DEACTIVATED, USER_LOGIN, USER_LOGIN_FAILED, USER_LOGOUT, USER_MFA_ENROLLED, USER_SESSION_EXPIRED
Service AccountsSERVICE_ACCOUNT_CREATED, SERVICE_ACCOUNT_REVOKED, SERVICE_ACCOUNT_SECRET_ROTATED
TenantTENANT_CREATED, TENANT_ACTIVATED, TENANT_SUSPENDED, TENANT_REACTIVATED, TENANT_TERMINATED, TENANT_CONFIG_CHANGED, SUBSCRIPTION_UPDATED, SUBSCRIPTION_EXPIRED
HierarchyNODE_CREATED, NODE_UPDATED, NODE_DEACTIVATED, EDGE_CREATED, EDGE_REMOVED, PROFILE_CREATED, MEMBERSHIP_ASSIGNED, MEMBERSHIP_REMOVED
LicensingLICENSE_ASSIGNED, LICENSE_UPDATED, LICENSE_REVOKED, LICENSE_EXPIRED
Access PolicyROLE_CREATED, ROLE_UPDATED, ROLE_ARCHIVED, ASSIGNMENT_CREATED, ASSIGNMENT_REMOVED, POLICY_CREATED, POLICY_UPDATED
Platform AdminPLATFORM_CONFIG_CHANGED, FEATURE_FLAG_CREATED, FEATURE_FLAG_UPDATED, FEATURE_FLAG_ARCHIVED
Patient Data AccessPATIENT_RECORD_READ, CLINICAL_NOTE_READ, LAB_RESULT_READ, MEDICATION_READ
SecurityACCESS_DENIED, ACCOUNT_LOCKED, SUSPICIOUS_ACTIVITY, BULK_EXPORT
ConfigFEATURE_CREATED, FEATURE_UPDATED, ROLE_GRANT_CREATED, ROLE_GRANT_UPDATED, USER_OVERRIDE_CREATED, USER_OVERRIDE_DELETED, DESIGN_TOKEN_UPDATED

5. Domain Events Emitted by This Service

EventNATS SubjectTrigger
audit.export.requested.v1com.ghasi-ehr.audit.export.requestedExport job queued
audit.export.completed.v1com.ghasi-ehr.audit.export.completedExport file written
audit.dlq.alert.v1com.ghasi-ehr.audit.dlq.alertMessage max-deliver exceeded

6. Ubiquitous Language

TermDefinition
AuditEntryAn immutable, write-once record of a compliance-relevant platform event
Chain HashSHA-256 digest binding each entry to the previous one; enables tamper detection
Accounting of DisclosuresHIPAA-analogue right: patient knows who accessed their record and when
IngestionConsuming a CloudEvent from NATS and normalising it into an AuditEntry
Dead Letter QueueNATS subject audit.dlq for messages exhausting max-deliver retries
ExportAsync job streaming AuditEntry rows matching a filter set to NDJSON or CSV
Tamper-evidentProperty that any modification of a stored entry invalidates the chain hash
Meta-auditAuditing the audit service's own operations (e.g. bulk exports)
Platform LevelEvents where tenant_id IS NULL — actions by platform infrastructure
DedupIdempotent skip of an event whose source_event_id already exists