DEV Community

Gathmo
Gathmo

Posted on

A moderation architecture that never blocks user uploads

User-generated media systems often make one state do too much. An item is either “uploaded” or “not uploaded,” while storage, processing, moderation, and public visibility are treated as implementation details.

That model breaks down during live events. A guest needs immediate confirmation that a contribution arrived, but an organizer may require review before the item reaches a shared album or display. Moderation can slow down without turning successful uploads into apparent failures.

The solution is to separate ingestion from publication.

Use an append-first ingestion path

The synchronous request should do only the work needed to accept the contribution safely:

  1. authenticate or validate the temporary contributor session;
  2. confirm the uploaded object and basic policy constraints;
  3. create a canonical media record;
  4. assign an initial moderation state; and
  5. enqueue asynchronous processing.

The response can then return a durable identifier and a truthful state such as received or pending_review.

type MediaRecord = {
  id: string;
  eventId: string;
  objectKey: string;
  ingestionState: "received" | "processing" | "ready" | "failed";
  moderationState: "pending" | "approved" | "hidden" | "removed";
  visibilityState: "private" | "album" | "live_wall";
};
Enter fullscreen mode Exit fullscreen mode

These dimensions should not be collapsed into one enum. A file can be technically ready, pending moderation, and private at the same time.

Keep the live wall out of the write path

The display should query or subscribe only to an approved projection. It should never consume the raw ingestion queue.

guest upload -> canonical media record -> processing queue
                                      -> moderation queue

approved projection -> album API
                    -> live wall feed
Enter fullscreen mode Exit fullscreen mode

If moderation falls behind, the display can continue rotating previously approved media. Guests can continue uploading, and moderators can catch up without back-pressure reaching the upload endpoint.

Make moderation transitions explicit

Each action should be a named state transition with an actor and timestamp. Useful events include:

  • media_received;
  • processing_completed;
  • moderation_approved;
  • moderation_hidden;
  • visibility_added_to_wall; and
  • removal_requested.

An audit trail makes concurrent moderator behavior understandable and supports undo. “Hidden” should usually be reversible; deletion belongs to a separate workflow with its own confirmation and retention rules.

Design the moderator queue for decisions, not browsing

A good queue puts decision context next to the media:

  • event and contribution time;
  • media type and duration;
  • processing or safety warnings;
  • current album and live-wall visibility;
  • previous moderator actions; and
  • keyboard-accessible approve and hide controls.

Newest-first ordering is not always best. During a live event, the queue may prioritize items waiting longest, processing warnings, or media requested by the display operator. The ordering rule should be visible so moderators understand why an item appears next.

Prevent duplicate side effects

Approval may trigger notifications, search indexing, derivative generation, or display updates. Moderation commands therefore need the same idempotency discipline as payment or upload confirmation endpoints.

Use a command ID or state-version precondition:

await approveMedia({
  mediaId,
  expectedVersion: 7,
  commandId: "cmd_01J...",
});
Enter fullscreen mode Exit fullscreen mode

If two moderators act on the same item, one transition succeeds and the other receives the current state instead of replaying every downstream effect.

Publish from a stable read model

Album and display clients should read a projection optimized for visibility, not join raw operational tables on every refresh. The projection can contain the approved derivative URL, aspect ratio, caption, approval time, and event sequence number.

A monotonically increasing event sequence helps clients resume after disconnection without reloading the entire album. It also supports deterministic live-wall rotation when network conditions are poor.

Communicate the right success state to contributors

The contributor interface should say “Your upload was received” when the canonical record exists. If review is enabled, it can add “It will appear after host approval.”

Do not keep a progress bar at 99% while a human moderator is deciding. Technical completion and editorial visibility operate on different clocks.

This separation is central to the moderated event workflow described for Gathmo corporate events: guest contribution can remain fast even when publication is deliberately controlled.

Failure drills worth running

Test the architecture under operational pressure:

  1. Pause all moderation workers while uploads continue.
  2. Disconnect and reconnect the live wall.
  3. Approve the same item concurrently from two moderator sessions.
  4. Fail derivative generation after the canonical record is created.
  5. Hide an item that is currently visible on multiple displays.
  6. Restore a moderator session with stale queue data.

The goal is not instant publication at any cost. It is independent, observable progress: ingestion continues, moderation remains auditable, and every public surface shows only the state it is allowed to consume.

Top comments (0)