Audit System
Audit System
Relevant source files
The following files were used as context for generating this wiki page:
The Primebrick backend implements a robust, cross-cutting audit infrastructure designed to track every lifecycle event of domain entities. This system automatically records who changed what and when, storing state transitions as JSON deltas. The architecture ensures that audit trails are partitioned for performance and easily retrievable via standard API patterns.
Audit Architecture Overview
The audit system is triggered during repository write operations. When an entity decorated with @Auditable() is modified, the system calculates the difference between the old and new states and persists this "delta" into a dedicated audit table.
System Flow: Write to Retrieval
The following diagram illustrates the flow from a data modification to the retrieval of its audit history.
Audit Lifecycle Flow
Code
Sources: src/lib/audit/audit-service.ts:6-37, src/db/repository/audit-join-helper.ts:1-58
Core Components
1. AuditService and Delta Calculation
The AuditService is the primary interface for persisting audit records. It targets tables following the {table_name}_audit naming convention src/lib/audit/audit-service.ts:19-19.
- AuditAction: Captures the type of change:
INSERT,UPDATE,SOFT_DELETE,HARD_DELETE, orRESTOREsrc/lib/audit/audit-types.ts:1-7. - Delta Format: Changes are stored as a
jsonbobject where each key represents a field, containing an object witholdandnewvalues src/lib/audit/audit-service.ts:16-16. - Database Partitioning: Audit tables are automatically partitioned by month using
pg_partmanto ensure long-term scalability src/db/database-patch-to-sql.ts:120-122.
For details on implementation and the delta logic, see AuditService and Delta Calculator.
2. Auditable Joins and Display Names
Because audit records store the changed_by field as a string (often a UUID or 'system'), the system provides helpers to resolve these identifiers into human-readable display names during API retrieval.
- Regex Guardrails: To prevent join errors when
changed_bycontains non-UUID strings (like "system"), the system uses a regex pattern^[0-9a-fA-F-]{36}$in the SQL join clause src/db/repository/audit-join-helper.ts:13-19. - Standard Projections:
getAuditSelectWithDisplayNameprovides a consistent set of fields for audit logs, including the actor's display name and IDP code src/db/repository/audit-join-helper.ts:27-39.
For details on SQL construction and type safety, see Auditable Joins and Display Name Resolution.
Database Schema for Auditing
When the buildSqlPatchFromMetaDiff function detects an auditable entity, it generates a secondary audit table with a fixed schema.
| Column | Type | Description |
|---|---|---|
id | bigint | Primary key (identity) |
entity_id | bigint | The internal ID of the audited record |
entity_uuid | uuid | The public UUID of the audited record |
action | text | The AuditAction performed |
changed_at | timestamptz | Timestamp of the change |
changed_by | text | UUID of the user or 'system' |
version | integer | The version of the record after the change |
delta | jsonb | The specific field changes |
Audit Table Generation Logic
Code
Sources: src/db/database-patch-to-sql.ts:103-128, src/db/schema-types.ts:34-42, src/db/build-entity-snapshot.ts:28-35
API Exposure
Audit trails are typically exposed via a GET /audit sub-resource on entity routers (e.g., GET /customers/:uuid/audit). The Data Access Layer (DAL) utilizes the audit-join-helper.ts utilities to return a hydrated list of changes, allowing the frontend to display a chronological history of the record.
Sources: src/db/repository/audit-join-helper.ts:21-39