+ ))}
+
+ );
+};
+```
+
+---
+
+#### Форматирование props для компонентов
+
+##### ❌ Invalid
+```tsx
+export const Card = () => {
+ const [{ name, list }] = useState(createUIStore);
+
+ return (
+
+ description)}
+ />
+
+ );
+};
+```
+
+##### ✅ Valid
+```tsx
+export const Card = () => {
+ const [{ viewerTitle, descriptions }] = useState(createUIStore);
+
+ return (
+
+
+
+ );
+};
+```
+
+#### Расчет флагов, отвечающих за отображение частей ui
+
+##### ❌ Invalid
+```tsx
+export const Card = ({ name, isOwner }: Props) => {
+ const isShowTitle = Boolean(name) && isOwner;
+
+ return {isShowTitle && Заголовок};
+};
+```
+
+##### ✅ Valid
+```tsx
+export const Card = ({ name, isOwner }: Props) => {
+ const [{ isShowTitle }] = useState(() => createUIStore({ name, isOwner }));
+
+ return {isShowTitle && Заголовок};
+};
+
+```
+
+### ✅ В компоненте может находится
+
+ℹ️ Все нижеперечисленные ниже функции могут находиться как в компоненте, так и в `useLogic`, если `useLogic` определен.
+
+#### Получение ref и передача HTMLElement в логику для последующей обработки
+
+Пример:
+```tsx
+export const Card = () => {
+ const [{ setContainer }] = useState(createUIStore);
+
+ const containerRef = useRef();
+
+ useEffect(() => {
+ setContainer(containerRef.current);
+ }, []);
+
+ return (
+
+ ...
+
+ );
+};
+```
+
+#### Подвязка логики к методам жизненного цикла компонента
+
+Пример:
+```tsx
+export const Card = () => {
+ const [{ mount, unmount }] = useState(createUIStore);
+
+ useEffect(() => {
+ mount();
+
+ return unmount;
+ }, []);
+
+ return ...;
+};
+```
+
+#### Определение обработчиков событий
+
+Пример:
+```tsx
+export const Input = () => {
+ const [{ value, changeValue }] = useState(createUIStore);
+
+ const handleChange = (event: ChangeEvent) => {
+ changeValue(event.target.value);
+ };
+
+ return ;
+};
+```
+
+Примечание: SyntheticEvent являются частью React, поэтому проникновение events в слой логики нежелательно.
+
+#### Определение render функций
+
+Пример:
+```tsx
+export const Status = () => {
+ const [{ statusType, info }] = useState(createUIStore);
+
+ const renderStatus = () => {
+ switch (statusType) {
+ case StatusType.Success:
+ return ;
+ case StatusType.Error:
+ return ;
+ default:
+ return ;
+ }
+ };
+
+ return {renderStatus()};
+};
+```
+
+**UI компонент потребляет логику фичи и полностью зависит от ее интерфейсов.**
+
+## Логика
+
+В рамках `features` бизнес-логика и ui логика не разделяется.
+
+Логика в `feature` содержит:
+
+- Формирование данных для отображения в ui
+- Работу с данными, взаимодействие с `Data` слоем
+- Работу с флагами, которые в компоненте будут ответственны за отображение компонента (например, удаление из DOM, изменение цвета и т.п.)
+
+**Логика фичи не должна зависеть от ui компонента**. **Зависимости направлены от ui к логике:**
+
+![UILogic](../../images/ui-logic.png)
+
+Логика может быть реализована на любом предпочтительном стэке с использованием:
+
+- state manager
+- hook (React стэк)
+- service
+- utils
+
+### Мотивация объединения бизнес и ui логики в фиче
+
+- Если оставлять ui логику в компоненте, то велика вероятность просачивания бизнес логики в компонент
+- В реальном проекте зачастую достаточно сложно решить что относится к бизнес логике, а что относится к ui. Возникают ситуации, когда разработчики замедляются в реализации фичи из-за дилеммы: куда поместить эту логику? В компонент или в store?
+
+### Реализация UI логики
+
+Для реализации логики рекомендуется использовать state manager в [UIStore](./UIStore).
+
+State manager позволит:
+
+- Не завязываться на специфику ui фреймворка
+- Избежать нежелательных зависимостей от ui. Технически невозможно в state manager поместить специфику ui фреймворка
+- Писать простые тесты для логики
+
+Использование react hooks допустимо для реализации логики в [useLogic](./useLogic), но желательно избегать данного сценария.
+Так как hooks - это часть react, то в них доступны все методы по работе с ui, это значит в логику проникнет специфика фреймворка.
+
+Из этого могут возникнуть проблемы:
+
+- Невозможность переиспользования логики в другом стэке
+- Смешивание ui и логики. Без контроля в hooks будет попадать логика работы с ref, react событиями и т.п.
+- Невозможность переиспользования логики из-за косвенной зависимости от ui
+- Сложность работы с глобальными данными
+- Сложность тестирования. Для тестирования hooks необходимы дополнительные инструменты (react-testing-library, jsdom | happydom)
diff --git a/docs/arch/modules/features/UILogic/_category_.json b/docs/arch/modules/features/UILogic/_category_.json
deleted file mode 100644
index a2863bb..0000000
--- a/docs/arch/modules/features/UILogic/_category_.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "label": "UILogic",
- "position": 1,
- "link": {
- "type": "generated-index"
- }
-}
diff --git a/docs/arch/modules/features/UILogic/overview.md b/docs/arch/modules/features/UILogic/overview.md
deleted file mode 100644
index ca5bc3a..0000000
--- a/docs/arch/modules/features/UILogic/overview.md
+++ /dev/null
@@ -1,143 +0,0 @@
----
-sidebar_position: 0
----
-
-# Отделение логики от view
-
-UI компонент должен быть ответственным **только за отображение,** количество ui логики в компоненте должно быть сведено к нулю.
-
-Вся логика реализуется вне компонента в [`UIStore`](../UIStore/overview) или [`useLogic`](../useLogic/overview).
-
-![UILogic](../../../images/ui-logic.png)
-
-## Мотивация
-
-Отделение view слоя от логики дает следующие преимущества:
-
-- Возможность изменять логику и ui независимо
-- Простота переиспользования логики или ui по необходимости
-- Независимость от используемого фреймворка. Фреймворк при определенных обстоятельствах можно заменить, а логику переиспользовать
-- Простота тестирования. Можно тестировать отдельно логику и ui
-- Однозначность расположения логики. Вся логика всегда находится в одном месте
-- Логика не “размазывается” по компонентам. Избавляет от сложностей в поддержке кода
-- Повышение читаемости кода
-- Упрощение поддержки и доработки приложения
-
-## UI компонент
-
-Компонент должен содержать только то, что непосредственно связано с фреймворком, ответственным за отображение.
-
-В ui компоненте **не должно** находится:
-
-- Логики форматирования данных для отображения
-- Логики работы с данными
-- Флагов, отвечающих за отображение частей ui
-
-В ui компоненте **должно находится**:
-
-- Работа с браузерным API
-- Работа со спецификой фреймворка
- - Обработка пользовательского ввода с целью передачи данных в логику
- - Работа с DOM
-- Потребление данных из логики для их отображения
-- Коннект методов логики с обработкой пользовательского ввода и браузерных событий
-
-**UI компонент потребляет логику фичи и полностью зависит от ее интерфейсов.**
-
-## Логика
-
-В рамках `features` бизнес-логика и ui логика не разделяется.
-
-Логика в `feature` содержит:
-
-- Формирование данных для отображения в ui
-- Работу с данными, взаимодействие с `Data` слоем
-- Работу с флагами, которые в компоненте будут ответственны за отображение компонента (например, удаление из DOM, изменение цвета и т.п.)
-
-**Логика фичи не должна зависеть от ui компонента**. **Зависимости направлены от ui к логике:**
-
-![UILogic](../../../images/ui-logic.png)
-
-Логика может быть реализована на любом предпочтительном стэке с использованием:
-
-- state manager
-- hook (React стэк)
-- service
-- utils
-
-### Мотивация объединения бизнес и ui логики в фиче
-
-- Если оставлять ui логику в компоненте, то велика вероятность просачивания бизнес логики в компонент
-- В реальном проекте зачастую достаточно сложно решить что относится к бизнес логике, а что относится к ui. Возникают ситуации, когда разработчики замедляются в реализации фичи из-за дилеммы: куда поместить эту логику? В компонент или в store?
-
-### State manager для реализации логики
-
-Для реализации логики рекомендуется использовать state manager.
-
-State manager позволит:
-
-- Не завязываться на специфику ui фреймворка
-- Избежать нежелательных зависимостей от ui. Технически невозможно в state manager поместить специфику ui фреймворка
-- Писать простые тесты для логики
-- Простота распространения данных в приложении
-
-### Использование react hooks для реализации логики
-
-Желательно избегать использования react hooks для реализации логики. Так как hooks - это часть react, то в них доступны все методы по работе с ui, это значит в логику проникнет специфика фреймворка.
-
-Из этого могут возникнуть проблемы:
-
-- Невозможность переиспользования логики в другом стэке
-- Смешивание ui и логики. Без контроля в hooks будет попадать логика работы с ref, react событиями и т.п.
-- Невозможность переиспользования логики из-за косвенной зависимости от ui
-- Сложность работы с глобальными данными
-- Сложность тестирования. Для тестирования hooks необходимы дополнительные инструменты (react-testing-library, jsdom | happydom)
-
-### Переиспользование логики между фичами
-
-Логику необходимо выносить в `Domain` , если логику, реализованную внутри фичи, потребовалось:
-
-- Переиспользовать в другой фиче
-- Переиспользовать в другом модуле
-- Использовать для интеграции с другой фичей
-
-[Подробный обзор Domain](../domain)
-
-### Использование DI для контроля зависимостей
-
-Логика должна использовать базовую концепцию DI (dep. injection) для того, чтобы контролировать свои зависимости.
-
-Плюсы подхода:
-
-- Логику проще поддерживать за счет того, что нет скрытых зависимостей. Все зависимости сразу видны и очевидны
-- Логику проще тестировать. Зависимости можно просто подменять на тестовые сущности
-
-**Пример**
-
-```tsx
-import { makeAutoObservable } from 'mobx';
-import { CartStore } from '@astral/modules/cart';
-
-export class CatalogStore {
- constructor(private readonly cartStore: CartStore) {
- makeAutoObservable(this, {}, { autoBind: true });
- }
-
- addToCart = (productID: string) => {
- this.cartStore.add(productID);
- };
-}
-```
-
-## Демонстрация профита отделения ui и логики
-
-Представим, что у нас есть блок, в котором происходит оплата услуги по карте.
-
-Данный блок является фичей `CardPayment` в модуле `Payment`.
-
-Сейчас фича производит оплату через конкретную платежную систему после успешной оплаты отправляем нашему API данные.
-
-Через некоторое время появилась необходимость реализовать новую фичу, ui которой должен повторять `CardPayment`, но при этом должна использоваться другая платежная система и после оплаты данные отправляются на другое API.
-
-Так как мы сразу отделили логику и ui компонент, то мы можем без особых проблем вынести исходную логику `CardPayment` в `Domain` и использовать для `CardPayment` один и тот же ui, но разную логику.
-
diff --git a/docs/arch/modules/features/UIStore.md b/docs/arch/modules/features/UIStore.md
new file mode 100644
index 0000000..1fc854b
--- /dev/null
+++ b/docs/arch/modules/features/UIStore.md
@@ -0,0 +1,420 @@
+---
+sidebar_position: 2
+---
+
+# UIStore
+
+`UIStore` - это логика фичи, реализованная с помощью state manager.
+
+`UIStore` можно рассматривать как **View-Model** из паттерна **MVVM** или [Supervising Controller](https://www.martinfowler.com/eaaDev/SupervisingPresenter.html).
+
+Рекомендуется отдавать предпочтение реализации логики через `UIStore` перед `useLogic`. Причины:
+- Возможность упрощения реактивной логики за счет использования state manager
+- Более простые тесты для логики
+- Меньшая связь со спецификой ui фреймворка
+
+## Структура
+
+```
+├── app/
+├── screens/
+├── modules/
+| └── payment/
+| | ├── features/
+| | | ├── PaymentSwitch/
+| | | | ├── PaymentSwitch.tsx
+| | | | ├── UIStore/
+| | | | | ├── UIStore.ts
+| | | | | └── index.ts
+| | | | └── index.ts
+| | | ├── CashPayment/
+| | | └── index.ts
+| | ├── domain/
+| | └── index.ts
+├── data/
+└── shared/
+```
+
+## Style guide
+
+[Style guide | UIStore](https://kaluga-astral.github.io/style-guide/docs/rules/arch/modules/features/UIStore)
+
+## Работа с data слоем
+
+`UIStore` взаимодействует с `data` слоем для:
+- Получения данных
+- Форматирования данных для отображения в компоненте
+- Формирования флагов состояния загрузки данных для отображения в компоненте
+
+## Формирование данных для отображения
+
+### Форматирование дат для отображения
+
+```tsx
+export class UIStore {
+ constructor(private readonly params: { issueDate: Date }) {
+ makeAutoObservable(this);
+ }
+
+ public get issueDate() {
+ return this.params.issueDate.toLocaleDateString();
+ }
+}
+```
+
+```tsx
+export const Card = (props: Props) => {
+ const [{ issueDate }] = useState(() => createUIStore(props));
+
+ return (
+
+ {issueDate}
+
+ );
+};
+```
+
+---
+
+### Склеивание строк для отображения
+
+```ts
+export class UIStore {
+ constructor(private readonly params: { name: string; surname: string }) {
+ makeAutoObservable(this);
+ }
+
+ public get fullName() {
+ return `${this.params.name} ${this.params.surname}`;
+ }
+}
+```
+
+```tsx
+export const Card = (props: Props) => {
+ const [{ fullName }] = useState(() => createUIStore(props));
+
+ return (
+
+ {fullName}
+
+ );
+};
+```
+
+---
+
+### Формирование массивов или объектов
+
+```ts
+export class UIStore {
+ constructor(
+ private readonly params: { list: Array<{ name: string; surname: string }> },
+ ) {
+ makeAutoObservable(this);
+ }
+
+ public get data() {
+ return this.params.list.map(({ name, surname }) => `${name} ${surname}`);
+ }
+}
+```
+
+```tsx
+export const List = (props: Props) => {
+ const [{ data }] = useState(() => createUIStore(props));
+
+ return (
+
+ {data.map((fullName) => (
+
+ {fullName}
+
+ ))}
+
+ );
+};
+```
+
+---
+
+### Расчет флагов, отвечающих за отображение частей ui
+
+```ts
+export class UIStore {
+ constructor(private readonly params: { name?: string; isOwner: boolean }) {
+ makeAutoObservable(this);
+ }
+
+ public get isShowTitle() {
+ return Boolean(this.params.name) && this.params.isOwner;
+ }
+}
+```
+
+```tsx
+export const Card = ({ name, isOwner }: Props) => {
+ const [{ isShowTitle }] = useState(() => createUIStore({ name, isOwner }));
+
+ return {isShowTitle && Заголовок};
+};
+```
+
+---
+
+### Форматирование props для компонентов
+
+```ts
+export class UIStore {
+ constructor(private readonly userStore: UserStore) {
+ makeAutoObservable(this);
+ }
+
+ public get viewerTitle() {
+ const { name } = this.userStore;
+
+ return `Подробная информация о ${name}`;
+ }
+
+ public get descriptions() {
+ return this.userStore.descriptions.map(({ text }) => text);
+ }
+}
+```
+
+```tsx
+export const Card = () => {
+ const [{ viewerTitle, descriptions }] = useState(createUIStore);
+
+ return (
+
+
+
+ );
+};
+```
+
+## UIStore не должен зависеть от props компонента текущей фичи
+
+Если `UIStore` будет зависеть от props компонента текущей фичи, то возникнут циклические зависимости.
+
+Типы `UIStore` могут зависеть от:
+- Props компонентов других фичей
+- Props shared компонентов
+- Domain любых модулей
+- Data слоя
+
+![PropsDeps](../../images/props-deps.png)
+
+[Про формирование props для компонента фичи](./props).
+
+## Отслеживание изменений props компонента
+
+Зачастую в `UIStore` необходимо отслеживать изменения props текущей фичи.
+Для этого необходимо в компоненте через `useEffect` точечно подписываться на изменение конкретных props и передавать их в `UIStore`:
+
+```tsx
+const FullName = ({ name, surname }: Props) => {
+ const [{ fullName, updateUserInfo }] = useState(() =>
+ createUIStore({ name, surname }),
+ );
+
+ useEffect(() => {
+ updateUserInfo({ name, surname });
+ }, [name, surname]);
+
+ return {fullName};
+};
+```
+
+## Render компонентов в store
+
+[Modules Guides | Render компонентов в store](../../guides/renderComponentInStore).
+
+## Проброс ссылок на ref
+
+В `UIStore` допустимо пробрасывать `ref` для передачи ссылок в компоненты или сервисы:
+
+```ts
+import type { Ref } from 'react';
+
+export class UIStore {
+ private containerRef?: Ref;
+
+ constructor(private readonly scroller: Scroller) {
+ makeAutoObservable(this);
+ }
+
+ public setContainerRef = (ref: Ref) => {
+ this.scroller.setScrollContainer(ref);
+ };
+}
+```
+
+## Подвязка на mount и unmount компонента
+
+```ts
+import { autorun, makeAutoObservable } from 'mobx';
+import type { Ref } from 'react';
+
+export class UIStore {
+ private unobserveSearch: () => void = () => {};
+
+ public search: string = '';
+
+ constructor(
+ private readonly listStore: ListStore,
+ private readonly scroller: Scroller,
+ ) {
+ makeAutoObservable(this);
+ }
+
+ private observeSearch = () =>
+ autorun(() => {
+ this.listStore.changeParams({ search: this.search });
+ });
+
+ public setSearch = (search: string) => {
+ this.search = search;
+ };
+
+ public get list() {
+ return this.listStore.data;
+ }
+
+ public mount = (containerRef: Ref) => {
+ this.scroller.setScrollContainer(containerRef);
+ this.unobserveSearch = this.observeSearch();
+ };
+
+ public unmount = () => {
+ this.unobserveSearch();
+ };
+}
+```
+
+```tsx
+const List = () => {
+ const containerRef = useRef();
+
+ const [{ mount, unmount }] = useState(createUIStore);
+
+ useEffect(() => {
+ mount(containerRef);
+
+ return unmount;
+ }, []);
+
+ ...
+};
+```
+
+### Методы, связанные с mount и unmount компонента должны называться соответственно
+
+##### ✨ Мотивация
+
+Однозначная связь жизненного цикла компонента и названий методов `UIStore`.
+
+##### ✅ Valid
+
+```tsx
+const List = () => {
+ const containerRef = useRef();
+
+ const [{ mount, unmount }] = useState(createUIStore);
+
+ useEffect(() => {
+ mount(containerRef);
+
+ return unmount;
+ }, []);
+
+ ...
+};
+```
+
+##### ❌ Invalid
+
+```tsx
+const List = () => {
+ const containerRef = useRef();
+
+ const [{ init, destroy }] = useState(createUIStore);
+
+ useEffect(() => {
+ init(containerRef);
+
+ return destroy;
+ }, []);
+
+...
+};
+```
+
+## Работа с Browser API через абстракцию
+
+Работа с Browser API необходимо проводить через абстракцию.
+
+##### ✨ Мотивация
+
+Позволяет писать упрощенные тесты за счет использования тестовых зависимостей вместо реальных.
+
+Примеры:
+```ts
+export class UIStore {
+ constructor(
+ private readonly storage: LocalStorageService,
+ ) {
+ makeAutoObservable(this);
+ }
+
+ public setSearch = (search: string) => {
+ this.storage.setItem('search', search)
+ }
+}
+```
+
+```ts
+export class UIStore {
+ constructor(
+ private readonly intersectionObserver: IntersectionObserver,
+ ) {
+ makeAutoObservable(this);
+ }
+
+ ...
+
+ public mount = (itemRef: Ref) => {
+ this.intersectionObserver(this.showAction, { root: itemRef.current })
+ }
+}
+```
+
+## Все входные зависимости UIStore должны быть инвертированы через DI
+
+UIStore должен использовать базовую концепцию DI (dep. injection) для того, чтобы контролировать свои зависимости.
+
+Плюсы подхода:
+
+- Логику проще поддерживать за счет того, что нет скрытых зависимостей. Все зависимости сразу видны и очевидны
+- Логику проще тестировать. Зависимости можно просто подменять на тестовые сущности
+
+**Пример**
+
+```tsx
+import { makeAutoObservable } from 'mobx';
+import { CartStore } from '@astral/modules/cart';
+
+export class CatalogStore {
+ constructor(private readonly cartStore: CartStore) {
+ makeAutoObservable(this, {}, { autoBind: true });
+ }
+
+ addToCart = (productID: string) => {
+ this.cartStore.add(productID);
+ };
+}
+```
diff --git a/docs/arch/modules/features/UIStore/_category_.json b/docs/arch/modules/features/UIStore/_category_.json
deleted file mode 100644
index 9291981..0000000
--- a/docs/arch/modules/features/UIStore/_category_.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "label": "UIStore",
- "position": 1,
- "link": {
- "type": "generated-index"
- }
-}
diff --git a/docs/arch/modules/features/UIStore/overview.md b/docs/arch/modules/features/UIStore/overview.md
deleted file mode 100644
index e69de29..0000000
diff --git a/docs/arch/modules/features/UIStore/renderComponentInStore.md b/docs/arch/modules/features/UIStore/renderComponentInStore.md
deleted file mode 100644
index 117857b..0000000
--- a/docs/arch/modules/features/UIStore/renderComponentInStore.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-sidebar_position: 1
----
-
-# Render компонентов в store
diff --git a/docs/arch/modules/features/dependencies.md b/docs/arch/modules/features/dependencies.md
index 6cb764f..9feb946 100644
--- a/docs/arch/modules/features/dependencies.md
+++ b/docs/arch/modules/features/dependencies.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 2
+sidebar_position: 5
---
# Зависимости фичей
diff --git a/docs/arch/modules/features/domain.md b/docs/arch/modules/features/domain.md
index e69de29..e5a58a3 100644
--- a/docs/arch/modules/features/domain.md
+++ b/docs/arch/modules/features/domain.md
@@ -0,0 +1,9 @@
+---
+sidebar_position: 7
+---
+
+# Domain
+
+## Render компонента в store
+
+[Modules Guides | Render компонентов в store](../guides/renderComponentInStore).
diff --git a/docs/arch/modules/features/props.md b/docs/arch/modules/features/props.md
new file mode 100644
index 0000000..e24bdb6
--- /dev/null
+++ b/docs/arch/modules/features/props.md
@@ -0,0 +1,23 @@
+---
+sidebar_position: 6
+---
+
+# Формирование props компонента фичи
+
+Props фичи может формироваться из:
+- Типов `UIStore`
+- Типов DTO из `data`
+- Props других фичей
+- Props shared компонентов
+
+```ts
+type Props = {
+ data: UIStore['data'];
+ list: RequestsRepositoryDTO.List;
+ onClick: ButtonProps['onClick'];
+};
+```
+
+![PropsDeps](../../images/props-deps.png)
+
+**`UIStore` при этом не зависит от props компонента своей фичи**.
diff --git a/docs/arch/modules/features/shared-logic.md b/docs/arch/modules/features/shared-logic.md
new file mode 100644
index 0000000..9787111
--- /dev/null
+++ b/docs/arch/modules/features/shared-logic.md
@@ -0,0 +1,13 @@
+---
+sidebar_position: 4
+---
+
+# Переиспользование логики между фичами
+
+Логику необходимо выносить в `Domain` , если логику, реализованную внутри фичи, потребовалось:
+
+- Переиспользовать в другой фиче
+- Переиспользовать в другом модуле
+- Использовать для интеграции с другой фичей
+
+[Подробный обзор Domain](./domain)
diff --git a/docs/arch/modules/features/style-guide.md b/docs/arch/modules/features/style-guide.md
index 6db9939..09e50eb 100644
--- a/docs/arch/modules/features/style-guide.md
+++ b/docs/arch/modules/features/style-guide.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 3
+sidebar_position: 8
---
# Style Guide
diff --git a/docs/arch/modules/features/testing.md b/docs/arch/modules/features/testing.md
index 0952fd5..57904dc 100644
--- a/docs/arch/modules/features/testing.md
+++ b/docs/arch/modules/features/testing.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 4
+sidebar_position: 9
---
# Тестирование
diff --git a/docs/arch/modules/features/useLogic.md b/docs/arch/modules/features/useLogic.md
new file mode 100644
index 0000000..7195807
--- /dev/null
+++ b/docs/arch/modules/features/useLogic.md
@@ -0,0 +1,230 @@
+---
+sidebar_position: 3
+---
+
+# useLogic
+
+`useLogic` предназначен для реализации логики фичи, сильно зацепленной на используемую react-библиотеку и фичи самого react.
+Если логику можно реализовать без использования хука, то стоит отдать предпочтение [UIStore](./UIStore).
+
+```
+├── PaymentForm/
+| ├── UIStore/
+| ├── useLogic/
+| | |── utils/
+| | |── hooks/
+| | |── useLogic.ts
+| | |── useLogic.test.ts
+| | |── constants.ts
+| | |── enums.ts
+| | |── types.ts
+| | └── index.ts
+| ├── PaymentForm.tsx
+| └── index.ts
+```
+
+## useLogic - единственная точка входа данных для компонента
+
+Если в фиче есть `useLogic`, то только из него должны потребляться данные для компонента.
+Даже если в фиче используется UIStore или другие stores:
+
+```tsx
+const Card = (props: Props) => {
+ const [uiStore] = useState(() => createUIStore(props));
+
+ const { fullName, isShowDescription, description } = useLogic(uiStore);
+
+ return (
+
+ {fullName}
+ {isShowDescription && {description}}
+
+ );
+};
+```
+
+```ts
+export const useLogic = (store: UIStore) => {
+ const isShowDescription = useEndScroll();
+
+ return {
+ isShowDescription,
+ fullName: store.fullName,
+ description: store.fullName,
+ };
+};
+```
+
+Hook должен возвращать абстрактный интерфейс, с которым работает компонент.
+
+Благодаря этой абстракции появляется возможность изменить инструмент реализации логики, при этом оставить выходной интерфейс и ui неизменными.
+
+## Взаимодействие с UIStore
+
+`useLogic` должен принимать `UIStore` и другие stores параметром по ссылке для возможности более простого тестирования:
+```ts
+export const useLogic = (store: UIStore) => {
+ const isShowDescription = useEndScroll();
+
+ return {
+ isShowDescription,
+ fullName: store.fullName,
+ description: store.fullName,
+ };
+};
+```
+
+### Инициализация UIStore
+
+`UIStore` при использовании `useLogic` инициализируется в компоненте и передается в `useLogic`:
+```tsx
+const Card = (props: Props) => {
+ const [uiStore] = useState(createUIStore);
+
+ const { fullName, isShowDescription, description } = useLogic(uiStore);
+
+ return (
+
+ {fullName}
+ {isShowDescription && {description}}
+
+ );
+};
+```
+
+### Зависимости
+
+`UIStore` не должен зависеть от `useLogic`:
+
+![LogicDeps](../../images/logic-deps.png)
+
+Типы должны импортироваться из `UIStore` и `useLogic`.
+А в свою очередь компонент для формирования своих props может использовать типы как из `useLogic`, так и из `UIStore`.
+
+## Разделение зон ответственности между UIStore и useLogic
+
+### Зона ответственности store
+
+- Работа с данными. Взаимодействие с слоем `data`
+- Форматирование данных для отображения, если эти данные не завязаны на изменение state формы
+
+## Зона ответственности hooks
+
+### Описание типов формы
+
+`useLogic` должен содержать типы формы:
+
+```tsx
+export type BookFormValues = {
+ name: string;
+ genre: BookRepositoryDTO.GenreDTO;
+ pageCount: string;
+ author: AdministrationRepositoryDTO.CreateBookInputDTO['author'];
+ coAuthor?: AdministrationRepositoryDTO.CreateBookInputDTO['coAuthor'];
+ isPresentCoAuthor: boolean;
+};
+```
+
+### Валидация формы
+
+`useForm` должен взаимодействовать с валидацией формы:
+
+```tsx
+const validationSchema = v.object({
+ name: v.string(),
+ genre: v.object({
+ id: v.string(),
+ name: v.string(),
+ description: v.optional(v.string()),
+ }),
+ pageCount: v.number(),
+ author: v.object({
+ name: v.string(),
+ surname: v.string(),
+ }),
+ isPresentCoAuthor: v.optional(v.boolean()),
+ coAuthor: v.when({
+ is: (_, ctx) => Boolean(ctx.values?.isPresentCoAuthor),
+ then: v.object({
+ name: v.string(),
+ surname: v.string(),
+ }),
+ otherwise: v.any(),
+ }),
+});
+
+export const useLogic = () => {
+ const form = useForm({ validationSchema });
+
+ ...
+
+};
+```
+
+### Инициализация формы с необходимыми параметрами
+
+```tsx
+export const useLogic = (): Result => {
+ const form = useForm({ validationSchema });
+
+ ...
+
+};
+```
+
+### Подписка на изменение полей и state формы
+
+```tsx
+export const useLogic = (
+ store: UIStore,
+ { onSubmit }: Params,
+): Result => {
+ const form = useForm({ validationSchema });
+
+ const isPresentCoAuthor = form.watch('isPresentCoAuthor');
+
+ const name = form.watch('name');
+
+ useEffect(() => {
+ store.findBook(name);
+ }, [name]);
+
+ return { form, isPresentCoAuthor, submit: form.handleSubmit(onSubmit) };
+};
+```
+
+### Формирование данных для отображения
+
+Если данные для отображения завязаны на изменение state формы, то логика форматирования помещается в хук:
+
+```tsx
+export const useLogic = (): Returned => {
+ const { watch } = useBookFormContext();
+
+ const { name, author } = watch();
+
+ return {
+ name,
+ authorFullName: `${author.name} ${author.surname}`,
+ };
+};
+```
+
+## При использовании useLogic в компоненте не должно оставаться никакой логики, кроме инициализации
+
+В `useLogic` переносятся из компонента:
+- Подвязка на `mount`, `unmount`
+- Создание `ref`
+- Обработка событий
+
+## Style guide
+
+[Style Guide | useLogic](https://kaluga-astral.github.io/style-guide/docs/rules/arch/modules/features/logic-hook)
+
+## Тестирование
+
+Тестировать store и hook необходимо отдельно.
+
+## Пример
+
+[Vite-boilerplate](https://github.com/kaluga-astral/vite-boilerplate/tree/main/modules/administration/features/BookForm)
diff --git a/docs/arch/modules/features/useLogic/overview.md b/docs/arch/modules/features/useLogic/overview.md
deleted file mode 100644
index e69de29..0000000
diff --git a/docs/arch/modules/features/useLogic/_category_.json b/docs/arch/modules/guides/_category_.json
similarity index 69%
rename from docs/arch/modules/features/useLogic/_category_.json
rename to docs/arch/modules/guides/_category_.json
index fe1b4dd..1cfc37c 100644
--- a/docs/arch/modules/features/useLogic/_category_.json
+++ b/docs/arch/modules/guides/_category_.json
@@ -1,5 +1,5 @@
{
- "label": "useLogic",
+ "label": "Modules Guides",
"position": 2,
"link": {
"type": "generated-index"
diff --git a/docs/arch/modules/guides/renderComponentInStore.md b/docs/arch/modules/guides/renderComponentInStore.md
new file mode 100644
index 0000000..4bad059
--- /dev/null
+++ b/docs/arch/modules/guides/renderComponentInStore.md
@@ -0,0 +1,136 @@
+# Render компонентов в store
+
+Производить render компонента в store необходимо в случаях:
+- Проброс кастомного отображения в сервис
+- Формирование props для компонента
+
+## adaptComponentToDomain
+
+`adaptComponentToDomain` позволяет вызывать рендер компонента вне `jsx`, а именно в `UIStore`.
+
+Реализация функции:
+```ts
+import { type FunctionComponent, type ReactNode, createElement } from 'react';
+
+export type RenderComponentInDomain = (
+ props: TProps,
+) => ReactNode;
+
+/**
+ * Позволяет использовать react-компонент в бизнес-логике как render функцию
+ */
+export const adaptComponentToDomain =
+ (
+ component: FunctionComponent,
+ ): RenderComponentInDomain =>
+ (props) =>
+ createElement(component, props);
+```
+
+## Проброс render в service. Пример с notify
+
+Задача: при вызове notify использовать кастомное отображение сообщения.
+
+```ts
+import { PaymentMessage } from '../../features';
+
+export class PaymentStore {
+ constructor(
+ private readonly notify: Notify,
+ private readonly paymentRepo: PaymentRepo,
+ private readonly renderPaymentMessage: RenderComponentInDomain<{
+ productID: string;
+ }>,
+ ) {
+ makeAutoObservable(this);
+ }
+
+ public pay = async (productID: string) => {
+ await this.paymentRepo.pay(productID);
+
+ this.notify.success('Оплачено', {
+ content: this.renderPaymentMessage({ productID }),
+ });
+ };
+}
+
+export const createPaymentStore = () =>
+ new PaymentStore(
+ notifyService,
+ paymentRepository,
+ adaptComponentToDomain(PaymentMessage),
+ );
+```
+
+## Формирование props для компонента
+
+```ts
+import { DeleteIcon, EditIcon } from '@example/shared';
+import type { ActionCellProps } from '@example/shared';
+
+type ActionIcons = {
+ renderEdit: RenderComponentInDomain;
+ renderDelete: RenderComponentInDomain;
+};
+
+export class UIStore {
+ constructor(private readonly actionIcons: ActionIcons) {
+ makeAutoObservable(this);
+ }
+
+ public get actions(): ActionCellProps {
+ return [
+ {
+ icon: this.actionIcons.renderDelete(),
+ onClick: () => this.delete(),
+ },
+ {
+ icon: this.actionIcons.renderEdit(),
+ onClick: () => this.edit(),
+ },
+ ];
+ }
+}
+
+export const createUIStore = () =>
+ new UIStore({
+ renderDelete: adaptComponentToDomain(DeleteIcon),
+ renderEdit: adaptComponentToDomain(EditIcon),
+ });
+```
+
+## Сохранение ссылки на ReactNode для последующего использования
+
+```ts
+import type { ReactNode } from 'react';
+
+export class UIStore {
+ private alertMessage: ReactNode;
+
+ constructor(private readonly notify: Notify) {
+ makeAutoObservable(this);
+ }
+
+ public mount = (message: ReactNode) => {
+ this.alertMessage = message;
+ };
+
+ public send = () => {
+ this.notify(this.alertMessage);
+ };
+}
+```
+
+```tsx
+const Alert = () => {
+ const [{ mount }] = useState(createUIStore);
+
+ const message = Hello;
+
+ useEffect(() => {
+ mount(message);
+ }, []);
+
+ ...
+};
+```