Derived Modular Arch
Гайды

Vite + React

Канонический запускаемый-пример DMA на Vite + React.

Это эталонный пример DMA: запускаемый mini-shop, полный граф импортов, размещённый рядом stores, promotion cart → services/. Остальные гайды по фреймворкам — вариации той же истории.

Исходники: examples/vite-react.

Корень композиции

DMA считает src/app/ корень композиции — аналог pages/ или routes/ в других стеках.

src/app/
├── app.tsx         # nav, badges, монтирование features, связка через props
├── providers.tsx   # ThemeProvider из shared/ui
└── app.css         # глобальные стили только здесь

app.tsx импортирует только */public/*:

import { CatalogPage } from "@/features/catalog/public/catalog-page";
import { addNotification } from "@/features/notifications/public/notifications-api";

<CatalogPage
  onAddedToCart={(name) => addNotification(`Added ${name}`)}
/>

Склейка catalog → notifications через props — см. Склейка модулей.

Mini-shop: кто что владеет

МодульВладелец состоянияИмпортирует
Catalogcatalog.store.ts (фильтры)services/cart, shared/*
Checkoutcheckout.store.ts (доставка)services/cart, shared/*
Notificationsnotifications.store.tsshared/*
Cartservices/cart/public/cart.tsshared/model
Profileстадия-0 profile.tsxshared/ui

Нет ребра catalog → notifications — только app → оба модуля.

Promotion: checkout → cart

Корзина не начиналась в services/. Когда catalog тоже понадобился addCartItem, cart промоутили:

До (1 потребитель)              После (2 потребителя)
features/checkout/              features/catalog/public/ ──┐
  checkout.store (корзина)      features/checkout/public/ ─┼──► services/cart/public/cart.ts
                                checkout.store (только доставка)

dma check видит входящие рёбра и подтверждает, что cart — это services/, а не внутренность feature.

Куда класть код

ЗадачаПапкаПример
Экран, монтируемый из appfeatures/<name>/public/ или стадия-0 features/<name>.tsxcatalog-page.tsx
Состояние одного featureразмещённый рядом *.store.tscheckout.store.ts
Сценарий для 2+ модулейservices/<name>/public/cart.ts
UI-примитив без продуктаshared/ui/button.tsx
Чистый хелперshared/lib/format-currency.ts
Тип для 2+ модулейshared/model/product.ts
Провайдерыapp/providers.tsxThemeProvider

Data fetching

СлойФайлРоль
Транспортshared/api/http.tsТонкий get<T>()
Feature APIfeatures/catalog/catalog.api.tsТолько catalog импортирует
Демо-данныеpublic/catalog.jsonVite static

Другие features не импортируют catalog.api.ts. Общий HTTP — через shared/api/http.ts.

Стили

ПодходГдеПример
Global CSSapp/app.cssreset, shell
CSS Modulesрядом с public/ entrycatalog-page.module.css
Plain CSSразмещённый рядом в featurecheckout-page.css
SCSS moduleстадия-0 featureprofile.module.scss
CSS-in-JSinternal featurenotifications.styles.ts

Глобальные стили — только в app/. Feature-specific скины не в shared/.

Тесты

Размещённый рядом *.test.ts рядом с кодом (Vitest):

  • catalog.store.test.ts — internal store
  • services/cart/public/cart.test.ts — public API сервиса
  • shared/lib/format-currency.test.ts — portable helper

E2E (не в примере) — в корне app (e2e/).

Команды

bun run dev
bun run build
bun run dma-check   # dma check .
bun run lint        # ESLint + @derived-modular/eslint-plugin
bun run test

Что дальше

On this page