# TorbatYar Frontend — Enterprise Architecture Audit **Audit date:** 2026-07-26 **Scope:** `frontend/` source (excluding `.next`, `node_modules`) **Method:** Read-only static analysis — no source files modified **Stack:** Next.js 15.5.18 · React 19 · TypeScript 5.4 · Tailwind CSS 3.4 --- ## Executive Summary The TorbatYar SuperApp frontend is a **single Next.js monolith** hosting the platform shell plus three mature business modules (Accounting, Beauty Business, Healthcare). The codebase contains **~481 TypeScript/TSX files** and **350 route pages**, organized around a consistent multi-tenant, portal-based architecture. ### Overall readiness for long-term scaling: **Moderate — structurally sound, operationally strained** | Dimension | Rating | Notes | |-----------|--------|-------| | Module isolation | ✅ Strong | No cross-domain imports between accounting/beauty/healthcare | | Routing architecture | ✅ Good | App Router with portal layouts and BFF proxies | | Design system | ⚠️ Mixed | `components/ds/` is mature; parallel `components/ui/` and per-module DS exist | | Route vs component separation | ⚠️ Inconsistent | Beauty/healthcare use thin routes; accounting embeds logic in routes | | Build health | ❌ Blocked | Production build fails TypeScript check (see Build Performance) | | Dependency hygiene | ⚠️ Weak | Unused npm packages; 3 monolithic API clients (1,800+ lines each) | | Observability / resilience | ⚠️ Missing | No `middleware.ts`, no route-level `error.tsx` / `loading.tsx` | --- ## 1. Repository Topology ``` frontend/ ├── app/ # Next.js App Router (350 pages, 20 layouts, 3 API routes) ├── components/ # Shared + domain UI │ ├── ds/ # Primary design system (11 files) │ ├── ui/ # Legacy/marketing UI (6 files) │ ├── providers/ # Query + color mode │ ├── accounting/ # Accounting shell + screen aggregators │ ├── beauty/ # Portal shells, design-system/, pages/ │ ├── healthcare/ # Portal shells, ui/, pages/ │ ├── admin/, auth/, settings/ │ └── *.tsx # Root-level guards, header, theme ├── hooks/ # 10 shared hooks ├── lib/ # 20 files — API clients, nav, auth, theme ├── styles/globals.css # CSS variables, RTL, module accents ├── public/ # Fonts, theme.config.json ├── scripts/ ├── tailwind.config.ts ├── tsconfig.json # paths: @/* → ./* └── next.config.mjs # env defaults, healthcare redirects ``` **Path alias:** Single alias `@/*` maps to project root. No scoped aliases (`@accounting/*`, `@shared/*`). --- ## 2. App Router Structure ### 2.1 Top-level segments | Segment | ~Pages | Layout depth | Auth pattern | |---------|--------|--------------|--------------| | `/` (platform) | 6 | Root only | Mixed | | `/accounting` | ~130 | Root → Accounting | `AuthGuard` + tenant gate in layout | | `/admin` | 9 | Root → Admin | `AdminAuthGuard` (bypass on `/admin/login`) | | `/beauty` | ~100 | Root → pass-through → portal layouts | `createPortalLayout()` per portal | | `/healthcare` | ~100 | Root → pass-through → portal layouts | `createPortalLayout()` per portal | | `/auth`, `/login`, `/register`, `/onboarding`, `/dashboard` | 6 | Root only | Inline guards | ### 2.2 Route groups Only one route group exists: `app/beauty/(owner)/`. Parentheses are invisible in URLs — `/beauty/organizations` resolves through `(owner)/organizations/page.tsx`. **Architecture smell:** Beauty owner functionality is split across two URL trees: - `app/beauty/(owner)/*` — CRUD pages with inline form logic (~20 routes) - `app/beauty/owner/*` — analytics, calendar, integrations via thin re-exports (~6 routes) Both use `createPortalLayout("owner")` but live in separate folder hierarchies. ### 2.3 Dynamic routes (13) | Pattern | Module | |---------|--------| | `/accounting/vouchers/[id]` | Accounting | | `/admin/tenants/[id]` | Platform admin | | `/beauty/mobile/[portal]` | Beauty | | `/beauty/site/{center,salon,service,staff,tracking}/[id]` | Beauty public | | `/healthcare/mobile/[portal]` | Healthcare | | `/healthcare/site/{clinic,doctor,hospital,tracking}/[id]` | Healthcare public | ### 2.4 Missing App Router conventions | File | Status | Impact | |------|--------|--------| | `middleware.ts` | Absent | No edge auth, tenant routing, or geo redirects | | `loading.tsx` | Absent | No streaming skeletons at segment boundaries | | `error.tsx` | Absent | Uncaught errors bubble to default Next.js error UI | | `not-found.tsx` | Absent | Custom 404 not defined | | `template.tsx` | Absent | No per-navigation remount behavior | --- ## 3. Layouts (20 total) ### Root layout (`app/layout.tsx`) - RTL Persian (`lang="fa" dir="rtl"`) - Provider stack: `ThemeProvider` → `ColorModeProvider` → `AppProviders` - Global `SiteHeader` wraps all routes including module portals and public sites **Concern:** Public marketing sites (`/beauty/site`, `/healthcare/site`) inherit the platform `SiteHeader`, which may not match intended public-site UX. ### Module layout patterns **Pattern A — Dedicated module shell (Accounting, Admin):** ``` app/layout.tsx → accounting/layout.tsx → AccountingShell + prefetch app/layout.tsx → admin/layout.tsx → AdminShell ``` **Pattern B — Pass-through + portal factory (Beauty, Healthcare):** ``` app/layout.tsx → beauty/layout.tsx (pass-through) → beauty/{portal}/layout.tsx → createPortalLayout(id) ``` **Pattern C — Public site layout:** ``` app/layout.tsx → beauty/site/layout.tsx → PublicBeautyLayout ``` Portal factory (`createPortalLayout`) encapsulates: 1. `AuthGuard` 2. Tenant/onboarding gate via `useMe()` 3. Domain portal shell with nav from `lib/*-portals.ts` --- ## 4. API Routes (BFF Layer) Three same-origin catch-all proxies under `app/api/`: | Route | Upstream env | Default | |-------|--------------|---------| | `/api/accounting/[...path]` | `ACCOUNTING_SERVICE_URL` | `:8002` | | `/api/beauty-business/[...path]` | `BEAUTY_BUSINESS_SERVICE_URL` | `:8011` | | `/api/healthcare/[...path]` | `HEALTHCARE_SERVICE_URL` | `:8010` | **Methods:** GET, POST, PATCH, PUT, DELETE **Forwarded headers:** `authorization`, `content-type`, `x-tenant-id`, `accept` (+ `x-patient-id` for healthcare) **Gaps:** - No BFF for platform Core API (uses direct `NEXT_PUBLIC_BACKEND_URL`) - No BFF for Identity/Keycloak - No `/api/delivery/*` despite backend delivery service existing - No rate limiting, request logging, or response caching at BFF layer --- ## 5. Business Modules ### 5.1 Accounting (`/accounting`) - **Architecture:** Monolithic screen components + inline route pages - **Components:** `AccountingShell`, `BusinessDocsPage`, `WorkflowScreens`, `DomainScreens`, `OperationalDocumentPage` - **API:** `lib/accounting-api.ts` (1,856 lines), consumed via `/api/accounting/*` - **Forms:** Heavy RHF + Zod (~20 files); `useFieldArray` in voucher flows - **Route pattern:** Most logic lives directly in `app/accounting/**/page.tsx` (many 200–660 line files) ### 5.2 Beauty Business (`/beauty`) - **Architecture:** Portal-based with page bundles - **Portals:** owner, admin, customer, reception, staff, site (public), hub, auth, mobile - **Route pattern:** ~75 routes are 2–3 line re-exports to `components/beauty/pages/{portal}.tsx` - **Owner CRUD:** ~20 routes under `(owner)/` contain inline page logic (forms, mutations) - **API:** `lib/beauty-business-api.ts` (1,446 lines) ### 5.3 Healthcare (`/healthcare`) - **Architecture:** Mirrors beauty portal pattern - **Portals:** patient, doctor, reception, clinic, hospital, pharmacy, admin, site, hub, auth, mobile - **Route pattern:** ~90 routes are thin re-exports to `components/healthcare/pages/` - **Legacy redirects:** 13 stub pages + 13 `next.config.mjs` redirects for URL compatibility - **Delivery:** Only accessible via `/healthcare/pharmacy-portal/delivery` — no standalone delivery module ### 5.4 Platform Admin (`/admin`) - Tenant/user/plan/feature management - Uses `lib/api.ts` (Core backend) directly, not BFF --- ## 6. Shared Infrastructure ### 6.1 Design system Two parallel UI layers — see [shared-components-report.md](./shared-components-report.md). Primary: `components/ds/` — CVA-based Button, FormField, DataTable, DatePicker (Jalali), Dialog, etc. ### 6.2 Theme - White-label via CSS variables in `styles/globals.css` - Runtime theme from `public/theme.config.json` or tenant API (`lib/theme.ts`, `useTheme`) - Module accents: `--beauty-accent`, `--health-accent` - Tailwind maps semantic tokens to CSS vars (`tailwind.config.ts`) ### 6.3 State management | Layer | Technology | Scope | |-------|------------|-------| | Server/async state | TanStack Query v5 | Global (`AppProviders`) | | Auth/session | Custom hooks + localStorage tokens | `useMe`, `useAuth`, `lib/auth.ts` | | UI state | `useState` | Local per component | | Global UI | React Context | Color mode only (`ColorModeProvider`) | No Redux, Zustand, Jotai, or URL state library. **Query defaults:** staleTime 60s, gcTime 10min, retry 1, no refetch on focus/mount. ### 6.4 Hooks (`hooks/` — 10 files) | Hook | Usage count | Role | |------|-------------|------| | `useTenantId` | ~67 | Tenant scoping for API calls | | `useMe` | ~7 | Session/user/workspace | | `useBeautyLookups` / `useHealthcareLookups` | ~6–7 | Cached dropdown data | | `useHealthcarePatientContext` / `useHealthcareDoctorContext` | 1 each | Portal identity resolution | ### 6.5 Forms - **Stack:** react-hook-form + @hookform/resolvers + zod - **Accounting:** Mature — Controller for MoneyInput/DatePicker, useFieldArray for vouchers - **Beauty owner:** CRUD dialogs with zodResolver - **Healthcare:** Display-heavy; minimal form usage - **No FormProvider** pattern; `FormField` wrapper from DS used instead ### 6.6 Tables - **Installed:** `@tanstack/react-table` — **zero imports in source** - **Actual:** Custom `DataTable` in `components/ds/Page.tsx` — simple column/row renderer - **Beauty wrapper:** `BeautyTablePage` adds client-side search ### 6.7 Charts - **Installed:** `recharts` — **zero imports in source** - Analytics routes exist; chart rendering not implemented --- ## 7. Route Implementation Patterns ### Thin re-export (Beauty/Healthcare — ~175 routes) ```tsx // app/beauty/customer/wallet/page.tsx export { CustomerWallet as default } from "@/components/beauty/pages/customer"; ``` **Benefits:** Clean URL-to-component mapping, page bundles co-locate related screens **Risks:** Large page bundle files (753 lines in `beauty/pages/public.tsx`) ### Inline page logic (Accounting — ~130 routes) Most accounting routes are `"use client"` components with queries, forms, and mutations inline. Some delegate to shared screen components (`WorkflowScreens`, `DomainScreens`) via 3–25 line wrappers. ### Hybrid (Beauty owner CRUD — ~20 routes) `(owner)/*` pages contain full CRUD implementation inline rather than delegating to page bundles like other beauty portals. --- ## 8. Architecture Violations & Smells ### Critical | Issue | Location | Severity | |-------|----------|----------| | Production build fails TypeScript | `app/accounting/purchase/returns/page.tsx:409` — `loading` prop on DS `Button` | 🔴 Blocker | | Business logic in routes (accounting) | 644-line `chart-of-accounts/page.tsx`, 663-line `reports/page.tsx` | 🔴 Maintainability | | Split owner portal (beauty) | `(owner)/` vs `owner/` folders | 🟡 Confusion | ### Moderate | Issue | Details | |-------|---------| | Dual Button/Badge systems | `components/ui/` vs `components/ds/` | | Monolithic API clients | 3 files × 1,300–1,850 lines | | Unused npm dependencies | `@tanstack/react-table`, `recharts` | | Global SiteHeader on public sites | May leak platform chrome into marketing pages | | No middleware | Auth/tenant checks duplicated in layouts | | Dead code | 5 files with zero imports (see dependency-report.md) | ### Low | Issue | Details | |-------|---------| | Inconsistent module DS folder naming | beauty: `design-system/` vs healthcare: `ui/` | | Deprecated `BeautyBusinessShell` | Still in codebase | | Healthcare legacy redirect stubs | 13 client-side redirect pages duplicate config redirects | --- ## 9. Build Performance **Build command:** `npm run build` (Next.js 15.5.18) | Metric | Value | |--------|-------| | Webpack compile | ~55 seconds | | Type check | **Failed** | | Lint | Not reached (blocked by type error) | **Type error:** ``` app/accounting/purchase/returns/page.tsx:409 Property 'loading' does not exist on ButtonProps (components/ds/Button.tsx) ``` **Implications for scaling:** - 350 static/SSR pages in single build graph — compile time will grow linearly with new modules - No module federation or package splitting - All business modules share one `node_modules` and one build pipeline - Incremental compilation enabled (`tsconfig incremental: true`) but full builds remain heavy **Recommendations (documented in migration plan):** - Fix type error immediately - Consider route-group-level code splitting audit - Evaluate `@next/bundle-analyzer` before adding 4th business module --- ## 10. Scaling Readiness Assessment ### What works well 1. **Clean domain boundaries** — accounting, beauty, healthcare do not import each other's components or lib 2. **Consistent BFF pattern** — browser never calls microservice origins directly for module APIs 3. **Portal factory pattern** — new role-based portals can reuse `createPortalLayout` 4. **Shared DS adoption** — `components/ds/` used across all modules (~90+ import sites) 5. **TanStack Query conventions** — predictable cache keys per domain 6. **No circular dependencies** — import graph analysis found zero cycles ### What blocks enterprise scale 1. **Accounting route bloat** — business logic not extracted from App Router pages 2. **API client monoliths** — unmaintainable at 2,000+ methods per service 3. **Missing edge/middleware layer** — auth and tenant resolution duplicated 4. **No error/loading boundaries** — poor resilience UX at scale 5. **Build not green** — CI cannot enforce quality gate 6. **Beauty/healthcare duplication** — ~90% identical portal infrastructure copied twice ### Verdict The architecture is **ready for adding routes and portals within existing modules** but **not ready for adding many new business modules** without first establishing: - Enforced module boundary lint rules - Shared portal infrastructure abstraction - Route-thin / component-thick convention across all modules - Green build pipeline --- ## 11. Related Documents | Document | Focus | |----------|-------| | [frontend-migration-plan.md](./frontend-migration-plan.md) | Phased improvement roadmap | | [module-boundaries.md](./module-boundaries.md) | Import rules and ownership | | [shared-components-report.md](./shared-components-report.md) | DS inventory and duplicates | | [dependency-report.md](./dependency-report.md) | npm, API clients, dead code | | [import-report.md](./import-report.md) | Alias usage and violations | | [refactor-risk-analysis.md](./refactor-risk-analysis.md) | Risk matrix for future changes | --- ## Appendix: File Counts | Category | Count | |----------|------:| | Total `.ts`/`.tsx` source files | ~481 | | `page.tsx` routes | 350 | | `layout.tsx` | 20 | | API route handlers | 3 | | `components/ds/` files | 11 | | `hooks/` files | 10 | | `lib/` files | 20 | | `@/` import statements | 756 | | Relative import statements | 55 | | Files > 300 lines | 20+ | | Circular dependencies | 0 |