Course contentsShow
← C#
Lesson 1587 of 1,90814. Background Work and MessagingFree lesson

Audit Logging for Background Work

Maintain compliant, immutable audit trails for all asynchronous state changes.

In this lesson, you will learn how to maintain non-repudiable, immutable audit trails for asynchronous state transitions triggered by background workers.

When background processes modify data—especially in distributed systems—standard application logs are often insufficient because they lack transactional context. To maintain a compliant audit trail, you must treat audit logs as an immutable append-only ledger that exists alongside your data modifications.

The Immutable Audit Pattern

The core challenge is ensuring that the "intent," the "actor," and the "resulting state" are captured atomically. If a background worker updates a tenant's billing status, the audit record must be committed within the same database transaction as the update itself. If the background process fails, the audit record rolls back; if it succeeds, the record is permanently etched into the ledger.

Consider this implementation pattern within your background worker’s handler:

public async Task ProcessBillingUpdateAsync(Guid tenantId, decimal amount)
{
    using var transaction = await _dbContext.Database.BeginTransactionAsync();
    
    // 1. Perform the state change
    var tenant = await _dbContext.Tenants.FindAsync(tenantId);
    tenant.Balance += amount;

    // 2. Append the audit entry (Immutable record)
    var audit = new AuditLogEntry {
        EntityId = tenantId,
        Action = "BILLING_UPDATE",
        Payload = JsonSerializer.Serialize(new { amount, timestamp = DateTime.UtcNow }),
        Actor = "BackgroundWorker_BillingService",
        CorrelationId = _correlationId // Critical for tracing
    };

    _dbContext.AuditLogs.Add(audit);
    await _dbContext.SaveChangesAsync();
    await transaction.CommitAsync();
}

Why Atomicity Matters

By embedding the audit log into the transaction, you prevent "ghost changes"—scenarios where the system state changes, but the audit trail fails to materialize. Because background workers often process thousands of events, using a dedicated AuditLog table with a strictly append-only schema (no UPDATE or DELETE permissions) ensures compliance for regulatory environments.

When designing these systems, avoid writing logs to external sinks (like standard logging frameworks) as the primary source of truth, as these can be lost during network partitions or message replay events. Always prioritize the database-backed ledger to ensure that every background state change is accounted for, regardless of infrastructure hiccups.

Key Takeaway: Never decouple the audit trail from the state change; by enforcing atomicity within the database transaction, you guarantee that every background operation is traceable, immutable, and compliant.