# 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 |
