--- description: Architecture for feature modules in apps/web (full-page pattern) globs: apps/web/src/apps/**/*.{ts,tsx} alwaysApply: false --- # Web Module Architecture Working directory: `apps/web/`. Create authenticated features under `src/apps/main/modules/`. Canonical sample: `example/full-page/`. ## Directory layout ```text src/apps/main/modules/// data/ # *RemoteDataServices domain/ constants/ # ModuleConfigEntity entities/ # Entity + DTO factories/ # Wire apiClient + service + transformer transformers/ # DTO ↔ entity validators/ # Zod factories (forms) presentation/ factory/index.tsx # registerModuleNamespace + EnterpriseModuleProvider + routes pages/ # *.page.index | *.page.form | *.page.detail components/ # Module-local UI only store/ # Module zustand (if needed) languages/{en,id}/ # Module dictionaries index.tsx # Optional group router (see example/index.tsx) ``` Register: lazy route in `src/apps/main/index.tsx` + menu entry in `src/apps/main/layouts/data/menu.data.ts`. Auth screens live under `src/apps/auth/` (separate from main modules). ## FULL_PAGE routes (sample) `/index`, `/detail/:dataId`, `/create`, `/edit/:dataId`, `/duplicate/:dataId` — default redirect to `webUrl/index`. | Page | Wrapper | |---|---| | Index | `EnterpriseIndexPageProvider` + `EnterpriseDataTable` | | Detail | `EnterpriseDetailPageProvider` | | Form | `EnterpriseFormPageProvider` + `FormPageType` | Copy `example/full-page` — do not invent a third layout style. ## Required wiring 1. **Constants** — `ModuleConfigEntity`: `moduleKey`, `translationNamespace`, `apiUrl`, `webUrl`, `moduleCategory` (`FULL_PAGE` | `SINGLE_PAGE`), `moduleType` (`TRANSACTION` | `MASTER_DATA`). 2. **Presentation factory** — `registerModuleNamespace` once at module scope; wrap routes in `EnterpriseModuleProvider`. 3. **Domain factory** — singleton service via `apiClient` from `src/core/lib/api-client` (never raw axios). 4. **i18n** — `useEnterpriseModuleTranslationContext()`; module keys unprefixed; shared via `common:` / `nav:`. 5. **Navigation** — `useEnterpriseModuleNavigationContext()` helpers, not ad-hoc paths. 6. **RBAC** — `moduleKey` must match an `api.md` privilege-keys `code`; the menu leaf uses the same `moduleKey`. See `web-rbac.mdc`. ## Layer rules - `presentation/` → `domain/` / `data/` via factories; never reverse. - Shared/reusable across modules → `src/core/` (see web-core-placement). - Prefer `@repo/ui/components` + `@repo/ui/foundations`; form fields via `@repo/ui/form`. - Pages: default export, lazy-loaded from the presentation factory.