A small event system is mostly a study in boundaries. The interesting part is not the queue itself, but deciding where work enters, where it waits, and what must remain visible when something fails.

This example uses a browser, a single edge endpoint, and a worker that appends events to a durable log. It is intentionally less ambitious than a full message broker. For a technical journal, the useful question is often: what is the smallest shape that keeps the important guarantees visible?

Start with the contract

The producer sends an event with an identifier, a name, and a payload. The consumer may process the event more than once, so handlers must be idempotent. That is a more useful first constraint than promising exactly-once delivery.

The wire format can stay plain:

type Event struct {
    ID      string          `json:"id"`
    Name    string          `json:"name"`
    Payload json.RawMessage `json:"payload"`
}

The ID gives the consumer a stable key for deduplication. The payload stays opaque at the transport boundary, which means the event service does not need to know every domain type it carries.

For the first version, the useful invariants are small:

  • accept only authenticated writes;
  • append before acknowledging the request;
  • make retries safe with the event identifier;
  • expose enough timing information to find a slow stage.

A queue is not a guarantee. It is a place to make waiting explicit.

That distinction matters when the downstream service is unavailable. The request can be accepted into the log, but the event is not complete until a consumer has recorded a successful attempt. The difference should be visible in both the data model and the operational notes.

Keep the path legible

The architecture is deliberately linear. The edge function authenticates and normalizes the request; the log provides a durable hand-off; the worker owns retries and calls the downstream service.

The same idea is shown as a local SVG so the post also demonstrates how a page bundle can carry a diagram alongside its Markdown:

Architecture of the small event system

The SVG is an ordinary page resource. Hugo resolves the relative image path from the bundle, so it can remain next to this post in Git. That makes it easy to revise a diagram without creating a separate asset pipeline.

Make failure observable

Retries should be boring. A worker can use an exponential backoff, cap the number of attempts, and move exhausted events to a dead-letter stream. The important part is that each transition is recorded with the same event ID.

In practice, a log line with event_id, attempt, status, and elapsed_ms is often enough to explain the first incident. More elaborate tracing can be added later if the boundary between services remains clear.

This site itself follows the same small-loop approach. Posts live in Hugo page bundles, the theme stays as an upstream PaperMod submodule, and the generated HTML is the only deployment artifact.