Skip to main content

7N7D Realtime Architecture Audit

Last Updated: December 2024
Status: Migrating from Socket.IO to GraphQL Subscriptions

Overview

This document audits the realtime communication architecture in the 7N7D trading platform and tracks the migration from Socket.IO to GraphQL subscriptions via graphql-ws.


Current Socket.IO Implementation

Server-Side Entrypoint

File: apps/api/src/websocket/websocket.gateway.ts

The WebSocket gateway uses NestJS @nestjs/platform-socket.io and broadcasts the following event types:

Event NameDescriptionPayload Type
trade_executedNew trade executed by the agentTrade data with timestamp
decision_madeAgent made a trading decisionDecision data with reasoning
performance_updatePerformance metrics updatedMetrics object
position_updatePosition changedPosition data
balance_updateAccount balance changedBalance snapshot
agent_statusAgent status changed (running/paused/stopped)Status object
agent_messageAgent chat message (Q&A, activities)Message object
long_term_position_openedLong-term position openedPosition data
long_term_position_updatedLong-term position updatedPosition data
long_term_position_closedLong-term position closedPosition data
profit_transfer_executedProfit transferred between accountsTransfer data
strategy_allocation_updatedStrategy allocation changedAllocation snapshot
errorError occurredError message and context
heartbeatConnection keep-alive (every 30s)Timestamp
connection_establishedInitial connection confirmationWelcome message

Client-Side Implementation

Files:

  • apps/app/contexts/WebSocketContext.tsx - Socket.IO client context provider
  • apps/app/hooks/useWebSocketQuerySync.ts - React Query cache invalidation on events
  • apps/app/hooks/useAgentChat.ts - Real-time agent message handling

Features:

  • Connection status tracking (isConnected)
  • Event subscription system via subscribe() function
  • Automatic reconnection with configurable delays
  • React Query cache invalidation on relevant events

Disabled GraphQL Subscription Infrastructure

The codebase has GraphQL subscription infrastructure that was disabled pending graphql-ws integration.

Disabled Code Locations

FileWhat's DisabledReason
apps/api/src/app.module.tsPubSubModule import"PubSubModule disabled until GraphQL subscriptions are implemented"
apps/api/src/trading/trading.module.tsTradingSubscriptionResolver"Disabled - requires graphql-ws package"
apps/api/src/websocket/websocket.gateway.tsPubSubService import"re-enable when GraphQL subscriptions are implemented"

Existing Subscription Resolvers

File: apps/api/src/trading/trading.subscriptions.ts

Already defined (but disabled):

  • tradeExecuted - Trade execution notifications
  • balanceUpdated - Balance change notifications
  • decisionMade - Decision notifications
  • agentStatusChanged - Agent status notifications

PubSub Service

File: apps/api/src/common/pubsub.service.ts

Uses graphql-subscriptions package with in-memory PubSub. Defines event constants:

  • TRADE_EXECUTED
  • BALANCE_UPDATED
  • POSITION_UPDATED
  • DECISION_MADE
  • AGENT_STATUS_CHANGED
  • AGENT_MESSAGE

Migration Decision: GraphQL Subscriptions as Default

Why GraphQL Subscriptions?

  1. Single schema, single mental model - Queries, mutations, and subscriptions share types
  2. Type safety - Codegen generates typed subscription hooks
  3. Consistent auth - Reuses existing GraphQL auth/context logic
  4. Infra simplicity - One gateway (/graphql) over HTTP + WS
  5. Better React Query integration - Direct cache updates from subscriptions

Socket.IO Exception Criteria

Keep Socket.IO only if:

  • Complex room/broadcast patterns not cleanly mapped to GraphQL
  • Binary streaming requirements
  • Very chatty low-level events outside GraphQL schema

Current Exceptions: None identified. All 12+ events map cleanly to GraphQL subscriptions.


Scalability Considerations

Current Implementation (In-Memory PubSub)

The graphql-subscriptions package uses an in-memory PubSub which:

  • ✅ Works perfectly for single API instance
  • ❌ Does NOT work across multiple API instances (horizontal scaling)

Future Scaling (If Needed)

For horizontal scaling, migrate to graphql-redis-subscriptions:

import { RedisPubSub } from 'graphql-redis-subscriptions';
import Redis from 'ioredis';

const options = {
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT || '6379'),
};

export const pubSub = new RedisPubSub({
publisher: new Redis(options),
subscriber: new Redis(options),
});

This is not required for current single-instance deployment.


Migration Progress

Phase 1: Audit Document ✅

  • Document current Socket.IO events
  • Document disabled GraphQL subscription code
  • Document migration decisions

Phase 2: Enable GraphQL Subscriptions in API ✅

  • Install graphql-ws package
  • Configure Apollo Server subscriptions with auth
  • Re-enable PubSubModule and TradingSubscriptionResolver
  • Add missing subscription resolvers (14 subscriptions total)
  • Wire PubSubService alongside WebSocketGateway

Phase 3: Setup GraphQL Subscriptions Client ✅

  • Install graphql-ws in frontend
  • Create subscription client with reconnection (graphql-subscription-client.ts)
  • Create GraphQL Subscription Context (GraphQLSubscriptionContext.tsx)
  • Create React Query integration hooks (useGraphQLSubscriptions.ts)

Phase 4: Migrate Frontend Components ✅

  • Migrate useWebSocketQuerySync → useGraphQLQuerySync
  • Migrate useAgentChat to use GraphQL subscriptions
  • Add GraphQLSubscriptionProvider to app layout

Phase 5: Testing and Validation ✅

  • Add subscription integration tests (25 tests passing)
  • Manual verification of all features (pending)

Phase 6: Cleanup (In Progress)

  • Remove Socket.IO client code (keeping during parallel running period)
  • Update documentation

Event Mapping: Socket.IO → GraphQL Subscriptions

Socket.IO EventGraphQL SubscriptionStatus
trade_executedtradeExecutedExists (disabled)
balance_updatebalanceUpdatedExists (disabled)
decision_madedecisionMadeExists (disabled)
agent_statusagentStatusChangedExists (disabled)
position_updatepositionUpdatedTo be added
performance_updateperformanceUpdatedTo be added
agent_messageagentMessageTo be added
profit_transfer_executedprofitTransferExecutedTo be added
strategy_allocation_updatedstrategyAllocationUpdatedTo be added
long_term_position_openedlongTermPositionOpenedTo be added
long_term_position_updatedlongTermPositionUpdatedTo be added
long_term_position_closedlongTermPositionClosedTo be added
errorerrorOccurredTo be added
heartbeatN/A (graphql-ws has built-in ping/pong)Not needed
connection_establishedN/A (connection event)Not needed