PrimebrickPrimebrick
  • Primebrick.dev
  • GitHub
  • Documentation
  • Services
  • Libraries
  • API Catalog
Resources
  • Landing Page
  • API Catalog
  • GitHub
PrimebrickPrimebrick

© 2026 Primebrick. MIT License.

github
DAL Library
    AI Agent Rules and SkillsAudit and Soft-Delete SubsystemsAudit Port and Delta TrackingAuditable Joins and Display NamesBulk OperationsCI/CD and Release ProcessConnection Pool and Session ConfigurationCore ArchitectureDal GatewayEntity Metadata SystemError HandlingGetting StartedGitFlow and Branching RulesGlossaryKey Design DecisionsOverviewQuery DSL and SQL BuilderRead OperationsRepository: CRUD and FindersStreaming Large Result SetsTest Infrastructure and EntitiesTest Suite CoverageTestingTimeout Management and withClientType Coercion: JS ↔ PostgreSQLWrite OperationsREADME
SDK Library
powered by Zudoku
DAL Library

Audit Port and Delta Tracking

Audit Port and Delta Tracking

Relevant source files

The following files were used as context for generating this wiki page:

  • src/audit/auditable-types.ts
  • src/repository/repository.ts
  • src/types/types.ts

The audit subsystem in @primebrick/dal-pg provides a non-blocking, port-based mechanism for tracking data mutations. It automatically calculates field-level differences (deltas), stamps actor information into auditable columns, and offloads audit log persistence to an external consumer-provided implementation.

Architectural Design

The audit system is designed around the Port and Adapter pattern. The DAL defines the interface (Port), and the consuming application provides the implementation (Adapter).

Key Components

ComponentRole
AuditPortInterface defining the writeAudit method src/types/types.ts:86-88.
AuditActionEnum defining tracked operations: INSERT, UPDATE, SOFT_DELETE, HARD_DELETE, RESTORE src/types/types.ts:104-110.
AuditParamsData envelope containing the entity state, actor, and delta src/types/types.ts:91-101.
LoggerPortInterface for swallowing or reporting errors during fire-and-forget audit writes src/types/types.ts:113-117.

Audit Data Flow

The following diagram illustrates how a write operation triggers the audit process.

Audit Write Pipeline

Code
sequenceDiagram participant R as Repository participant DB as PostgreSQL participant AP as AuditPort (Adapter) participant LP as LoggerPort (Adapter) R->>DB: Execute Write (RETURNING *) DB-->>R: New Record State Note over R: calculateDelta(old, new) R->>AP: writeAudit(params) (Fire-and-Forget) activate AP Note right of R: Repository continues execution... AP-->>R: Promise (handled via .catch) deactivate AP alt Audit Failure AP--xR: Throw Error R->>LP: error("Audit failed", err) end

Sources: src/repository/repository.ts:52-64, src/repository/repository.ts:316-335, src/types/types.ts:86-88

Delta Tracking Logic

The DAL performs field-level diffing to determine exactly what changed during an operation.

calculateDelta

This function compares the oldEntity and newEntity objects. It uses JSON.stringify to perform deep comparisons of values, ensuring that complex types like JSONB objects or arrays are correctly evaluated for changes src/repository/repository.ts:53-64.

calculateDeltaWithForcedFields

In certain scenarios (like soft-deletes), specific fields must be included in the audit log even if they haven't changed (e.g., the primary key or a status field). This helper ensures those fields are present in the delta object src/repository/repository.ts:67-79.

Entity State Comparison

Code
graph TD subgraph "Delta Calculation" A["oldEntity Record"] --> D{"calculateDelta"} B["newEntity Record"] --> D D --> E["Changed Fields Only"] D --> F["Forced Fields (e.g. UUID)"] end E & F --> G["AuditParams.delta"]

Sources: src/repository/repository.ts:53-79

Actor Stamping and Auditable Columns

When an entity is marked as auditable via metadata, the Repository automatically stamps the actor (provided in AuditableWriteOptions) into the corresponding PostgreSQL columns.

Automatic Column Updates

The DAL maps internal AuditableFieldType values to specific columns during write operations:

  • INSERT: Sets created_by, updated_by, and created_at, updated_at src/repository/repository.ts:303-314.
  • UPDATE: Sets updated_by and updated_at src/repository/repository.ts:405-412.
  • SOFT_DELETE: Sets deleted_by and deleted_at src/repository/repository.ts:474-482.

Non-Blocking Execution

Audit writes are "fire-and-forget". The Repository does not await the AuditPort.writeAudit call. Instead, it attaches a .catch() block that uses the LoggerPort to log any failures without interrupting the primary database transaction or returning an error to the caller src/repository/repository.ts:316-335.

Implementation Details

The AuditPort Interface

Consumers must implement this interface to bridge the DAL to their audit logging service (e.g., an audit_logs table or an external event bus).

FieldTypeDescription
entityClassNamestringThe TS class name (e.g., "UserEntity")
tableNamestringThe physical DB table name
entityIdnumberThe integer primary key of the record
entityUuidstringThe UUID of the record
actionAuditActionThe operation type (INSERT, UPDATE, etc.)
deltaRecord<string, {old, new}>The map of changed fields

Sources: src/types/types.ts:91-101

Error Swallowing with LoggerPort

If an AuditPort is provided but no LoggerPort is injected, the DAL uses a noopLogger which silently swallows errors src/repository/repository.ts:45-50. This prevents audit infrastructure issues from causing cascading failures in the primary data access layer.

Audit/Logger Association

Code
classDiagram class Repository { -db: Queryable +add(entity, data, options) +update(entity, updates, options) -calculateDelta(old, new) } class AuditPort { <<interface>> +writeAudit(params: AuditParams) } class LoggerPort { <<interface>> +error(message, args) } class AuditParams { +action: AuditAction +delta: Record +changedBy: string } Repository ..> AuditPort : "calls (non-blocking)" Repository ..> LoggerPort : "logs errors to" AuditPort ..> AuditParams : "receives"

Sources: src/repository/repository.ts:148-150, src/types/types.ts:86-117


Last modified on July 13, 2026
Audit and Soft-Delete SubsystemsAuditable Joins and Display Names
On this page
  • Architectural Design
    • Key Components
    • Audit Data Flow
  • Delta Tracking Logic
    • calculateDelta
    • calculateDeltaWithForcedFields
  • Actor Stamping and Auditable Columns
    • Automatic Column Updates
    • Non-Blocking Execution
  • Implementation Details
    • The AuditPort Interface
    • Error Swallowing with LoggerPort