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:
- authenticate or validate the temporary contributor session;
- confirm the uploaded object and basic policy constraints;
- create a canonical media record;
- assign an initial moderation state; and
- 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";
};
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
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...",
});
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:
- Pause all moderation workers while uploads continue.
- Disconnect and reconnect the live wall.
- Approve the same item concurrently from two moderator sessions.
- Fail derivative generation after the canonical record is created.
- Hide an item that is currently visible on multiple displays.
- 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)