# Architecture

## Multi-tenant model

```
Company
  └── Outlet (restaurant / hotel / resort / cafe / qsr / other)
        └── Users (via user_outlets, many-to-many)
              └── POS Orders / KOT / Payments
              └── Hotel Reservations / Folios
```

Every business-relevant table carries `company_id` and, where applicable,
`outlet_id`. Every protected API request is checked against both before any
role/permission check runs (`backend/src/middleware/authorize.js`) — see
`docs/SECURITY.md` for the exact chain.

## Backend layout

```
backend/src/
  config/       env + Sequelize connection
  models/       one file per domain (core, crm, menu, floor, order, kot,
                inventory, purchase, hotel, ops, online, system) + index.js
                wiring all associations
  middleware/   auth, authorize (company/outlet/permission), validate,
                errorHandler, auditLogger
  controllers/  business logic — bespoke for POS/payments/KOT/kitchen/hotel/
                inventory/purchase/reports; generic CRUD (utils/crudFactory.js
                + utils/crudRouter.js) for straightforward reference data
  services/     orderPricingService (server-authoritative pricing),
                inventoryService (recipe-based stock deduction)
  routes/       one file per resource, assembled in routes/index.js
  sockets/      Socket.IO room management (outlet:<id>, kitchen:<id>)
  utils/        money (decimal-safe cents arithmetic), generateNumber,
                apiResponse, logger (Winston), upload (Multer/Sharp)
  seeders/      create-admin CLI
  scripts/      db:setup (imports database/schema.sql + seed.sql)
```

## Frontend layout

```
frontend/src/
  styles/       design-system CSS (variables/reset/global/layout/typography/
                buttons/forms/tables/cards/modal/animations/responsive/utilities)
  context/      AuthContext, OutletContext, ThemeContext, NotificationContext
  services/     one Axios wrapper per API domain
  hooks/        useApiList (paginated/searchable list), useDebounce, useSocket
  components/   common/ (Button, Input, Modal, DataTable, Skeleton wrappers,
                Badge, StatCard, ErrorBoundary, ...), pos/ (PaymentModal)
  layouts/      AdminLayout, Sidebar, Topbar
  pages/        one folder per module (auth, dashboard, pos, tables, kitchen,
                hotel, inventory, purchase, customers, reports, employees,
                settings)
  routes/       ProtectedRoute
  App.jsx       React Router + React.lazy code-split routes
```

## Request lifecycle (protected route)

```
Client (Axios, Bearer token)
  → Helmet / CORS / rate-limit (app.js)
  → authenticate            (JWT verify, loads role+permissions+outlets)
  → requireCompany          (company_id isolation)
  → requireOutletAccess     (outlet_id isolation)
  → auditLogger             (records mutating requests post-response)
  → requirePermission(slug) (per-route)
  → controller               (Sequelize transaction for financial writes)
  → apiResponse helper       ({ success, message, data[, pagination] })
```

## Real-time

Socket.IO rooms: `outlet:<id>` (POS/admin clients) and `kitchen:<id>` (KDS
screens). Order/KOT/payment mutations emit through
`backend/src/sockets/index.js`; the frontend's `useSocket` hook joins the
relevant room and triggers a re-fetch on any relevant event (kept simple —
payload carries only IDs, the UI always re-fetches the authoritative state
rather than trusting the socket payload as ground truth).
