Core Concepts
Vix Realtime is built around a small set of concepts: rooms, commands, events, authoritative state, sessions, persistence, and transport.
The central rule is:
command
|
v
handler
|
v
event
|
v
persistence
|
v
stateClients express intent through commands. Commands do not modify room state directly. A room handler decides whether a command is accepted and which authoritative events it produces. Those events are persisted before they are applied to the room state.
Rooms
A room is an isolated unit of authoritative state and command processing.
Examples of rooms could include:
game/match-42
document/891
chat/general
dashboard/team-aEach room has its own:
RoomId- room type
RoomStateRoomHandler- event stream
- room version
- members
- command queue
Rooms of the same type can share the same application logic while maintaining independent state.
Room type: game
|
+---- game/match-1
|
+---- game/match-2
|
+---- game/match-3Each room owns its own state and event history.
Authoritative state
RoomState represents the authoritative application state of a room.
Applications implement the interface:
class MyState : public vix::realtime::RoomState
{
// ...
};A state implementation defines four important operations:
schema_version()
apply(...)
serialize()
restore(...)and provides an independent copy through:
clone()The most important operation is apply():
void apply(
const vix::realtime::RoomEvent &event) override;Only authoritative room events should mutate the state.
A command handler must not directly change the state.
Deterministic state transitions
RoomState::apply() must be deterministic.
Given the same initial state and the same ordered event stream:
initial state
+
event 1
+
event 2
+
event 3the application must always produce the same final state.
apply() should therefore not perform operations such as:
- network requests
- database writes
- broadcasting
- external service calls
- other side effects
This property allows the runtime to reconstruct room state by replaying persisted events.
Commands
A RoomCommand represents a client intention.
For example:
message.send
player.move
document.rename
counter.incrementA command identifies:
- the target room
- the submitting session
- an application-defined command type
- an optional payload
- an optional request identifier
- an optional correlation identifier
- an optional expected room version
- metadata
For example:
vix::realtime::RoomCommand command{
roomId,
sessionId,
"counter.increment",
payload,
"request-1"};The command describes what the client wants to happen. It does not describe an authoritative state change.
Expected room version
A command may include an expected room version:
command.set_expected_version(
vix::realtime::RoomVersion{12});This allows the caller to express:
execute this command only if
the room is still at version 12If the current room version differs, the runtime can reject the command instead of executing it against unexpected state.
This provides optimistic concurrency control for workflows that depend on a known state version.
Room handlers
RoomHandler contains application command logic.
Conceptually:
command + current state + room context
|
v
RoomHandler
|
v
CommandResultA handler can:
- validate a command
- inspect current state
- reject invalid operations
- ignore an operation
- produce one or more authoritative events
The handler decides what should happen. The runtime remains responsible for persistence, event application, and delivery.
Command results
A handler returns a CommandResult.
There are three possible statuses:
Accepted
Rejected
IgnoredAccepted
An accepted command may produce zero or more events:
return vix::realtime::CommandResult::accepted(event);or:
return vix::realtime::CommandResult::accepted(events);Only events from an accepted result are persisted and applied by the room runtime.
Rejected
A rejected command represents a deterministic application rejection:
return vix::realtime::CommandResult::rejected(
vix::realtime::ErrorCode::InvalidCommand,
"amount must be positive");A normal application rejection does not represent a room runtime failure.
Ignored
A command can also be intentionally ignored:
return vix::realtime::CommandResult::ignored();Ignored commands produce no events and do not modify room state.
Events
A RoomEvent represents an authoritative fact that occurred in a room.
Commands usually use intention-oriented names:
message.send
player.move
counter.incrementEvents usually describe completed facts:
message.sent
player.moved
counter.changedThe distinction is important:
Command
"move the player"
|
v
application decision
|
v
Event
"the player moved"Once accepted and persisted, the event becomes part of the room's authoritative history.
Event persistence and state application
For an accepted command that produces events, the normal order is:
1. handle command
2. prepare events
3. persist events
4. apply events to RoomState
5. deliver eventsPersistence happens before state application.
This means the runtime does not first modify the authoritative state and then attempt to record what happened afterward.
Event IDs and room versions
Persisted room events have two different positions.
Event ID
EventId identifies the ordered position of an event in the room's persisted event stream.
Conceptually:
EventId 1
EventId 2
EventId 3
EventId 4Room version
RoomVersion represents the logical version of the authoritative room state.
An event records the room version produced after that event is applied.
These concepts are related but have different responsibilities:
EventId
|
+---- position in persisted history
RoomVersion
|
+---- logical version of room stateApplications should not treat them as interchangeable identifiers.
Event audiences
An event also defines which sessions may receive it.
Realtime supports five audiences:
| Audience | Delivery |
|---|---|
Room | All authorized sessions in the room |
Sender | Only the session that submitted the command |
Others | All authorized room sessions except the sender |
Session | One explicitly targeted session |
Internal | No connected client |
For example:
vix::realtime::RoomEvent event{
roomId,
"message.sent",
payload,
vix::realtime::EventAudience::Room};An internal event may still be persisted and applied to room state:
EventAudience::Internal
|
+---- persisted
|
+---- applied to state
|
+---- not delivered to clientsAudience controls delivery, not whether the event is authoritative.
Sessions
A Session represents one logical client.
It contains information such as:
SessionId- application identity
- room memberships
- resume information
- per-room event positions
- metadata
- the currently attached connection
A session has three lifecycle states:
Connected
Detached
ClosedA connected session owns an active connection.
A detached session has no active connection but may still be resumable.
A closed session is permanently closed.
Connections
A Connection represents an active transport connection.
A session and a connection are deliberately separate concepts:
Session
|
+---- Connection A
network disconnects
Session
|
+---- no connection
client resumes
Session
|
+---- Connection BThe logical session can therefore survive the replacement of its network connection.
This separation is the basis of session resumption.
Room membership
A session may join one or more rooms.
Session A
|
+---- Room 1
+---- Room 2
+---- Room 3Membership determines which rooms the logical session participates in.
The runtime coordinates membership between the session and the room so the two sides remain consistent.
A connection itself is not the room member. The logical session is.
Presence
Presence describes the current participation of a logical session inside a room.
Presence has three states:
Present
Detached
LeftPresent means the logical session is currently present.
Detached means the session remains logically present but has lost its active connection.
Left means the session permanently left the room.
This allows temporary network loss to be represented without immediately treating the user as having left the application state.
Presence is not authoritative room state
Presence and RoomState serve different purposes.
RoomState
|
+---- authoritative application state
+---- reconstructed from persisted events
Presence
|
+---- ephemeral participation state
+---- connection and activity informationPresence metadata must not be used as authoritative room state.
If a value must survive recovery and determine application behavior, it normally belongs in the room's event-driven state model instead.
Event stores
EventStore persists authoritative room events.
The room's event stream allows the runtime to reconstruct state:
initial state
|
v
event 1
|
v
event 2
|
v
event 3
|
v
current stateVix Realtime provides in-memory and PostgreSQL-backed event stores.
The event store is the durable history when persistent storage is used.
Snapshots
Replaying an entire event stream can become expensive as a room accumulates history.
A snapshot stores a serialized representation of the room state at a known point:
event 1
event 2
event 3
event 4
event 5
|
+---- snapshot
|
+---- state at this position
event 6
event 7Recovery can then begin from the snapshot and replay only the later events:
snapshot
+
event 6
+
event 7
|
v
current stateSnapshots are optional.
They improve recovery efficiency but do not replace the event-driven state model.
Replay
Replay applies an ordered sequence of persisted events to reconstruct state or recover missed updates.
For room restoration:
stored snapshot, if available
|
v
events after snapshot
|
v
restored RoomStateFor a resumed session, replay can also be used to recover room updates the session did not observe while disconnected.
Replay depends on ordered, valid event history and deterministic state application.
Session resumption
Because sessions are independent from connections, a detached session can potentially resume using a new connection.
Conceptually:
Connected
|
connection lost
|
v
Detached
|
resume
|
v
recover missed room data
|
v
attach new connection
|
v
ConnectedThe session keeps its logical identity and room memberships across the temporary disconnect.
Detailed resume rules, limits, tokens, replay behavior, and snapshot fallback are covered in Session Resume.
Transport and protocol
Realtime does not require application room logic to depend on a particular network transport.
The relationship is:
Application logic
|
v
Realtime runtime
|
v
Protocol envelope
|
v
TransportA transport is responsible for moving protocol messages.
Room handlers remain concerned with application commands and events rather than socket management.
Vix provides optional WebSocket integration, but WebSocket is not the authoritative Realtime state model itself.
The complete mental model
The main concepts fit together as follows:
Client
|
v
Connection
|
v
Session
|
v
Room membership
|
v
RoomCommand
|
v
RoomHandler
|
v
CommandResult
|
v
RoomEvent
|
+---- EventStore
|
v
RoomState
|
+---- optional Snapshot
|
v
Event delivery
|
v
Session
|
v
Connection
|
v
ClientThe key invariant is simple:
Commands express intent. Persisted events define authoritative changes. Room state is derived by applying those events in order.
Continue with Architecture for how the runtime components implement this model.