Initial commit
This commit is contained in:
@@ -0,0 +1,392 @@
|
||||
# CRUX React Frontend Architecture
|
||||
|
||||
This guide describes the recommended React frontend architecture for apps in the CRUX monorepo.
|
||||
|
||||
Its main goal is to make frontend development easier, more structured, and consistent across teams, so that every developer can write and understand each other's code quickly.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Base Concepts](#base-concepts)
|
||||
- [Disclaimer](#disclaimer)
|
||||
- [Core Principles](#core-principles)
|
||||
- [Recommended Layering](#recommended-layering)
|
||||
- [Architecture Scope](#architecture-scope)
|
||||
- [Layer Dependency Rules](#layer-dependency-rules)
|
||||
- [Entities Layer](#1-entities-layer)
|
||||
- [Data Layer (Data Access Layer)](#2-data-layer-data-access-layer)
|
||||
- [Purpose](#purpose)
|
||||
- [Responsibilities](#responsibilities-2)
|
||||
- [Rules and Guidelines](#rules-and-guidelines)
|
||||
- [Infrastructure Layer](#infrastructure-layer)
|
||||
- [Reference Data Layer](#reference-data-layer)
|
||||
- [Providers Layer](#3-providers-layer)
|
||||
- [Pages Layer](#4-pages-layer)
|
||||
- [View Layer](#5-view-layer)
|
||||
- [view/components/](#viewcomponents)
|
||||
- [view/forms/](#viewforms)
|
||||
- [view/widgets/](#viewwidgets)
|
||||
- [Shared Layer](#6-shared-layer)
|
||||
- [Responsibilities](#responsibilities-4)
|
||||
- [Rules](#rules-2)
|
||||
- [Full documentation](#full-documentation)
|
||||
|
||||
## Base Concepts
|
||||
|
||||
- **Purpose:** Provide a shared frontend foundation with predefined packages, utilities, and patterns for React apps and frontend modules in this monorepo.
|
||||
- **Goals:**
|
||||
- Reduce boilerplate code.
|
||||
- Standardize folder structure, file naming, and coding practices in the company context.
|
||||
- Assist in onboarding of new developers.
|
||||
- **Included:**
|
||||
- Preconfigured `React` + `TypeScript` setup.
|
||||
- Common utilities and helper functions.
|
||||
- Shared UI components, hooks, and state management helpers.
|
||||
- Integrated i18n, form validation, and API utilities.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This architecture guide is intended as a **recommendation**, not a strict rulebook. **Feel free to adapt and modify it based on your team’s needs and project context**.
|
||||
|
||||
Key points to keep in mind:
|
||||
|
||||
1. **Maintainability comes first**.
|
||||
The main goal of this structure is to help someone new to the project quickly understand where to make changes.
|
||||
It’s about finding the right place to touch the code now, rather than strictly following a pattern for its own sake.
|
||||
**Ultimately, the main goal is to make development easier for everyone**
|
||||
2. **Naming debates are inevitable**.
|
||||
Any architecture guide may spark discussions about file, folder, or function naming. Use this guide as a reference, but remember that practicality and clarity are more important than strict adherence.
|
||||
3. **Flexibility is allowed**.
|
||||
- You can adjust layers, module structure, or naming conventions if it improves readability or reduces complexity.
|
||||
- New patterns or exceptions can be added as the project evolves.
|
||||
|
||||
In short: this guide exists to make the project easier to understand and maintain, not to create unnecessary friction. Use judgment and prioritize clarity over rigid consistency.
|
||||
|
||||
---
|
||||
|
||||
Цей посібник з архітектури є **рекомендацією**, а не суворим зводом правил. **Не соромтеся адаптувати та змінювати його відповідно до потреб вашої команди та контексту проєкту**.
|
||||
|
||||
Ключові моменти, які варто мати на увазі:
|
||||
|
||||
1. **Насамперед — зручність підтримки**.
|
||||
Головна мета цієї структури — допомогти новому учаснику проєкту швидко зрозуміти, де вносити зміни.
|
||||
Йдеться про те, щоб знайти правильне місце в коді просто зараз, а не сліпо дотримуватися патерну заради самого патерну.
|
||||
**Зрештою, головна мета — зробити розробку простішою для всіх.**
|
||||
2. **Дискусії щодо назв неминучі**.
|
||||
Будь-який посібник з архітектури може спровокувати суперечки щодо назв файлів, папок або функцій. Використовуйте цей посібник як орієнтир, але пам'ятайте, що практичність і зрозумілість важливіші за суворе дотримання правил.
|
||||
3. **Гнучкість допускається**.
|
||||
- Ви можете коригувати шари, структуру модулів або угоди щодо найменування, якщо це покращує читабельність або зменшує складність.
|
||||
- У міру розвитку проєкту можна додавати нові патерни або винятки.
|
||||
|
||||
Коротко: цей посібник існує для того, щоб проєкт був зрозумілішим і простішим у підтримці, а не для того, щоб створювати зайві труднощі. Керуйтеся здоровим глуздом і надавайте перевагу ясності, а не жорсткій послідовності.
|
||||
|
||||
## Core Principles
|
||||
|
||||
1. Blocks are composable and isolated
|
||||
|
||||
- Each block (component, form, widget) has a single responsibility.
|
||||
- Higher-level blocks (widgets, pages) compose lower-level blocks.
|
||||
|
||||
2. Data vs. UI separation
|
||||
|
||||
- `data` handles API, state, and business data.
|
||||
- `view` handles UI and interaction.
|
||||
- No API logic in UI components — UI calls hooks from `data`.
|
||||
|
||||
3. Entities are global types/interfaces
|
||||
|
||||
- Placing them in `entities`.
|
||||
- Any block (data hook, form, widget, page) can use these.
|
||||
|
||||
4. Shared
|
||||
|
||||
- shared/ = globally reusable components, hooks, utilities.
|
||||
|
||||
5. Infrastructure
|
||||
|
||||
- infrastructure/ = app-wide technical adapters such as HTTP clients, React Query, storage adapters, and formatting service wrappers.
|
||||
|
||||
6. Reference data
|
||||
- refdata/ = app-wide API-backed lookup datasets shared by multiple modules.
|
||||
|
||||
## Recommended Layering
|
||||
|
||||
```plaintext
|
||||
src/
|
||||
├── data/ # API, react-query hooks, DTOs
|
||||
├── entities/ # global types and interfaces
|
||||
├── infrastructure/ # technical adapters: HTTP, query, storage, error handling
|
||||
├── providers/ # global providers (router, query, session, i18n, theme)
|
||||
├── refdata/ # API-backed common reference datasets
|
||||
├── shared/ # reusable global components, hooks, libs, assets, styles
|
||||
├── view/ # UI layer (blocks)
|
||||
│ ├── components/ # Feature-level reusable components (ProductCard, LoginButton, VehicleSearchInput)
|
||||
│ ├── forms/ # smart form components (ProductForm, UserForm)
|
||||
│ └── widgets/ # composed smart, multi-entity components (DashboardStatsWidget, UserProfileWidget)
|
||||
└── pages/ # route-level pages
|
||||
```
|
||||
|
||||
## Architecture Scope
|
||||
|
||||
The layer structure can be used at three levels:
|
||||
|
||||
| Scope | Example | Meaning |
|
||||
| --------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| App-level architecture | `apps/admin/src/app`, `apps/admin/src/shared` | Global app shell, providers, routing, layout, infrastructure, reusable app-wide building blocks. |
|
||||
| Core module architecture | `apps/admin/src/core/FileManager` | Built-in product modules shipped with the admin template. Each large core module can own its own `data`, `entities`, `providers`, `pages`, `view`, and `shared`. |
|
||||
| Project module architecture | `apps/admin/src/modules/*` | Generated or project-specific modules. Each module should follow the same layer rules inside its own folder when it becomes more than a simple page. |
|
||||
|
||||
For small modules, do not create empty layers only to match the template. Add a layer when the module has real ownership for that layer.
|
||||
|
||||
### Admin App Interpretation
|
||||
|
||||
`apps/admin` is a reusable admin panel template, not a simple single-purpose React app.
|
||||
|
||||
The app shell owns cross-cutting concerns:
|
||||
|
||||
- routing and route registration
|
||||
- global providers
|
||||
- layouts
|
||||
- i18n setup
|
||||
- shared UI
|
||||
- technical infrastructure
|
||||
- module registration
|
||||
|
||||
Admin app infrastructure that is shared by every module lives in `apps/admin/src/infrastructure`.
|
||||
Examples include HTTP clients, React Query helpers, storage adapters, and error normalization.
|
||||
|
||||
Admin-wide API-backed reference datasets live in `apps/admin/src/refdata`.
|
||||
Examples include country lists, common option datasets, and other reusable data that comes from the backend but is not owned by one feature module.
|
||||
|
||||
Each feature module owns its feature concerns:
|
||||
|
||||
- API calls and query hooks in module `data`
|
||||
- module domain types in module `entities`
|
||||
- module-local contexts and stores in module `providers`
|
||||
- route-level screens in module `pages`
|
||||
- module UI in module `view`
|
||||
- module-only helpers in module `shared`
|
||||
|
||||
If a helper or component becomes useful across multiple modules, move it upward to `apps/admin/src/shared` or a package under `packages/`.
|
||||
|
||||
## Layer Dependency Rules
|
||||
|
||||
Each layer has defined set of layers it is allowed to import from.
|
||||
Imports must only flow **downward** — a lower layer never knows about a higher one.
|
||||
Violating this creates circular dependencies and couples layers that should be independent.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
pages["pages/"]
|
||||
view["view/"]
|
||||
data["data/"]
|
||||
providers["providers/"]
|
||||
infrastructure["infrastructure/"]
|
||||
refdata["refdata/"]
|
||||
entities["entities/"]
|
||||
shared["shared/"]
|
||||
|
||||
pages --> view
|
||||
pages --> data
|
||||
pages --> refdata
|
||||
pages --> providers
|
||||
pages --> infrastructure
|
||||
pages --> entities
|
||||
pages --> shared
|
||||
|
||||
view --> data
|
||||
view --> refdata
|
||||
view --> infrastructure
|
||||
view --> entities
|
||||
view --> shared
|
||||
|
||||
refdata --> infrastructure
|
||||
refdata --> shared
|
||||
|
||||
providers --> data
|
||||
providers --> refdata
|
||||
providers --> infrastructure
|
||||
providers --> shared
|
||||
|
||||
data --> infrastructure
|
||||
data --> entities
|
||||
data --> shared
|
||||
|
||||
infrastructure --> shared
|
||||
```
|
||||
|
||||
| Layer | Can import from |
|
||||
| ----------------- | --------------------------------------------------------------------------- |
|
||||
| `shared/` | nothing — zero app-layer dependencies |
|
||||
| `entities/` | nothing — zero app-layer dependencies |
|
||||
| `infrastructure/` | `shared/`, external packages, app runtime config |
|
||||
| `refdata/` | `infrastructure/`, `shared/` |
|
||||
| `data/` | `infrastructure/`, `entities/`, `shared/` |
|
||||
| `providers/` | `infrastructure/`, `data/`, `refdata/`, `shared/` |
|
||||
| `view/` | `data/`, `refdata/`, `entities/`, `shared/`, generic infrastructure helpers |
|
||||
| `pages/` | all layers |
|
||||
|
||||
### Rules
|
||||
|
||||
1. **`shared/` has no imports** — it is infrastructure and UI primitives only. It must not reference any domain type or application layer. If a utility needs a domain type, it does not belong in `shared/`.
|
||||
|
||||
2. **`entities/` has no imports** — No logic, no React, no external libraries.
|
||||
|
||||
3. **`data/` never imports from `view/` or `pages/`** — data hooks and API functions are completely independent of the UI.
|
||||
|
||||
4. **`view/` never imports from `pages/`** — components, forms, and widgets do not depend on page-level composition.
|
||||
|
||||
5. **The boundary between `shared/ui/` and `view/components/` is enforced by this rule** — if a component needs to import from `entities/`, it cannot live in `shared/`.
|
||||
|
||||
6. **`infrastructure/` contains technical adapters, not feature logic** — HTTP clients, React Query, storage adapters, and formatting service wrappers live here.
|
||||
|
||||
7. **`refdata/` contains app-wide lookup data** — country lists, languages, currencies, and other shared backend datasets live here instead of inside one feature module.
|
||||
|
||||
### 1. **Entities** Layer
|
||||
|
||||
The `entities` layer contains global types and interfaces that define your domain models.
|
||||
|
||||
- Define entity types for your business objects: User, Product, Message
|
||||
- Define enums or constants
|
||||
- No Framework
|
||||
- No API or hook logic
|
||||
|
||||
### 2. **Data** layer (Data Access Layer)
|
||||
|
||||
#### Purpose
|
||||
|
||||
The `data` layer is responsible for all interactions with external or internal data sources and state management hooks.
|
||||
|
||||
It is the only layer that knows how to fetch, mutate, and map data, while higher layers (`forms`, `widgets`, `pages`) just consume the hooks or data from this layer.
|
||||
|
||||
#### Responsibilities
|
||||
|
||||
- Handle API calls
|
||||
- Provide React Query hooks or other state management hooks for data fetching/mutations
|
||||
- Define DTOs (Data Transfer Objects)
|
||||
- Provide DTO mappers (map raw API data to app entities or view models)
|
||||
- Define types/interfaces specific to API responses
|
||||
- Isolate side-effects like HTTP requests, error handling, or caching
|
||||
|
||||
#### Rules and Guidelines
|
||||
|
||||
1. Only data and state logic belongs here
|
||||
|
||||
- No UI, forms, or layout logic
|
||||
|
||||
2. Use React Query (or other state library) for fetching/mutations
|
||||
|
||||
- Expose hooks, not raw API functions, to the view layer
|
||||
|
||||
3. DTOs + Mappers
|
||||
|
||||
- Map API response to internal entities/ types
|
||||
- Keep external API structure isolated from your app
|
||||
- Mappers should be covered by unit tests because they define the data-layer contract between API DTOs and app entities
|
||||
|
||||
4. Types/Interfaces
|
||||
|
||||
- Define API-specific types
|
||||
- Use entities/ for app-wide types
|
||||
|
||||
5. No direct imports from view/ or pages/
|
||||
- data/ is completely independent
|
||||
|
||||
### Infrastructure Layer
|
||||
|
||||
The `infrastructure` layer contains app-wide technical adapters.
|
||||
|
||||
In the admin app, it lives in `apps/admin/src/infrastructure/`.
|
||||
|
||||
Examples:
|
||||
|
||||
- configured HTTP clients and interceptors
|
||||
- React Query helpers, response types, and error normalization
|
||||
- storage or browser adapters
|
||||
- app-wide formatting service wrappers such as date and name formatters
|
||||
|
||||
This layer should stay feature-agnostic. Feature API modules should use infrastructure clients instead of creating their own clients.
|
||||
|
||||
### Reference Data Layer
|
||||
|
||||
The `refdata` layer contains app-wide reference datasets.
|
||||
|
||||
In the admin app, it lives in `apps/admin/src/refdata/`.
|
||||
|
||||
Examples:
|
||||
|
||||
- countries
|
||||
- languages
|
||||
- currencies
|
||||
- time zones
|
||||
- common option dictionaries
|
||||
|
||||
Use this layer for backend-backed lookup data shared by multiple modules. If the data belongs to one feature module, keep it in that module's `data/` layer.
|
||||
|
||||
### 3. **Providers** layer
|
||||
|
||||
The `providers` layer is responsible for global context, app-wide state, and services that need to be available throughout the React application.
|
||||
It sets up everything that any component or page might need: routing, state management, internationalization, theme, notifications, etc.
|
||||
|
||||
### 4. **Pages** layer
|
||||
|
||||
- `pages` = the highest-level blocks
|
||||
- Compose `widgets` + `forms` + `components` + `data` into full pages
|
||||
- Only page-level layout, routing, and orchestration logic lives here
|
||||
- Example: `UserPage` → uses `UserProfileWidget` + `UserActivityWidget`
|
||||
|
||||
### 5. **View** layer
|
||||
|
||||
1. **view/components/**
|
||||
|
||||
- “Dumb” components
|
||||
- Pure UI, no business logic
|
||||
- Example: `ProductCard`, `UserAvatar`
|
||||
|
||||
2. **view/forms/**
|
||||
|
||||
- Form components
|
||||
- Encapsulate form state, validation, and submit logic
|
||||
- Use hooks from `data/` for mutations/queries
|
||||
- Example: `CreateProductForm` → uses `useCreateProduct()` from `data/hooks`
|
||||
|
||||
3. **view/widgets/**
|
||||
- Composed components
|
||||
- Combine `components` + `forms` + `shared/ui`
|
||||
- Feature blocks that can be reused on multiple pages
|
||||
- Example: `UserProfileWidget` → contains `UserAvatar`, `UserForm`, `UserStatsTable`
|
||||
|
||||
### 6. **Shared** layer
|
||||
|
||||
The `shared` layer contains global reusable building blocks for the frontend:
|
||||
|
||||
- Components, hooks, utilities, assets, styles
|
||||
- Not tied to a specific feature or entity
|
||||
- Can be used by `view`, `pages`, `widgets`, or `forms`
|
||||
|
||||
This is where common "Lego" blocks live.
|
||||
|
||||
#### Responsibilities
|
||||
|
||||
- Provide reusable UI components (`Button`, `Input`, `Modal`, `Card`)
|
||||
- Provide reusable hooks (`useToggle`, `useDebounce`, `useLocalStorage`)
|
||||
- Provide utility functions (`formatDate`, `errorHandler`, http helpers)
|
||||
- Provide assets (icons, images, fonts)
|
||||
- Provide global styles (CSS variables, global CSS, Tailwind overrides)
|
||||
|
||||
#### Rules
|
||||
|
||||
- Components here are dumb, reusable, feature-agnostic
|
||||
- Hooks here are generic, not tied to a single entity or feature
|
||||
- Assets can be imported anywhere
|
||||
- Styles are global or utility-level, not component-specific
|
||||
|
||||
## Full documentation
|
||||
|
||||
- [Entities Layer](./layers/entities.md)
|
||||
- [Data Layer](./layers/data.md)
|
||||
- [Infrastructure Layer](./layers/infrastructure.md)
|
||||
- [Reference Data Layer](./layers/refdata.md)
|
||||
- [Providers Layer](./layers/providers.md)
|
||||
- [View Layer](./layers/view.md)
|
||||
- [Shared Layer](./layers/shared.md)
|
||||
- [File & Folder Naming Convention](./naming.md)
|
||||
Reference in New Issue
Block a user