Move business logic out of App Router into module packages, add boundary validation scripts, and keep all routes as thin re-exports without changing URLs or API behavior. Co-authored-by: Cursor <cursoragent@cursor.com>
9.9 KiB
TorbatYar Frontend — Module Boundaries
Date: 2026-07-26
Scope: Frontend import and ownership rules
Aligns with: docs/architecture/module-boundaries.md (platform-level)
1. Module Inventory
| Module ID | Route prefix | Component root | API client | BFF route |
|---|---|---|---|---|
| platform | /, /login, /register, /onboarding, /dashboard, /auth |
components/ (root), components/ui/, components/auth/ |
lib/api.ts, lib/auth.ts, lib/identity.ts |
None (direct) |
| admin | /admin |
components/admin/ |
lib/api.ts |
None (direct) |
| accounting | /accounting |
src/modules/accounting/ |
src/modules/accounting/services/accounting-api.ts |
/api/accounting/* |
| beauty | /beauty |
modules/beauty/ |
modules/beauty/services/beauty-business-api.ts |
/api/beauty-business/* |
| healthcare | /healthcare |
src/modules/healthcare/ |
src/modules/healthcare/services/healthcare-api.ts |
/api/healthcare/* |
Not yet in frontend: delivery (backend exists; UI only via healthcare pharmacy portal), loyalty, communication, sports center, experience.
Partially migrated: CRM (modules/crm/), Hospitality (modules/hospitality/).
2. Shared Layer (Cross-Module)
These folders are owned by the platform and may be imported by any module:
components/ds/ # Design system — MUST remain domain-agnostic
components/providers/ # Query, color mode
components/AuthGuard.tsx
components/AdminAuthGuard.tsx
components/ThemeProvider.tsx
components/SiteHeader.tsx
components/TenantSwitcher.tsx
components/RouteProgress.tsx
components/AppStoreGrid.tsx
components/settings/ # Account settings (platform)
hooks/ # All shared hooks
lib/utils.ts
lib/auth.ts
lib/theme.ts
lib/apps-catalog.ts
styles/globals.css
Shared layer rules
| Rule | Status |
|---|---|
components/ds/ MUST NOT import from domain modules |
✅ Compliant |
| Shared hooks MUST NOT import domain API clients | ✅ Compliant |
Shared hooks MAY import lib/auth.ts, lib/api.ts (platform) |
✅ By design |
| Platform components MUST NOT import domain shells | ✅ Compliant |
3. Domain Layer Rules
Each business module owns:
app/{module}/ # Routes and layouts (thin re-exports when migrated)
src/modules/{module}/ # Healthcare, Accounting module root
modules/{module}/ # Beauty, CRM, Hospitality module root
components/{module}/ # Legacy (removed for migrated modules)
lib/{module}-api.ts # Deprecated shims → module services
hooks/use{Module}*.ts # Deprecated shims → module hooks
app/api/{service}/ # BFF proxy
Allowed import directions
┌─────────────┐
│ shared │
│ ds, hooks │
│ lib/auth │
└──────▲──────┘
│
┌─────────────────┼─────────────────┐
│ │ │
┌─────┴─────┐ ┌──────┴──────┐ ┌──────┴──────┐
│ accounting │ │ beauty │ │ healthcare │
└───────────┘ └─────────────┘ └─────────────┘
✕ ✕ ✕
└──────────────── no cross-imports ──┘
| From → To | platform/shared | accounting | beauty | healthcare | admin |
|---|---|---|---|---|---|
| platform | ✅ | ❌ | ❌ | ❌ | ✅ (shell only) |
| accounting | ✅ | ✅ | ❌ | ❌ | ❌ |
| beauty | ✅ | ❌ | ✅ | ❌ | ❌ |
| healthcare | ✅ | ❌ | ❌ | ✅ | ❌ |
| admin | ✅ | ❌ | ❌ | ❌ | ✅ |
| ds | ✅ (internal) | ❌ | ❌ | ❌ | ❌ |
Current compliance: ✅ Zero cross-domain imports detected.
4. Route Layer Rules
What routes MAY contain
"use client"directive- Default export (page component or re-export)
- Metadata exports (server components only)
- Dynamic route param typing
- Thin re-exports to
components/{module}/
What routes MUST NOT contain
- Direct
fetch()to microservice origins (use BFF or lib API client) - Business validation logic (>20 lines)
- Form schemas (belong in components or
lib/schemas/) - Reusable UI subcomponents (belong in
components/{module}/) - Cross-module imports
Current compliance by module
| Module | Route thinness | Compliance |
|---|---|---|
| Beauty (portals/site) | 2–3 line re-exports | ✅ Excellent |
| Healthcare (portals/site) | 2–3 line re-exports | ✅ Excellent |
Beauty (owner) CRUD |
44–245 lines inline | ⚠️ Violation |
| Accounting | 3–663 lines inline | ❌ Major violation |
| Platform/admin | Mixed | ⚠️ Acceptable |
5. API Boundary Rules
Client-side API access
| API | Access method | Correct |
|---|---|---|
| Accounting service | accountingApi.* → /api/accounting/* |
✅ |
| Beauty business service | beautyBusinessApi.* → /api/beauty-business/* |
✅ |
| Healthcare service | healthcareApi.* → /api/healthcare/* |
✅ |
| Core platform | api.* → NEXT_PUBLIC_BACKEND_URL |
✅ |
| Identity/Keycloak | identity.* → NEXT_PUBLIC_IDENTITY_API_URL |
✅ |
Forbidden
- Importing another module's API client
- Calling microservice ports directly from browser (bypassing BFF)
- Storing business rules in BFF route handlers (proxies must stay thin)
6. State & Cache Boundaries
Query key namespaces
Each module owns its query key prefix:
// Platform
["me"]
["theme", tenantId]
// Accounting
["accounting", tenantId, ...]
// Beauty
["beauty", tenantId, ...]
// Healthcare
["healthcare", tenantId, ...]
Rule: Modules MUST NOT invalidate another module's query keys.
Current compliance: ✅ No cross-module cache invalidation detected.
7. UI Component Boundaries
Design system (components/ds/)
- Domain-agnostic primitives only
- No module-specific labels, icons, or business types
- Used by all modules
Module design systems
| Module | Path | May import |
|---|---|---|
| Beauty | modules/beauty/design-system/ |
@/components/ds, @/lib/utils |
| Healthcare | src/modules/healthcare/design-system/ |
@/src/shared/ui, @/lib/utils |
| Accounting | (uses ds via @/src/shared/ui) |
@/src/shared/ui, @/lib/utils |
Rule: Module DS MUST NOT import from other modules' DS or pages.
Legacy UI (components/ui/)
- Platform/marketing surfaces only: homepage, auth, onboarding, dashboard
- New module code MUST NOT import from
components/ui/
8. Known Boundary Violations
Structural (not import violations)
| Issue | Type | Severity |
|---|---|---|
Beauty at modules/ vs Healthcare/Accounting at src/modules/ |
Path inconsistency | Low |
Global SiteHeader on public module sites |
Layout leak | Medium |
BeautyBusinessShell deprecated but present |
Dead code | Low |
| Healthcare legacy redirect pages duplicate config | Redundancy | Low |
Beauty at modules/ vs Healthcare at src/modules/ |
Path inconsistency | Low |
| Accounting business logic in routes | Responsibility | Resolved — 120 thin adapters |
Naming inconsistencies (boundary clarity)
| Issue | Impact |
|---|---|
healthcare uses design-system/ (aligned with beauty) |
Resolved in migration |
lib/beauty-business-nav.ts dead file alongside lib/beauty-portals.ts |
Stale boundary docs |
9. Enforcement Recommendations
ESLint no-restricted-imports (proposed)
// .eslintrc — example rules
{
"rules": {
"no-restricted-imports": ["error", {
"patterns": [
{
"group": ["@/components/accounting/*"],
"message": "Accounting components are not importable outside accounting module."
},
{
"group": ["@/components/beauty/*"],
"message": "Beauty components are not importable outside beauty module."
},
{
"group": ["@/src/modules/healthcare/*"],
"message": "Healthcare components are not importable outside healthcare module."
},
{
"group": ["@/components/healthcare/*"],
"message": "Legacy healthcare path — use @/src/modules/healthcare/*."
}
]
}]
}
}
Apply overrides for files inside each module's app/ and components/ trees.
CI checks (proposed)
- Import graph script — fail on cross-domain edges
- Route size lint — warn if
app/**/page.tsx> 100 lines - API client size — warn if any
lib/*-api.ts> 500 lines
10. Adding a New Module Checklist
- Create isolated
app/{module}/,components/{module}/,lib/{module}-api.ts - Add BFF at
app/api/{service}/[...path]/route.ts - Register in
lib/apps-catalog.ts - Add env vars to
next.config.mjs - Use
components/ds/exclusively (notcomponents/ui/) - Define query key prefix
["{module}", tenantId] - Add ESLint override allowing self-imports only
- Document in this file
Related Documents
- frontend-architecture-audit.md
- import-report.md
- dependency-report.md
- Platform:
docs/architecture/module-boundaries.md