OS Trading Engine
Technical Documentation
Frontend
Feature Organization

Feature Organization

Nexgent uses a feature-based organization pattern where code is grouped by domain feature rather than technical type.

Directory Structure

src/
├── app/                    # Next.js App Router (pages)
├── features/               # Feature modules
├── infrastructure/         # API clients, auth, WebSocket
└── shared/                 # Shared components, hooks, utils

Features Directory

Each feature is a self-contained module with its own components, hooks, types, and exports.

      • index.ts

  • Feature Module Structure

    Every feature follows the same structure:

    features/{feature-name}/
    ├── components/           # Feature-specific components
    │   └── {component-name}/
    │       └── {component-name}.tsx
    ├── hooks/               # React Query hooks, custom hooks
    │   └── use-{resource}.ts
    ├── types/               # TypeScript types
    │   └── {feature}.types.ts
    └── index.ts             # Public exports

    Barrel Exports

    Each feature has an index.ts that exports its public API:

    // features/agents/index.ts
    export { AgentSwitcher } from './components/agent-switcher/agent-switcher';
    export { CreateAgentDialog } from './components/create-agent-dialog/create-agent-dialog';
    export { useAgents, useAgent, useCreateAgent, useUpdateAgent } from './hooks/use-agent';
    export { useAgentTradingConfig } from './hooks/use-agent-trading-config';
    export type { Agent, CreateAgentRequest } from './types/agent.types';

    This allows clean imports:

    import { useAgents, AgentSwitcher, CreateAgentDialog } from '@/features/agents';

    Feature List

    FeatureDescription
    agentsAgent CRUD, trading config, performance
    positionsLive positions table, close dialogs
    tradesHistorical swaps, trade detail
    trading-signalsSignal history and detail
    transactionsTransaction history
    walletsWallet management, deposit/withdraw
    authLogin/register forms
    api-keysAPI key management
    integrationsData source connections
    system-healthSystem health display

    Component Pattern

    Components are organized in folders with their name:

    components/
    └── live-positions-table/
        └── live-positions-table.tsx

    Component Structure

    // features/positions/components/live-positions-table/live-positions-table.tsx
     
    'use client';
     
    import { useWebSocket, type LivePosition } from '@/infrastructure/websocket';
    import { useAgentSelection } from '@/shared/contexts/agent-selection.context';
    import { Table, TableHeader, TableBody, TableRow, TableCell } from '@/shared/components/ui/table';
     
    interface LivePositionsTableProps {
      className?: string;
    }
     
    export function LivePositionsTable({ className }: LivePositionsTableProps) {
      const { selectedAgentId } = useAgentSelection();
      const { positions, isConnected } = useWebSocket(selectedAgentId);
      
      if (!isConnected) {
        return <ConnectionStatus />;
      }
      
      if (positions.length === 0) {
        return <EmptyState />;
      }
      
      return (
        <Table className={className}>
          <TableHeader>
            <TableRow>
              <TableCell>Token</TableCell>
              <TableCell>Amount</TableCell>
              <TableCell>P/L</TableCell>
            </TableRow>
          </TableHeader>
          <TableBody>
            {positions.map(position => (
              <PositionRow key={position.id} position={position} />
            ))}
          </TableBody>
        </Table>
      );
    }

    Hook Pattern

    Feature hooks wrap React Query for data fetching:

    // features/agents/hooks/use-agent.ts
     
    import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
    import { AgentsService } from '@/infrastructure/api/services/agents.service';
     
    const agentsService = new AgentsService();
     
    /**
     * Hook to fetch all agents for a user
     */
    export function useAgents(userId?: string) {
      return useQuery({
        queryKey: ['agents', userId],
        queryFn: () => agentsService.getAgents(userId),
        enabled: !!userId,
        staleTime: 5 * 60 * 1000, // 5 minutes
        refetchOnWindowFocus: false,
      });
    }
     
    /**
     * Hook to create a new agent
     */
    export function useCreateAgent() {
      const queryClient = useQueryClient();
      
      return useMutation({
        mutationFn: (data: CreateAgentRequest) => agentsService.createAgent(data),
        onSuccess: (newAgent) => {
          // Optimistically update cache
          queryClient.setQueryData(['agents', newAgent.userId], (old: Agent[] | undefined) => {
            return old ? [...old, newAgent] : [newAgent];
          });
          // Invalidate to ensure fresh data
          queryClient.invalidateQueries({ queryKey: ['agents'] });
        },
      });
    }

    Hooks encapsulate React Query configuration, cache invalidation, and optimistic updates so components stay simple.


    Infrastructure Layer

    The infrastructure/ folder contains technical concerns:

    infrastructure/
    ├── api/
    │   ├── client/
    │   │   └── api-client.ts       # HTTP client
    │   ├── services/
    │   │   ├── agents.service.ts   # Agent API calls
    │   │   ├── wallets.service.ts  # Wallet API calls
    │   │   └── ...
    │   └── hooks/
    │       └── use-system-health.ts
    ├── auth/
    │   ├── auth-config.ts          # NextAuth config
    │   └── token-utils.ts          # Token handling
    └── websocket/
        ├── hooks/
        │   └── use-websocket.ts    # WebSocket hook
        └── types/
            └── websocket.types.ts

    API Services

    Services encapsulate API calls:

    // infrastructure/api/services/agents.service.ts
     
    import { apiClient } from '../client/api-client';
    import { handleApiError } from '../client/error-handler';
     
    export class AgentsService {
      async getAgents(userId?: string): Promise<Agent[]> {
        const response = await apiClient.get('/api/v1/agents');
        if (!response.ok) {
          throw await handleApiError(response);
        }
        return response.json();
      }
      
      async createAgent(data: CreateAgentRequest): Promise<Agent> {
        const response = await apiClient.post('/api/v1/agents', data);
        if (!response.ok) {
          throw await handleApiError(response);
        }
        return response.json();
      }
    }

    Shared Layer

    The shared/ folder contains reusable code:

    shared/
    ├── components/
    │   ├── ui/               # shadcn/ui components
    │   ├── layout/           # Sidebar, navigation
    │   ├── loading/          # Loading states
    │   └── error/            # Error boundaries
    ├── contexts/
    │   ├── agent-selection.context.tsx
    │   ├── currency.context.tsx
    │   ├── trading-mode.context.tsx
    │   └── providers.tsx
    ├── hooks/
    │   ├── use-auth.ts
    │   └── use-toast.ts
    ├── utils/
    │   ├── formatting.ts
    │   └── error-handling.ts
    └── types/
        └── api.types.ts

    Import Aliases

    Path aliases simplify imports:

    // tsconfig.json
    {
      "compilerOptions": {
        "paths": {
          "@/*": ["./src/*"]
        }
      }
    }
    // Clean imports
    import { Button } from '@/shared/components/ui/button';
    import { useAgents } from '@/features/agents';
    import { apiClient } from '@/infrastructure/api/client/api-client';