5.9 KiB
5.9 KiB
Architecture Rules
These rules describe the approved implementation patterns for ALLA OS.
New work should extend these patterns instead of inventing parallel architecture.
Frontend Architecture
- Use Next.js App Router.
- Prefer server components by default.
- Use client components only for interactivity, browser APIs, or client-side state/query hooks.
- Use
PageContainerfor dashboard page headers and top-level content framing. - Keep business-facing CRM UI aligned with
docs/business/crm-terminology.mdandsrc/features/crm/shared/terminology.ts.
Data Loading Pattern
- For data-heavy dashboard/app pages, prefer server prefetch plus
HydrationBoundary. - Client tables and detail panes should consume prefetched data with
useSuspenseQuery(). - Define query options and query keys in
src/features/<feature>/api/queries.ts. - Keep filters in URL state with
nuqswhen the page supports shareable filtering.
Reference examples:
src/features/users/components/user-listing.tsxsrc/features/crm/customers/components/customer-listing.tsxsrc/app/dashboard/crm/settings/user-role-assignments/page.tsx
UI Composition Rules
- Reuse shadcn/ui primitives from
src/components/ui/**. - Reuse table shell components from
src/components/ui/table/**. - Reuse
useDataTablefor table state orchestration. - Reuse TanStack Form wrappers from
@/components/ui/tanstack-formand field components from@/components/forms/fields. - Do not create a parallel CRM design system or terminology layer.
Backend Architecture
- Use Route Handlers under
src/app/api/**as the HTTP boundary. - Keep route handlers thin: auth, validation, service call, response mapping, and audit.
- Put business logic in feature or foundation service layers under
src/features/**. - Use Drizzle ORM for database access.
- Keep direct database access out of client components.
Route Structure Pattern
Approved structure:
requireOrganizationAccess()or another shared auth helper- request parsing and schema validation
- build resolved access/security context when the feature is scope-sensitive
- call feature/foundation service
- emit audit/security events through shared audit services
- return typed JSON/stream response
Reference examples:
src/app/api/crm/quotations/route.tssrc/app/api/crm/approvals/route.tssrc/app/api/crm/reports/pipeline/route.tssrc/app/api/crm/reports/export/route.ts
Module Structure
Preferred feature layout:
src/features/<feature>/
api/
types.ts
service.ts
queries.ts
mutations.ts
components/
schemas/
server/
Rules:
- components import contracts from
api/types.ts - UI calls query options from
api/queries.ts - UI mutations reuse shared mutation configs from
api/mutations.ts - services in
api/service.tscall route handlers throughsrc/lib/api-client.ts - server business logic stays under feature/foundation server services
Service Layer Pattern
- Services own business rules, persistence orchestration, and reusable data shaping.
- Services may call other services when extending an existing foundation.
- Services should not depend on UI concerns.
- Report services must use dataset/builders/filter layers instead of embedding ad hoc SQL in routes.
- PDF flows must use the PDF and artifact foundations instead of custom rendering paths.
Reference services:
src/features/crm/reports/server/service.tssrc/features/foundation/approval/server/service.tssrc/features/foundation/pdf-generator/server/service.tssrc/features/foundation/storage/service.ts
Query Pattern
- Define centralized query key factories in each feature's
api/queries.ts. - Keys should group into
all,lists(),list(filters),details(),detail(id), and child-resource keys where applicable. - Server pages prefetch query options and dehydrate.
- Clients read the same keys with
useSuspenseQuery().
Reference examples:
src/features/products/api/queries.tssrc/features/users/api/queries.tssrc/features/crm/reports/api/queries.ts
Mutation Pattern
- Centralize mutation configs in
api/mutations.ts. - Shared mutation configs must retain cache invalidation.
- After create: invalidate list-level keys.
- After update: invalidate list and detail keys.
- After delete: invalidate lists and remove stale detail queries.
- Refresh related tabs, counts, previews, and approval panels when they depend on the changed entity.
Audit Pattern
- Use
auditCreate(),auditUpdate(),auditDelete(), orauditAction()fromsrc/features/foundation/audit-log/service.ts. - Reuse existing entity types and action naming where possible.
- Security-sensitive denials use
crm_security_accessthroughauditCrmSecurityEvent(). - Report view/export events use
crm_report.
Reference examples:
src/features/foundation/audit-log/service.tssrc/features/crm/security/server/service.tssrc/features/crm/reports/server/exports/service.ts
Security Pattern
- Organization access starts with
requireOrganizationAccess(). - CRM authorization must use resolved access, not raw role strings.
- Use
resolveCrmMembershipAccess()for effective permission/scope unions. - Use
buildCrmSecurityContext()for scope-aware CRM services. - Use
resolveCrmAccess()/ report-context builders for report routes. - Scope checks must enforce branch, product, ownership, and pricing visibility server-side.
Reference docs:
docs/security/crm-authorization-boundaries.mddocs/security/crm-access-enforcement-inventory.mddocs/security/team-scope-limitations.md
Explicitly Forbidden
- business logic in route handlers
- client-side only authorization
- direct role-string CRM authorization
- direct DB access from client code
- duplicate export/report/PDF/approval/security foundations
- new Clerk integration
- new SWR or Redux data architecture for app-owned features
- new React Hook Form usage in this repo