Files
admin/docs/apps-architecture/react-frontend/Readme.md
T
2026-06-25 15:53:49 +03:00

17 KiB
Raw Blame History

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

  • 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.
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.

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