Overview
The current domain event architecture emits events from SurrealStore (data layer) without any client identification, causing feedback loops when Tauri receives its own events back during optimistic UI updates. This needs to be rearchitected so events are emitted at the NodeService layer with source_client_id awareness.
Problem Statement
Current Architecture (Broken):
- Frontend performs optimistic UI update
- Tauri command calls
NodeService → SurrealStore
SurrealStore.emit_event() broadcasts DomainEvent (no client_id)
DomainEventForwarder receives event and forwards to Tauri frontend
- Frontend receives its own change back → state corruption/race conditions
Evidence:
Root Cause:
emit_event() is called directly in SurrealStore (data layer) with no client context
- No mechanism to pass
client_id through the call chain
DomainEventForwarder forwards ALL events blindly
Proposed Solution
Move event emission to the NodeService layer with client_id awareness:
Target Architecture:
- Each client (Tauri, MCP, proxy, cloud sync) has a unique
client_id
NodeService methods accept optional client_id parameter (or use context)
- Events are emitted at
NodeService level with source_client_id
DomainEventForwarder filters events matching subscriber's client_id
Key Design Decision:
NodeService should be the shared service layer for ALL clients
- Data layer (
SurrealStore) remains unaware of clients
- All entry points (Tauri commands, MCP handlers, HTTP proxy) go through
NodeService
Implementation Approach
Phase 1: Refactor Event Emission Location
- Remove
emit_event() calls from SurrealStore
- Add event emission to
NodeService after successful operations
- Keep
SurrealStore.event_tx broadcast channel for now (or move to NodeService)
Phase 2: Add Client Context
- Create
ClientContext type with client_id: String
- Add
with_client() or similar method to NodeService for setting context
- Update
DomainEvent struct to include source_client_id: Option<String>
Phase 3: Update Event Consumers
- Update
DomainEventForwarder to accept Tauri's client_id
- Filter events where
source_client_id == subscriber_client_id
- Re-enable
DomainEventForwarder in db.rs
Phase 4: Fix Layering Violations
- Audit Tauri commands for direct
SurrealStore calls
- Route all operations through
NodeService
get_all_edges in nodes.rs:890 is only current violation (read-only, low priority)
Acceptance Criteria
Technical Specifications
Reference Files
- SurrealStore:
packages/core/src/db/surreal_store.rs - Current event emission location
- DomainEvent:
packages/core/src/db/events.rs - Event types (needs source_client_id)
- NodeService:
packages/core/src/services/node_service.rs - Target event emission location
- DomainEventForwarder:
packages/desktop-app/src-tauri/src/services/domain_event_forwarder.rs - Needs filtering
- db.rs:
packages/desktop-app/src-tauri/src/commands/db.rs - DomainEventForwarder currently disabled
- dev-proxy.rs:
packages/desktop-app/src-tauri/src/bin/dev-proxy.rs - Working client_id example
Current emit_event Locations in SurrealStore
Lines: 975, 1206, 1207, 1443, 1615, 1722, 1753, 1840, 2943, 2948, 3010, 3031
Browser Mode Pattern (Reference)
// dev-proxy.rs has working client_id filtering
pub enum SseEvent {
NodeCreated {
node_id: String,
node_data: Node,
client_id: Option<String>, // ← This is what we need
},
// ...
}
// Filter logic
if let (Some(this_client), Some(event_client)) = (&client_id, event_client_id) {
if this_client == event_client {
return None; // Filter out own events
}
}
Dependencies
Related Issues
Non-Goals
- Changing the broadcast channel mechanism (tokio broadcast is fine)
- Adding client_id to every database operation signature
- Real-time collaborative editing (future scope)
Overview
The current domain event architecture emits events from
SurrealStore(data layer) without any client identification, causing feedback loops when Tauri receives its own events back during optimistic UI updates. This needs to be rearchitected so events are emitted at theNodeServicelayer withsource_client_idawareness.Problem Statement
Current Architecture (Broken):
NodeService→SurrealStoreSurrealStore.emit_event()broadcastsDomainEvent(no client_id)DomainEventForwarderreceives event and forwards to Tauri frontendEvidence:
dev-proxy.rs) has workingclient_idfiltering, Tauri mode does notRoot Cause:
emit_event()is called directly inSurrealStore(data layer) with no client contextclient_idthrough the call chainDomainEventForwarderforwards ALL events blindlyProposed Solution
Move event emission to the
NodeServicelayer with client_id awareness:Target Architecture:
client_idNodeServicemethods accept optionalclient_idparameter (or use context)NodeServicelevel withsource_client_idDomainEventForwarderfilters events matching subscriber'sclient_idKey Design Decision:
NodeServiceshould be the shared service layer for ALL clientsSurrealStore) remains unaware of clientsNodeServiceImplementation Approach
Phase 1: Refactor Event Emission Location
emit_event()calls fromSurrealStoreNodeServiceafter successful operationsSurrealStore.event_txbroadcast channel for now (or move to NodeService)Phase 2: Add Client Context
ClientContexttype withclient_id: Stringwith_client()or similar method toNodeServicefor setting contextDomainEventstruct to includesource_client_id: Option<String>Phase 3: Update Event Consumers
DomainEventForwarderto accept Tauri'sclient_idsource_client_id == subscriber_client_idDomainEventForwarderindb.rsPhase 4: Fix Layering Violations
SurrealStorecallsNodeServiceget_all_edgesinnodes.rs:890is only current violation (read-only, low priority)Acceptance Criteria
NodeServicelayer, notSurrealStoreDomainEventincludessource_client_id: Option<String>dev-proxy.rs) continues to workbun run quality:fixbun run test:all)Technical Specifications
Reference Files
packages/core/src/db/surreal_store.rs- Current event emission locationpackages/core/src/db/events.rs- Event types (needssource_client_id)packages/core/src/services/node_service.rs- Target event emission locationpackages/desktop-app/src-tauri/src/services/domain_event_forwarder.rs- Needs filteringpackages/desktop-app/src-tauri/src/commands/db.rs- DomainEventForwarder currently disabledpackages/desktop-app/src-tauri/src/bin/dev-proxy.rs- Working client_id exampleCurrent emit_event Locations in SurrealStore
Lines: 975, 1206, 1207, 1443, 1615, 1722, 1753, 1840, 2943, 2948, 3010, 3031
Browser Mode Pattern (Reference)
Dependencies
Related Issues
Non-Goals