Skip to main content
TableOrder follows a feature-based architecture that organizes code by domain rather than by file type. This approach keeps related functionality together, making it easier to understand, maintain, and extend.

Design philosophy

The architecture is built around three core principles:

Domain-driven

Features are grouped by domain (menu, payments, notifications) rather than technical layer

Low coupling

Each module is self-contained with minimal dependencies on other parts of the system

High cohesion

Related functionality lives together, making it easy to locate and modify features

Directory structure

All application code lives in the src/ directory:
src/
├── lib/
│   ├── core/               # App-wide singletons
│   │   ├── types.ts        # TypeScript interfaces (source of truth)
│   │   ├── mockData.ts     # Products + tables dataset
│   │   ├── config.ts       # API credentials from .env
│   │   ├── payments/       # paymentService — mock Stripe flow
│   │   ├── notifications/  # NotificationService — expo-notifications
│   │   └── sound/          # SoundService — expo-av beep
│   ├── modules/
│   │   └── menu/           # useMenuLogic — contextual filtering hook
│   └── services/           # External API integrations
│       ├── mapboxService.ts # Directions API + polyline decode + ETA
│       ├── pdfService.ts    # expo-print branded ticket generator
│       └── telegramService.ts # Bot API PDF upload
├── stores/
│   ├── useTableStore.ts    # Active table session (Zustand)
│   ├── useCartStore.ts     # Cart items, totals, discount, service type (Zustand)
│   └── useLocationStore.ts # App mode, GPS coords, delivery info (Zustand)
└── components/
    ├── scanner/            # CameraScanner with idempotency ref
    ├── location/           # ContextSwitcher — Mapbox map + geofencing logic
    └── ui/                 # ErrorState, BirthdayBanner, ToastMessage
The directory structure reflects the application’s domain model, making it intuitive to navigate even for developers new to the codebase.

Layer breakdown

Core layer (lib/core/)

Contains app-wide singletons and shared utilities that don’t fit into a specific feature domain.
types.ts — The single source of truth for all TypeScript interfaces
src/lib/core/types.ts
export type AppMode = 'CHECKING' | 'SCANNER' | 'DELIVERY';

export interface Coordinates {
  latitude: number;
  longitude: number;
}

export interface DeliveryInfo {
  distanceKm: number;
  etaMinutes: number;
  polyline: string;
  decodedRoute: Coordinates[];
}

Modules layer (lib/modules/)

Feature-specific business logic organized by domain. Each module is self-contained. Example: Menu module The menu/ module contains useMenuLogic.ts, a custom hook that handles:
  • Table context hydration from route parameters
  • Birthday mode activation based on table metadata
  • Product filtering by menu type (FULL vs DRINKS_ONLY)
  • Category-based grouping for section rendering
src/lib/modules/menu/useMenuLogic.ts
const products: Product[] =
  currentTable?.menuType === 'DRINKS_ONLY'
    ? PRODUCTS.filter((p) => p.category === 'DRINK')
    : PRODUCTS;

Services layer (lib/services/)

External API integrations that communicate with third-party services.

Mapbox

Directions API, polyline decoding, ETA calculation

PDF

HTML template rendering and file generation

Telegram

Bot API document upload with multipart/form-data
See Services architecture for detailed implementation patterns.

Stores layer (stores/)

Global state management using Zustand. Three independent stores handle all session state:
  • useTableStore — Active table session and QR scan state
  • useCartStore — Shopping cart, totals, discounts, service type
  • useLocationStore — App mode, GPS coordinates, delivery route
See State management for store patterns and best practices.

Components layer (components/)

React Native UI components organized by feature:
  • scanner/ — QR code scanning with camera and idempotency guards
  • location/ — GPS-based context switching and Mapbox integration
  • ui/ — Reusable UI components (banners, error states, toasts)

Why feature-based?

Compare the two approaches:
lib/services/
├── mapboxService.ts
├── pdfService.ts
└── telegramService.ts
Benefits:
  • All payment logic in one place
  • Easy to add/remove complete features
  • Clear domain boundaries
Adding a new feature like SMS notifications would require touching only one folder: lib/services/smsService.ts. Removing Telegram integration means deleting a single file.

App flow

The application follows a context-driven navigation pattern: Each mode has its own navigation stack, but they share the same cart and checkout flow. State is managed by Zustand stores that persist across mode switches.

Next steps

State management

Learn how Zustand stores manage application state

Services

Explore external API integration patterns