Project Structure

Understanding the monorepo structure and organization.

Learn how the MVS Telecom Operator Console codebase is organized and where to find things.

Monorepo Overview

This project uses Turborepo to manage a monorepo with multiple apps and packages.

project-root/
├── apps/                       # Applications
│   ├── web/                    # Main Next.js operator console
│   └── e2e/                    # Playwright E2E tests
├── packages/                   # Shared packages
│   ├── ui/                     # UI components
│   ├── database/               # Prisma schema, client & migrations
│   ├── billing-usage/          # Pure billing/usage rating engine
│   ├── provisioning-workflows/ # Saga FSM, services, crons
│   ├── vendor-core/            # Carrier registry + neutral adapter
│   ├── vendor-twilio/          # Twilio carrier adapter
│   ├── vendor-voipms/          # VoIP.ms carrier adapter
│   ├── vendor-yeastar/         # Yeastar/YCM Cloud PBX adapter
│   └── vendor-yealink/         # Yealink YMCS + RPS adapter
├── tooling/                    # Development tools
└── docs/                       # Repo/contributor documentation

Main Application (apps/web)

The primary Next.js 16 (App Router + RSC) application:

apps/web/
├── app/                    # Next.js App Router
│   └── [locale]/           # Locale-prefixed routes (see below)
├── content/                # In-app product docs & help content
├── config/                 # Configuration files
├── lib/                    # Utility functions (incl. lib/digest, lib/copilot)
└── public/                 # Static assets

Route Structure

All routes live under app/[locale]/, organized into route groups:

app/[locale]/
├── (internal)/             # Authenticated operator console (auth-gated)
├── (public)/               # Public marketing pages
├── (invitation)/           # Invitation acceptance flow
├── auth/                   # Sign-in / sign-up / password reset
├── admin/                  # Super-admin pages
└── help/                   # In-app documentation / help

The (internal)/ layout enforces auth (redirects to sign-in if no session) and handles organization selection. All new authenticated pages go here.

Internal Console Routes ((internal))

The authenticated operator console renders inside the navy "Telecom Console" shell (fixed navy rail + frosted topbar + ⌘K palette; _config/nav-items.ts is the single source for both the sidebar and the palette):

app/[locale]/(internal)/
├── dashboard/              # Mission Control overview + AI MorningBrief card
├── customers/             # Customer list; [id] = Customer 360
├── tenants/[id]/          # Tenant detail (CDR / recordings / config tabs)
├── onboarding/            # Onboarding saga FSM pipeline; [jobId] = detail
├── tasks/                 # Human-task queue + port-doc upload; [taskId]
├── billing/               # Operator billing: plans, MRR, invoices, KPIs
├── operations/            # Read-only exceptions + health KPI console
├── audit/                 # Append-only audit-event log (filterable)
├── carriers/              # Carrier cards + org routing policies
├── numbers/               # Phone-number inventory + buy / port-in
├── inventory/             # DID + device pool
├── devices/               # Device fleet (RPS, firmware, keypad lock, online)
├── sync/                  # Config-sync / drift
└── settings/              # Org / account settings

Auth Routes (auth)

app/[locale]/auth/
├── sign-in/
├── sign-up/
├── password-reset/
└── verify/

Packages Structure

Vendor & Carrier Packages

The carrier abstraction lives in vendor-core, with one package per vendor:

packages/
├── vendor-core/            # CarrierRegistry + neutral CarrierAdapter interface
├── vendor-twilio/          # Twilio: DID, toll-free, port-in, E911, SMS
├── vendor-voipms/          # VoIP.ms: advertises 'did' only (port-in/E911 deferred)
├── vendor-yeastar/         # Yeastar/YCM Cloud PBX (PCE) provisioning + passthrough
└── vendor-yealink/         # Yealink YMCS (config/BLF/firmware) + RPS zero-touch

buildCarrierRegistry() registers Twilio + VoIP.ms only — there is no Sinch adapter in code. Routing applies only to switchable number ops; MVS's own Twilio SIP-trunk / IP-ACL / PBX plumbing stays bound to the Twilio dep.

Provisioning Workflows (@kit/provisioning-workflows)

The orchestration + I/O layer:

packages/provisioning-workflows/
├── functions/              # Inngest functions, saga stages, crons, lib/deps.ts
└── services/               # billing.service, carrier-routing.service, etc.

Crons (registered in functions/index.ts, mounted at /api/inngest): device-checkin, provisioning-resync, reconcile-ports, provisioning-config-sync, collect-usage (did_count meter only). Saga stages and job-resume are event-driven, not crons.

Billing & Usage (@kit/billing-usage)

Pure rating engine — no I/O:

packages/billing-usage/
└── src/
    ├── meters.ts           # BillingMeter discriminated union (4 kinds)
    └── pricing.ts          # rateUsage, projectCost, MILLICENTS_PER_CENT

Money units are load-bearing: per-unit rates are integer millicents (1¢ = 1000 millicents); money (base/line/invoice amounts, MRR) is integer cents. They are never mixed; per-line rounding is half-up.

UI Package (@kit/ui)

Shared UI components (Base UI + Tailwind 4 + Lucide):

packages/ui/
└── src/
    ├── components/         # UI components
    └── ...

Database Package (@kit/database)

Prisma 7 schema, client, and migrations (38 models, 18 migrations):

packages/database/
├── prisma/
│   ├── schema.prisma       # 38 models (auth, provisioning, carrier, billing)
│   └── migrations/         # 18 migrations (applied to prod Neon manually)
└── src/                    # Database client

Migrations are applied to prod Neon manually — Vercel does not auto-run prisma migrate deploy. Status-like columns are CHECK-constrained Strings, not native enums, across the provisioning / carrier / billing domains.

Configuration Files

Root Level

project-root/
├── package.json            # Root package.json
├── turbo.json              # Turborepo config
├── pnpm-workspace.yaml      # PNPM workspace
└── tsconfig.json           # Base TypeScript config

Application Level

apps/web/
├── next.config.js          # Next.js configuration
├── tsconfig.json           # TypeScript config
└── .env.local              # Environment variables

Naming Conventions

Files

  • Pages: page.tsx (Next.js convention)
  • Layouts: layout.tsx
  • Loaders: {feature}-page.loader.ts
  • Server Actions: {feature}-server-actions.ts
  • Schemas: {feature}.schema.ts
  • Components / utilities: kebab-case.tsx / kebab-case.ts

Directories

  • Route segments: [param] for dynamic
  • Route groups: (group) for organization
  • Private folders: _components, _lib, _config
  • Parallel routes: @folder

Code Organization

Each internal feature collocates its page, loader, actions, schema, and private folders:

(internal)/billing/
├── page.tsx                    # Route page
├── billing-page.loader.ts      # RSC data loader
├── billing-server-actions.ts   # next-safe-action server actions
├── billing.schema.ts           # Validation schema
├── _components/                # Private components
└── _lib/                       # Private utilities

Import Paths

Use TypeScript path aliases:

// Absolute imports from packages
import { Button } from '@kit/ui/button';
import { rateUsage } from '@kit/billing-usage';

// Relative imports within a feature
import { loadBilling } from './billing-page.loader';
import { CustomerBillingTable } from './_components/customer-billing-table';

Best Practices

  1. Keep route-specific code private - Use _components, _lib, _config
  2. Share reusable code - Extract to packages
  3. Collocate related files - Page, loader, actions, and schema live together
  4. Use consistent naming - {feature}-page.loader.ts, {feature}-server-actions.ts
  5. Organize by feature - Not by file type

Finding Your Way

Looking for...Location
UI Componentspackages/ui/src/components/
Prisma Schemapackages/database/prisma/schema.prisma
Migrationspackages/database/prisma/migrations/
API Routesapps/web/app/api/
Internal Console Pagesapps/web/app/[locale]/(internal)/{feature}/
Page Loaders{feature}-page.loader.ts
Server Actions{feature}-server-actions.ts
Carrier Adapterspackages/vendor-*/
Billing Enginepackages/billing-usage/src/
Provisioning Servicespackages/provisioning-workflows/services/
Sidebar / ⌘K Navapps/web/app/[locale]/(internal)/_config/nav-items.ts