Skip to content
Back to experience
Work · Case study

Zero-loss audit pipeline

How I designed an audit trail that keeps every event, even when the message broker goes down, across four .NET services and the browser.

Role
Designer & sole developer
Context
Production system at work
Scope
4 services + React app

This is proprietary software, so this page covers the architecture and decisions rather than code or screenshots.

01The problem

The platform I work on has a hard requirement: every meaningful change must end up in an audit trail. A missing event isn't cosmetic. The record is incomplete, and in a regulated setting that matters.

The obvious approach, publishing to the message broker right after saving, has a gap. If the broker is down, or the process dies between the database commit and the publish, the event is gone and nobody notices.

Some auditable actions also happen only in the browser, where the network is even less dependable.

02My role

I designed and built the whole subsystem: the outbox in each service, the background flusher, the dedicated audit service and the browser-side queue.

I also run it in production, so I live with its failure modes.

03Architecture

Server

  1. Any servicewrites its data change
  2. Local outboxsame DB transaction
  3. Background flusherretries until delivered
  4. RabbitMQmessage broker
  5. Audit servicedeveloper · activity · data logs

Browser

  1. React appuser actions queued client-side
  2. Central dispatcherbatches and flushes
  3. API → outboxjoins the same pipeline
Transactional outbox on the server, with the same guarantee extended to the browser.

04Key decisions

  • Transactional outbox

    Each service writes its audit event to a local outbox table in the same transaction as the data change. Either both are saved or neither is, so there is no window where data changes without a matching event.

  • Background flusher

    A background worker picks up unsent outbox rows and ships them to the audit service, retrying until they are delivered. A broker outage delays events instead of losing them, and requests never wait on the broker.

  • Audit the browser too

    Some actions only happen in the UI. Frontend events queue client-side and flush through a central dispatcher into the same outbox pipeline, so they get the same guarantee as server-side ones.

  • A dedicated audit service

    Audit lives in its own service. Other services only write to their outbox, while storage, querying and log views stay in one place.

  • One pipeline, three audiences

    Output is split into developer logs, activity logs and data-change logs. Engineers, operators and reviewers each get the view they need from one source of truth.

05Outcome

  • The audit trail survives broker outages and restarts without losing events.
  • Every service and the frontend share one pipeline instead of ad-hoc logging.
  • It has run in production across 20+ client deployments.

06Stack

  • .NET Core
  • ASP.NET Core
  • SQL Server
  • EF Core
  • RabbitMQ
  • Background services
  • React
Next case studyChatNET