Всем привет, меня зовут Павел Рожков, я lead фронтенда в компании Doubletapp. Мы занимаемся заказной разработкой, и в нашей работе над React-проектами важную роль играет наш архитектурный гайдлайн, который мы постоянно совершенствуем. Это свод договоренностей о том, каким образом будет организован код в нашем проекте.
Гайдлайн помогает нам:
Безболезненно менять состав команд на проектах между собой. Каждый может заменить коллегу или усилить команду, минуя этап долгого онбординга.
Сократить время разработки. У нас часто не возникает вопроса, как здесь сделать лучше, куда что положить, как организовать. Мы подумали об этом заранее.
Поддерживать старые проекты, т.к. они написаны по тем же принципам.
Поднять качество кода: работать на проекте становится удобнее и можно сосредоточиться на важных вещах.
Онбордить новых членов команды благодаря готовой документации.
Содержание:
Почему бы нам просто не взять FSD?
Я часто общался с людьми в комьюнити по поводу архитектуры их проектов, и многие из них описывают свои гайдлайны как некий гибрид FSD и того, что каждой команде показалось удобно внедрить в их конкретном случае. И это правильно, ведь архитектура — это инструмент, который должен выполнять задачи бизнеса, а задачи бывают разные. Подробнее эту мысль раскрывает Глеб Михеев в своем докладе «Рефлексия как метод проектирования архитектуры». .
Также FSD имеет довольной высокий порог вхождения. Одной из причин этого является то, что FSD предлагает новую парадигму мышления, когда ты должен строить свой проект во многом вокруг Сущностей (Entities), которые иногда даже могут не содержать в себе визуальное представление (UI). Мы же предлагаем строить приложения более прямолинейно, как мы все привыкли, вокруг Интерфейсов.
Шаблон проекта с архитектурой
Ниже мы рассмотрим подробнее нашу методологию, но со временем она может претерпевать изменения. Поэтому мы подготовили публичный репозиторий, который представляет собой шаблон для создания приложения на ее основе, а также содержит все необходимые настройки проекта и актуальный для нас стек. Мы будем поддерживать его в актуальном состоянии, и вы можете использовать его для своих проектов.
Структура основного кода приложения (обычно это каталог /src)
Api
App
Assets
-
Components
Pages
Wrapper
Layouts
Widgets
Dummies
UI
Constants
Hooks
Models
Stores
Styles
Utils
Ниже мы поговорим подробнее про каждую категорию.
/Components
Наверное один из ключевых вопросов в архитектуре React-проектов — по какому принципу мы делим наш интерфейс на компоненты. Мы предлагаем следующий подход.
Мы имеем 6 типов компонентов:
Pages
Widgets
Dummies
UI
Wrappers (HOCs)
Layouts
Создавая новую страницу (в Pages), мы думаем, какие из ее элементов могут быть потенциально или по факту переиспользованы в нашем приложении, и создаем эти компоненты в соответствующих их типу директориях.
Остальная верстка и элементы, которые необходимы только конкретному компоненту, хранятся непосредственно в нем или в его директории. Это правило действует для всех типов компонентов.
Теперь давайте разберем подробнее, как мы определяем тип компонента:
Pages
Как можно догадаться из названия, это компоненты, содержащие в себе код, отображающий всю страницу. Этот компоненты, которые мы передаем в наш роутер. Может содержать в себе бизнес-логику, если это необходимо.
Layouts
Этот парень отвечает за шаблонные расположения элементов в нашем интерфейсе. Самый популярный пример — компонент лэйаута страницы, который содержит в себе header, footer и через children prop принимает в себя верстку, которую необходимо отобразить. Layouts также могут принимать в себя компоненты через слоты, в виде props, и располагать их, как нам необходимо.
Wrappers (HOCs)
Вспомогательные компоненты, которые используются для расширения или изменения функциональности других элементов. Пример — wrapper, который добавляет своим детям анимации при перемещении.
Widgets
Автономный, самостоятельный компонент, заключающий в себе какую-то законченную часть функциональности. Содержит в себе всю бизнес-логику, которая ему необходима. Примеры: header, форма авторизации, список товаров, баннер.
Dummies
Комплексный компонент отображения. Необходимые ему данные принимает через props. Не содержит в себе бизнес-логики, только логику отображения, например переключение видимости блоков в рамках компонента. Самый популярный пример — карточка товара. Мы передаем через props все характеристики товара, а также callback для кнопки «добавить в корзину». Тем самым получаем переиспользуемый компонент, к которому мы можем привязать любую необходимую бизнес-логику, которая может быть разной для одного и того же отображения.
UI
Базовые компоненты, из которых строится интерфейс: кнопки, инпуты, заголовки, лоадеры, тултипы и т.д.). Может содержать в себе логику отображения, иметь локальный стейт.
Правила импортов:
Наши компоненты делятся на 2 типа: имеющие иерархию и нейтральные.
Нейтральные
Layouts
Wrappers
Нет ограничений на импорты.
Иерархические (с верхнего слоя к нижнему)
Pages
Widgets
Dummies
UI
Здесь каждый компонент может быть импортирован только в любой вышестоящий. Например UI не может содержать в себе Widget. При этом Widget может как содержать в себе любые компоненты нижнего уровня, так и не содержать их вообще, а иметь только свою собственную верстку с логикой. Также допускаются импорты в рамках одного уровня.
Структура папки отдельного компонента:
Каждый компонент хранится в отдельной папке с названием в PascalCase (например MyComponent) и имеет следующую структуру:
MyComponent.tsx
Основной файл компонента, название должно дублировать название папки, в которой он лежит. Если компонент принимает props, то описание типа Props мы храним в этом файле, над объявлением компонента. Также может содержать описание других типов, если необходимо.index.ts
Файл для сокращения пути import’а компонента. Также можно использовать для единой точки входа экспортируемых материалов из этого каталога.Styles.module.scss
Файл стилей, относящихся к компоненту.types.ts (опционально)
Здесь хранятся описания типов для нашего компонента. Иногда нам необходимо иметь описание типов, которые должны использоваться в другой части приложения. Их мы тоже храним здесь. Пример — тип, описывающий HTML-форму, у которой набор полей на фронтенде не совпадает с моделью бэкенда.constants.ts (опционально)
Любые статические данные компонента.useMyComponent.ts (для компонентов с бизнес логикой)
Всю бизнес-логику компонента мы выносим в хук рядом с компонентом. Это помогает разделять зоны ответственности в коде, и читать его становится легче. Также, если теоретически нам понадобится использовать эту бизнес-логику с другим отображением, мы сможем безболезненно имплементить такой функционал, ведь у нас он уже разделен.Components (опционально)
Если мы хотим вынести часть кода в отдельный компонент, и при этом мы понимаем, что использоваться он будет только своим родителем, мы создаем его в этой директории. Структура этих компонентов такая же. Вложенность может быть любая.
Вы можете расширять этот список всеми необходимыми этому компоненту материалами. Главное, чтобы соблюдалось правило использования этих материалов только этой частью приложения.
/Models
Здесь мы храним описание типов, общих для нашего приложения. Это все описания серверных запросов и ответов, а также клиентские модели, которые не замкнуты на одном конкретном интерфейсе.
Почему все модели в одном месте?
Часто в разных частях приложения нам могут понадобиться модели разных категорий. Хранение их в одном месте позволяет нам легко использовать их в интерфейсах, где нам необходимо, а также видеть всю «карту» уже созданных и доступных типов, что снижает риск дублирования кода при разработке нового функционала.
Мы создаем папку под каждую категорию моделей:
Common
Auth
Users
И т.д.
Каждая папка содержит:
- api.ts
- client.ts
Модели из обеих этих категорий могут быть использованы в нашем приложении там, где они необходимы.
Файл api.ts
Содержит в себе представления запросов и ответов сервера.
В названиях моделей все запросы и ответы сервера помечаются суффиксами Request и Response соответственно.
Пример:
interface User {
id: string;
name: string;
bio: string;
avatar: ImageDTO;
}
export interface GetUsersRequest {
limit: number;
offset: number;
}
export interface GetUsersResponse {
count: number;
items: User[];
}
Также иногда нам требуется создать модель какой-то сущности бэкенда, которую мы используем для описания эндпоинтов из разных категорий. Например File, который может приходить нам в ответе множества разных эндпоинтов. Такую модель мы кладем в папку Common и даем ей суффикс DTO, чтобы избежать потенциального пересечения имен с моделью клиента и понимать, что тип относится к серверу.
export interface FileDTO {
id: string
fileUrl?: string
fileName?: string
fileSize?: number
}
Файл client.ts
Содержит общие модели, которые относятся только к фронтенду. Мы кладем их сюда, когда понимаем, что они потенциально могут быть использованы в нескольких частях приложения.
Пример:
export interface SelectOption {
value: string
label: string
}
NOTE: когда модель бэкенда и фронта не совпадает по стилю написания (например у нас camelCase, а у бекенда на snake_case), мы используем интерсепторы axios, чтобы приводить данные к одному виду, и условно считаем, что мы работаем только в camelCase.
В ином случае мы предлагаем под каждый запрос писать функции сериализаторы для перевода данных в нужный формат. Но как показала наша практика, это не очень удобно.
export const http = axios.create({
baseURL: BASE_URL,
headers: {
'X-API-KEY': BASE_API_KEY,
'Content-Type': 'application/json'
}
})
const responseInterceptors = {
onSuccess: (response: AxiosResponse) => {
if (response.data && response.headers['content-type'] === 'application/json') {
response.data = camelizeKeys(response.data)
}
return response.data ? response.data : response
},
onError: (error: Error) => Promise.reject(error)
}
const requestInterceptors = {
onSuccess: (config: InternalAxiosRequestConfig) => {
config.params = decamelizeKeys(config.params)
if (config.data && config.headers['Content-Type'] === 'application/json') {
config.data = decamelizeKeys(config.data)
}
return config
},
onError: (error: Error) => Promise.reject(error)
}
http.interceptors.request.use(requestInterceptors.onSuccess, requestInterceptors.onError)
http.interceptors.response.use(responseInterceptors.onSuccess, responseInterceptors.onError)
/Api
Директория содержит функции для работы с сервером для всего приложения. По аналогии с models разделение файлов идет по сущностям, с которыми работаем.
auth.ts
users.ts
products.ts
и т.д.
Пример:
// auth.ts
import { http } from 'config/axios/http'
import { privateHttp } from 'config/axios/privateHttp'
import {
VerifyContactRequest,
VerifyContactResponse,
VerifyCodeRequest,
VerifyCodeResponse,
UpdateTokensResponse,
UpdateTokensRequest
} from 'models/auth/api'
import { SuccessResponse } from 'models/common/api'
export const updateTokens = (data: UpdateTokensRequest) =>
http.post<UpdateTokensResponse, UpdateTokensResponse>('/auth/update-tokens', data)
export const verifyContact = (data: VerifyContactRequest) =>
http.post<VerifyContactResponse, VerifyContactResponse>('/auth/verify/contact', data)
export const verifyCode = (data: VerifyCodeRequest) =>
http.post<VerifyCodeResponse, VerifyCodeResponse>('/auth/verify/contact/code', data)
export const logout = () => privateHttp.post<SuccessResponse, SuccessResponse>('/auth/logout')
/App
Инициализирующий слой приложения. Здесь хранится все необходимое для его запуска.
providers
styles
types
hooks
…
App.ts
Инициализация происходит в компоненте App.tsx (/app/App.tsx
). Здесь импортируются глобальные стили и шрифты приложения, провайдеры, роутер, декларации типов (d.ts), хуки и т.д.
Пример:
import { DEFAULT_ARIA_LOCALE } from 'constants/variables'
import { RouterProvider } from '@tanstack/react-router'
import { I18nProvider } from 'react-aria'
import { router } from './constants/router'
import { withAppProviders } from './providers/appProvider'
import './styles/global.scss'
function App() {
return (
<I18nProvider locale={DEFAULT_ARIA_LOCALE}>
<RouterProvider router={router} />
</I18nProvider>
)
}
export default withAppProviders(App)
/Assets
Здесь храним все медиафайлы проекта (картинки, иконки, аудио, видео)
Содержит список каталогов, разбитых по категориям:
icons
Images
audio
video
Внутри каталогов можно также организовывать файлы по категориям, например /icons/arrows/regular-arrow.svg
/Constants
Все необходимые приложению константы. Например:
// permissions.ts
export const rules: Rules<UserRole, Permissions, AbacUser> = {
[UserRole.VISITOR]: {
[Permissions.READ_PRIVATE_PAGES]: false
},
[UserRole.TUTOR]: {
[Permissions.READ_PRIVATE_PAGES]: true,
[Permissions.EDIT_CHATROOM]: (adminId, user) => adminId === user?.uuid,
[Permissions.READ_SETTINGS_DOCUMENTS]: true,
[Permissions.READ_SETTINGS_MEMBERSHIP]: false,
[Permissions.CREATE_KIDS_ONLY_CHATROOM]: false,
}
}
/Hooks
Общие для приложения хуки. Пример:
// useWindowSize.ts
import { useLayoutEffect, useState } from 'react'
const useWindowSize = () => {
const [windowSize, setWindowSize] = useState({ width: 0, height: 0 })
const handleSize = () => {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight
})
}
useLayoutEffect(() => {
handleSize()
window.addEventListener('resize', handleSize)
return () => window.removeEventListener('resize', handleSize)
}, [])
return windowSize
}
export default useWindowSize
/Stores
Сюда мы кладем хранилища наших стейт-менеджеров. Мы используем zustand.
alertStore.ts
userStore.ts
uiStore.ts
…
/Styles
Общие стили для всего приложения.
layout
mixins
variables
…
/Utils
Всевозможные вспомогательные функции, такие как debounce, compose, работа с localStorage и тд.
Файлы называем по категориям функций, к которым они относятся.
common.ts
storageManager.ts
validators.ts
…
Заключение
Все эти правила уже долгое время помогают нам разрабатывать проекты быстро и поддерживать их без боли. Такой подход отлично показывает себя как на небольших приложениях (~100–1000 часов frontend-разработки), так и на проектах долгих (~1000 и более часов frontend-разработки)
По этим же принципам можно расширять данную структуру под нужды конкретного приложения или менять ее, если в этом есть необходимость. Главное — сохранять общий подход.
Попробуйте это на своем проекте, и, возможно, вам тоже это подойдет.
И, главное, помните: проектирование приложений — это искусство)
Комментарии (56)
asultanov
28.12.2024 14:25Крутая статья!!! Только некоторые моменты смущают. Отпишу, чтобы показаться умным xD:
«useMyComponent.ts (для компонентов с бизнес логикой)» - смутило то, что какая-то часть бизнес-логики пишется в хуках, хотя у вас уже есть для этого стейт-менеджер (zustand). Такую "бизнес-логику" сложно отдирать от юайной логики, особенно если кто-то напичкает в этот хук useEffect'ов
То что можно делать папки components любой вложенности тоже не понравилось. Имхо, но лучше делать одну папку components для компонента и уже в ней создавать остальные компоненты. Проще понять какие самописные компоненты используются и не надо погружаться в ад из папок components
«Пример — тип, описывающий HTML-форму, у которой набор полей на фронтенде не совпадает с моделью бэкенда» - звучит как несоблюдение контракта. Без единого источника истины возникают разного рода конфликты
Написание своих моделей и клиента (функций для взаимодействия с бэком) очень геморный процесс. Можно генерировать по свагеру на фронте модели и клиента, что позволит мгновенно синхронизировать контракт с бэком. Также можно настроить мок-сервер и api-first, но это большая работа)
RateIsGreat Автор
28.12.2024 14:25Такой инструмент как стейт-менеджер предназначен для связи наших компонентов в разных частях интерфейса, чтобы не городить общие стейты на верхних уровнях. Это необязательно должно содержать в себе бизнес-логику, а в нашем случае вообще не должно.
Идея в том, чтобы инкапсулировать компоненты по тем зонам, к которым они относятся. По той же логике, условно, можно положить все компоненты приложения на один уровень /components. Но от этого мы и пытаемся уйти)
Это не всегда так. Иногда бывает, что фронтовая форма имеет только клиентские поля. Например галочка "Я принимаю условия...", которая служит для валидации. В таком случае типы будут различаться.
Это следующая проблема, о которой мы задумываемся, но пока что инструмента, который будет отвечать всем требованиям для нас мы не нашли. Возможно скоро мы это исправим)
clerik_r
28.12.2024 14:25То что можно делать папки components любой вложенности тоже не понравилось.
Тут всё индивидуально, компоненты могут быть сгруппированы по какому-то признаку. Просто в тупую ставить ограничения на то, что нет нельзя создать ещё одну папку это как минимум странно.
asultanov
28.12.2024 14:25Ну да, индивидуально. Я пробовал оба варианта. Вариант с одной components на модуль хорош тем, что не нужно в рамках модуля думать о композиции его частей. На плоскую структуру проще смотреть, можно пробежавшись взглядом увидеть от каких частей зависит модуль
js2me
28.12.2024 14:25Одно из главных отличий FSD от вашей архитектуры как раз то, что FSD старается оперировать бизнес сущностями, а все остальное лежит shared. Ну и как подметили ребята в комментариях FSD это больше методология, которая не может четко описать то, как все должно лежать
clerik_r
28.12.2024 14:25Но на практике, fsd это мертворожденная и вредная штука. Чего не скажешь о классической архитектуре.
Reallamos
28.12.2024 14:25Архитектура на реакте? А оно вам надо?
Если проект большой и надо реально думать над архитектурой, то надо брать точно не реакт.
vdshat
28.12.2024 14:25Как начинаешь новый frontend проект, так начинается все сначала: какие папки, как назвать, что такое компонент, слой, фича и т.д. и т.п. В одном проекте это согласование заняло около месяца! Уже бэк успели сделать, а все шли дебаты.
Общие компоненты, по-хорошему, должны быть внесены в отдельный репозитой и подтягиваться отдельным артефактом. Если вы положили что-то в определённую папку, это не значит, что это стало тем, что предполагалось помещать в эту папку.
Мы же предлагаем строить приложения более прямолинейно, как мы все привыкли, вокруг Интерфейсов.
Самое смешное, такого же принципа, часто придерживаются UX-дизайнеры. И получается, если нужно сделать принципиально новый дизайн и разработать новый поход, компоненты и прочее, то все в ступоре, т.к. такого никто не делал и интерфейса ещё в природе нет. А плясать нужно от бизнеса, от природы вещей - сущностей. А сущности могу не иметь UI отображения и тогда совсем беда...
clerik_r
28.12.2024 14:25И получается, если нужно сделать принципиально новый дизайн и разработать новый поход, компоненты и прочее, то все в ступоре, т.к. такого никто не делал и интерфейса ещё в природе нет.
Конкретный пример в студию, который может показать что эти слова похожи на истину, ибо я вообще не вижу проблемы, а вы пишете что все в ступоре.
Как так? Непонятно.
В чем проблема разработать новый дизайн? Берешь и делаешь новый дизайн.
Что конкретно вы имеете в виду под "разработать новый поход"?такого никто не делал и интерфейса ещё в природе нет.
В чем проблема опять же? Берешь и делаешь как обычно.А плясать нужно от бизнеса, от природы вещей - сущностей.
Не вижу выгоды для разработки примерно нисколько. Просто вы так решили и всё.
А сущности могу не иметь UI отображения и тогда совсем беда...
И что? Почему беда? В чем проблема?
Например нужно в фоне отправить аналитику, да, тут нет отображения, но я не понимаю почему это вдруг стало бедой?
olegkusov
FSD это просто структура папок а не архитектура
rtatarinov
А здесь у автора что? Не тоже самое ли?
И если уж на то пошло, это не просто структура папок, это слои с правилами взаимодействия друг с другом! Что еще в твоем понимании архитектура?
RateIsGreat Автор
В данном контексте архитектура - это определенный "фреймворк" того как мы организуем код в проекте. Это не только про структуру папок, но и про их содержание и связи.
NikitaLikosov
Вот из-за такого понимания fsd у многих разработчиков автор, я думаю, и решил использовать что-то проще. Вникать слишком долго, а при первом просмотре кажется, что это просто структура папок.
Даже в этой статье есть слои, как в fsd. Вам не кажется, что это не структура папок?
sovaz1997
Если вникнуть ещё глубже, FSD может не понравиться
NikitaLikosov
я не говорил что fsd идеален. Может нравиться может нет. Мне раньше нравился к примеру, но сейчас после релиза 2.1 перестал. Мой тезис был "это не просто структура папок". К чему это нравится, не нравится не очень понимаю.
sovaz1997
Так чем fsd-то хорош? Не масштабируемая штука абсолютно. А если что-то не масштабируется, толку от этого нет никакого, имхо. Вот есть у тебя папка фичей со слайсами. Она же будет бесконечно раздуваться по мере увеличения проекта из-за ограничений вложенности. (Да, я понимаю, что в сегментах может быть что угодно. Но зачем тогда оно вообще надо). Мне вот понравилась идея фрактальной структуры. Если какая-то часть проекта большая, там будет много папок и файлов. И всё это будет инкапсулировано в одном месте, а не разнесено по разным папкам.
NikitaLikosov
Да будет много папок, но зато удобней переиспользовать и опять же fsd не говорит сколько нам кода в папке писать. Ну есть у нас форма авторизации в 4 разных вариантах просто ложим в одну фитчу и через public API отдаём. Мне кажется классическая архитектура проектов ещё менее масштабируема.
clerik_r
Удобнее? Это как? Удобнее чем это?) Вы просто прочитайте что что вы написали) Ложим в фичу, через public api отдаем, джину лампу трем. Как будто реактивный двигатель для Союз-1 разрабатываете. И да, а с чего это вдруг в фичу? Почему не в widgets? :D Вопрос смешной да?) Такого вопроса вообще априори не должно возникать.
Даже не знаю, удобнее можно разве что, чтобы силой мысли код выполнялся.
Вы правы, вам кажется. К ней не применимы термины "менее удобна" и "менее масштабируема". Она основана на этих принципах. Просто, очевидно, интуитивно понятно, наглядно, быстро, это все про нее.
NikitaLikosov
Удобней чем без fsd. Если сможете скинуть документацию вашей архитектуры можем с ней сравнивать. Вам "кажется" лучше всей командой делать проекты по принципу "я так чуствую"?)
Ответ на ваш вопрос:
Features
Этот слой предназначен для основных взаимодействий в вашем приложении, действий, которые важны вашим пользователям. Эти взаимодействия часто затрагивают бизнес-сущности, поскольку сущности — это то, о чём ваше приложение https://feature-sliced.design/ru/docs/reference/layers
clerik_r
???
У нее нет документации, она интуитивно понятная. Если вам не понятно назначение файлов и папок из этих примеров, то у меня для вас плохие новости. Я скинул вам конкретные примеры структуры, пожалуйста. сравнивайте. Просто пройдитесь комментам тут полно примеров структуры файлов и папок. Они говорят сами за себя.
Нет. По принципу здравого смысла. Один из них это делать жизнь проще, а не делать жизнь сложнее.
Аххах. Я читал эти "определения" ко всем "слоям". Это просто маразм. И ваш комментарий не раскрыл, почему
LoginForm
должен лежать именно в features, а не в widgets или в entites. Более того, у вас не найдется утвердительного ответа, ибо этого не знает никто.NikitaLikosov
Слой features предназначен для взаимодействий то есть для кнопок, инпутов, форм и т.д. Это знаю минимум я потому что прочитал документацию и теперь и вы потому что прочитали комментарий)
не в entities лежит потому что это действие а не отображение данных
не в widget потому что это обособленная часть функционала которую можно переиспользовать не нуждающаяся в композиции с entities
про какой "здравый смысл" вы говорите? Если мой здравый смысл говорит использовать fsd я не здоров?) Вы знаете кажется вы единственный знаете как надо, а люди использующих и продвигающих fsd шизики выдумавшие сами себе проблемы чтобы больше работать)
clerik_r
Т.е. форма не отображает данные?) Submit формы это не действие?) А поля формы, это что, не данные которые улетят на сервер?)
Что?)))
Ну, скажем так, ваши вкусы специфичны)
Да нет, много кто знает как надо) Просто вам скучно использовать простые принципы и решения и вам создаете себе проблемы, а потом пытаетесь их решать)
NikitaLikosov
Кажется вы решили решили что fsd плох так и не разобравшись в нем)
Форма заставляет пользователя что то сделать
entity и feature это как существительное и глагол
И вся форма будет feature вместе с jsx стилями и тд.
слой widget обычно используют как композиционный слой для feature и entity. К примеру в widget посте лайк будет feature и через prop прокидываться в слот в сам компонент поста который entity
Ну с таким подходом и реакт не нужен вовсе
clerik_r
Перечитайте свой коммент и осознайте, что это абсурд, вы пытаетесь ответить на вопрос почему компонент
LoginForm
нужно положить в features, а не в widgets или в entities, а почему бы и не в shared? Сама эта ситуация уже маразм.В классической архитектуре такого в принципе быть не может. Если компонент будет использовать в разных местах, то это
src/components
, в остальных случаях он находится в локальной папке components например страницы, которая для удобства может быть разбита на компоненты. Если ты добавляешь глобальное состояние, оно без вариантов идет вsrc/globalState
, если локально, то оно лежит рядом с компонентом и называетсяstate.ts
.Разницу улавливаете, в одном случае все предельно просто и интуитивно понятно, для всех и всегда. А в другом случае взрыв мозга
И да, это не ответ на вопрос. Луна в Сатурне потому что небо зеленое, чай уплыл, а пыль погасла.
Омг, ну это же просто дичь. FSD доводит элементарные вещи до абсурда.
NikitaLikosov
не в shared потому что это бизнес логика нашего приложения. если вынести её в другое приложение она потеряет смысл.
Вы же предлагаете просто в components всё кидать и говорите что так проще. Причем проще потому что лично вам интуитивно понятней) Ваша классическая архитектура как вы её описали только у вас в голове. Я вот такой как у вас реализации вообще не встречал с components в страницах. Самое плохое в этом всём что работая в команде надо руководствоваться хоть какой то архитектурой и документацией. Руководствуясь здравым смыслом у вас крокозябра получитсья.
опять какие то поля, луны в сатурнах. Если вы не понимаете вы спросите что не понимаете, а лучше перечитайте доку доклад какой-нибудь послушайте.
clerik_r
Чем меньше опыта(в разрезе кол-ва проектов), тем меньше шанс что вы видели тот или иной подход.
Есть вещи не нуждающиеся в документации. Ибо они и так предельно ясны.
Это нуждается в документации? Нет, тут всё говорит за себя.
Более того, никто не запрещает на классику написать документацию. Это супер быстро и супер просто.
pages
- для страницlayuots
- для лейтаутовcomponents
- для общих компонентоввложенная папка
components
- для локальных компонентов, которые необходимы исключительно этой странице/компоненту/лейауту и т.п.Ну и так далее. Всё же элементарно. А вы опять какую-то смуту наводите, якобы тут что-то сложное, не понятное, бардак и т.п. Ну бред же.
Ахах. Ясно.
Так вы называйте вещи конкретно своими именами, не называйте стакан объектом, не называйте стул объектом по крупнее. Просто говорите стакан и стул.
NikitaLikosov
я вам про одно вы мне про другое. Где я говорил что в каждый проект надо добавлять fsd?
Где вы нашли что я был против папок под глобальный Стейт, лэйауты и тд?
"Чем меньше опыта(в разрезе кол-ва проектов), тем меньше шанс что вы видели тот или иной подход"
трава зеленая. небо синее. Если вы пытались так завуалировать что у меня не хватает опыта то вот ответ.
Вы вот не трогали fsd получается у вас меньше опыта?
какие стаканы и стулья?))) где вы что не поняли из того что я говорил? Хватит метафор пожалуйста
clerik_r
Как раз таки наоборот) Из-за опыта мне достаточно провести мысленный эксперимент чтобы увидеть сразу к чему приведет использование FSD или использование Effector и т.п. Чтобы понять, оно будет лучше чем Х или нет. Ну и касаемо самом FSD я видел его на 2х проектах в которых приходилось вносить доработки, это конечно было фрик шоу) Вполне ожидаемое фрик шоу, другого я и не ждал, ибо и так все понятно если просто взглянуть на эту мертворожденную концепцию)
Я же не извращенец или фанатик, я ленивый человек и если что-то добавляет удобства, я обязательно это использую. Если бы FSD приносил удобство по сравнению с классикой, я бы сразу же перевел все свои проекты на FSD и новые начинал с FSD. Или же если бы redux был удобнее чем mobx, я бы сразу выкинул mobx и перешел на redux. И т.п.
Простой пример из жизни, для большинства людей которые видят коровью лепешку на дороге достаточно просто ее видеть, чтобы понять что она не вкусная и вонючая) Для этого нет нужны к ней походить, пробовать ее на вкус и тд)
Или когда ты видишь кипящую воду(она вся такая бурлит) нет смысла совать в нее руку и проверять температуру, ты уже знаешь что это кипяток.
Вот и тут такой же принцип.
Можно себе сэкономить уйму времени и нервов, избежав такого рода экспериментов. Просто посмотри что перед тобой, проведи мысленный эксперимент и вуаля.
У меня в отличии от вас позиция не предвзятая и не фанатичная, а просто вытекает из фактов и простых принципов. Всё сугубо ради удобства и выгоды с точки зрения трудозатрат. Зачем мне на проекте применять подход Y, если с ним я в 2 раза больше времени трачу на реализацию таких же задач, как тратил бы если бы использовал подход X.
Например в 2016 году я считал что typescript это оверхед, с 2012 по 2016 ни разу TS не юзал, но в уже 2017 я пересмотрел свою позицию и с тех пор typescript only. До появления async/await в Node.js ноду я тоже не рассматривал в серьез как инструмент для написания бэка. Но JS эволюционировал, стал значительно круче и вуаля, в 2018 я уже писал бэкенд на Node.js с удовольствием.
sovaz1997
Что значит удобнее пере-использовать? Если вы положите компонент в папку components/, его нельзя будет пере-использовать?
С чего классическая архитектура менее масштабируемая? Может быть вы понимаете что-то другое под этим словом. Вам никто не запрещает создавать компоненты в компонентах, ну и так далее, в "классической" архитектуре.
В FSD, думаю, тоже. Но единственное - FSD добавляет свои ограничения, которые в огромном проекте будут только мешать и заставлять думать о том, о чем можно не думать при свободной организации кода. А также вообще непонятно, в чем преимущества такого подхода. Нельзя все проекты взять и подогнать под один шаблон.
Конечно, если на FSD писать ToDo-лист (а именно подобного уровня примеры я видел в официальной документации), всё может выглядеть красиво и по-фсд-шному.
NikitaLikosov
fsd заставляет оперировать бизнес сущностями что помогает проекту масштабироваться и переиспользовать код
sovaz1997
Что значит оперировать бизнес-сущностями? Наличие папки "entities"? И как это поможет, если этих бизнес-сущностей тысячи
Про масштабирование и пере-использование я описал выше
NikitaLikosov
тысяча бизнес сущностей это где столько?)
Да поможет так как обычно они валяются где то внутри components в лучшем случае, а чаще просто размазаны по всем компонентам. Когда они выделяются сразу становится проще понимать что где лежит и что от чего зависит.
Минимум "тысяча" бизнес сущностей не будут и зависеть от друг друга и мы будем это понимать потому что это заложено в архитектуру. От чего что зависит в папке components поймешь только пересчитав весь код
clerik_r
Может уже пора снять розовые очки наконец? В реальных проектах всегда есть сложные компоненты/сущности/суслики/кролики/называйте как угодно, которые зависят от других, в этом весь смысл компонентного подхода. Вы прикрываетесь абстрактными сущностями, избегая конкретики, потому что как только дело доходит до конкретных примеров, то сразу все становится элементарно и вся мифическая сложность сразу улетучивается.
Прям конкретный пример приведите, со структурам папок и файлов, с кодом. Чтобы мы видели что тут бардак, а тут все классно и все чрезмерные усложнения не напрасны, а необходимы.
Пока все ваши мифические доводы крутятся в парадигме из разряда: земля плоская, потому что я стою в поле, смотрю вперед и не вижу что это шар. А вот если бы вы использовали fsd, то тогда вы бы всё увидели)
NikitaLikosov
> Может уже пора снять розовые очки наконец? В реальных проектах всегда есть сложные компоненты/сущности/суслики/кролики/называйте как угодно, которые зависят от других, в этом весь смысл компонентного подхода.
Сложить лапки и плакать предлагаете?)
> Вы прикрываетесь абстрактными сущностями, избегая конкретики, потому что как только дело доходит до конкретных примеров, то сразу все становится элементарно и вся мифическая сложность сразу улетучивается
Я не понимаю вас. Вам пример из жизни привести? У меня нет кода который я бы мог скинуть в общий доступ) Есть много примеров в документации fsd. Я ничего не избегаю. У меня нет цели вас обмануть. С fsd правда может быть удобней делать проект.
> Пока все ваши мифические доводы крутятся в парадигме из разряда: земля плоская, потому что я стою в поле, смотрю вперед и не вижу что это шар. А вот если бы вы использовали fsd, то тогда вы бы всё увидели)
Ну если вы встречали таких проектов где хотелось бы добавить правил на архитектуру то видимо вам правда не нужен fsd сейчас)
clerik_r
Называть вещи своими именами и принять тот факт, что реально сложные вещи буду при любом раскладе сложными, никакая fsd/классика и т.п. этого не облегчит.
И не надо тут пытаться умножать сложность простых вещей в разы, называя их абстрактными сущностями. Таким образом якобы только у вас(тех кто манипулирует на этом) такие сложности что ппц, а все остальные максимум это цвет кнопок меняют и не понимают истинных "проблем".
Если у всех остальных нет никаких проблем с архитектурой многие годы, ибо для фронтенда на реакте уже десять раз придуман стандартный подход, он же классический. То это говорит лишь о том, что он удачный, а люди которые его используют понимают что они делают и зачем. Да и мыслят они конкретикой, поэтому все что для вас(мыслящий абстрактными сущностями) сложно, для нас(мыслящих конкретными вещами) - легко и очевидно.
Классика. Типичная "удобная" отмазка. Я не могу, я не я, посмотрите на примеры. Эти примеры не дают никакого ответа на вопрос, почему конкретно fsd будет лучше классики. А знаете почему? По тому что же, почему и вы не можете конкретного примера предоставить.
Вы прикалываетесь? Вы опять сравниваете fsd с какой-то выдуманной плохой архитектурой, на фоне которой fsd якобы лучше. Речь идёт о совершенно конкретной архитектуре здорового человека(пример базовой структуры есть в комментах) и FSD. Вы понимаете что у вас нет ровно ни одного реального аргумента, подтвержденного кодом и/или структурой папок и файлов. Всё на что вас хватает это фантазии каких-то мнимых кейсах где якобы FSD лучше.
sovaz1997
Как правильно подметил @clerik_r
Вы можете дать конкретный пример большого проекта, сделанного по FSD? Чтобы было конкретно видно, в чем преимущества данной структуры папок. Чтобы мы говорили не о чем-то абстрактном, а по существу.
clerik_r
А как классика этому мешает?
clerik_r
@NikitaLikosovтак как классика этому мешает?
clerik_r
Если вникнуть глубже или того хуже попробовать ее в реальном проекте, то это будет первый и последний раз когда у тебя возникала мысль использовать fsd.
Это настолько контр продуктивно и контр интуитивно, что аж страшно, почему не все это видят с первого взгляда. Достаточно провести мысленный эксперимент, или посмотреть примеры у них на сайте или попробовать создать тестовый проект и начать накидывать код, то кровь сразу из глаз пойдет и ты поймешь, что-то ты делать что-то очень не хорошее, остановись, это не правильно.
NikitaLikosov
возможно вам понравится версия 2.1. Я пробовал на реальных проектах. С 3 проекта научился и стало прям удобно и понятно.
clerik_r
Это я уже видел. Да, оно стало лучше чем было. Это факт.
Но это всё равно полумера и все равно FSD кривая мертворожденная история. Он сливает по всем фронтам классике. Хоть убей, не понимаю стремление некоторых людей собственноручно заставлять себя страдать.
На счет удобно и понятно это вы конечно преувеличиваете и лукавите))) Такие тезисы не применимы к fsd.
А вот ключевое тут:
с 3его проекта.
В стандартной классической архитектуре вам бы сразу, сходу все было понятно, ведь ее фишка в том что она интуитивно понятна и максимально логичная. всегда и для всех. А не только для тех, кто посветил ей пару тысяч часов и пару десятков проектов, участвовал в паре десятков созвонов где обсуждали что есть wdgets, что есть features, что есть shared (спойлер: и в итоге все равно никто не понял что есть что).
NikitaLikosov
Обычно, когда говорят о стандартной классической архитектуре, все представляют себе разные вещи. Что такое fsd можно просто прочитать у них в доке. Да и мне кажется сложно утверждать, что слои и public api это не удобно и не понятно. С тем что текущий вариант разделения по 6 слоям часто пораждает больше вопросов чем ответов я согласен.
RateIsGreat Автор
Именно поэтому мы и предлагаем наш подход. У FSD высокий порог вхождения, а также есть споры по поводу ее эффективности на дистанции, но сама идея создания общей договоренности о том, как строить проекты, абсолютно правильная. Говоря о классической архитектуре, каждый будет понимать свое и делать все по своему. И это не предположение, а личный опыт работы с командами. Насколько это проблема для ваших задач - решать вам. Мы же убедились в эффективности наличии общего фреймворка.
Также важно понимать, что те правила, которые мы предлагаем - это реакция на определенные боли с точки зрения Development Experience, с которыми мы сталкивались на протяжении многих лет работы, и которые с большой долей вероятности будут стрелять в "классической" архитектуре.
В нашем понимании это как раз шаг в сторону приятной и понятной разработки без лишнего, но с необходимым, чтобы не оказаться в ситуации, когда проект может поддерживать только сам его автор, либо это никому не нравится.
Я постарался подробно описать это в своей статье, но если что-то осталось непонятным - это можно спросить или прокомментировать.
clerik_r
Какие конкретно боли? И почему это боли? Каким образом они стреляют?
Один раз зайдя на проект и увидел структуру файлов и папок в классике сразу всем становится все понятно. Что, как, зачем и почему. Ключевые слова: интуитивно понятная и логичная. Что конкретно тут может быть не понятно?
С чего вы это решили? И каким это образом имея такую структуру кто-то вдруг будет ее понимать как-то по особенному и делать по своему, вопрос как именно остается открытым. Тут все названо не абстрактно, а вполне конкретно. Компоненты, страницы, глобальной состояние, маршруты, конфиг и т.п. Как можно трактовать страницы по разному? Как можно трактовать компоненты по разному?
С таким же успехом можно сказать: Архитектура которую вы предлагаете плохая. И далее пояснение вашей логики Почему плохая? А просто потому что я так написал, мне так кажется. Или стандартный фейковых аргумент, а исходя из моего опыта такие архитектуры превращаются в помойку и т.п.
Jenia_python
В случае с FSD основная идея как раз в четком определении границ и связей между фичами, слоями и модулями. Это не просто про папки и их содержимое, а про продуманное разделение ответственности и упрощение поддержки проекта.
markelov69
А теперь реальность: раздрабливание все и вся на файлы и папки, кране неудобная организация этих самых файлов и папок, код раскинут не логично и не удобно, ограничения, на каждый чих надо думать в какую именно папку положить тот или иной код, сложная навигация, каждый раз когда ты ищешь тот или иной компонент, то он может оказаться где угодно.
Упрощение поддержки проекта? Упрощение поддержки проекта Карл? Вы вообще знаете что такое классическая архитектура, она же интуитивная, понятная всем и всегда на интуитивном уровне? Вот приведу ее базовый пример из другой статьи:
Всё предельно понятно на интуитивном уровне. И главное для всех и главное ничего не надо изучать, это как ходить, ты интуитивно понимаешь куда тебе ноги переставлять. И никаких ограничений, можно делать по настоящему крутые вещи.
NikitaLikosov
Где в вашем примере мне сделать переиспользуемую на разных страницах форму зависимую от самописных ui компонентов? Кажется папка components быстро превратится в помойку спагети кода. Если проект маленький то я согласен что лучше вашего примера не придумать
clerik_r
src/components
разумеетсяЕсли компонентов полно, то да(если делать всё в тупую). И нет разницы, будет ли она называться shared или widgets или animals.
Открою секрет, например папка
components
может быть дополнительно структурирована по типу/назначению компонентов. Например:Уже не похоже на помойку да? И это касается любых папок, это все и так очевидно и интуитивно понятно, ограничений нет, от слова никаких.
Вообще нет разницы какой размер, он работает шикарно на всех.
NikitaLikosov
мне кажется тоже самое если ввести слои будет удобней. Допустим оставить как у автора статьи 3 слоя (положить их вашу папку components) и сразу понятно что за что отвечает и что с чем связанно. Добавить public api из fsd ещё удобней, а там уже пару шагов до самого fsd)
Опять же, если эти правила только добавят сложности, то оно и не надо, но я из личного опыта вижу, что на больших проектах эти правила помогут ориентироваться в проекте
clerik_r
А так что, не понятно?))
Вот смотрю и вообще не понимаю что тут к чему)) Вообще не понятно)) Если бы не подсказки в виде комментариев никогда бы не догадался с первого раза)
Правда?)) pages, layouts, components, globalState и т.д. и т.п. это так, шутка какая-то. А если добавить правило - каждый файл не более 10 строк, то будет ещё удобнее, а если добавить правило что в каждой папке должно быть не менее 4х папок, то будет ещё удобнее. Мы уже почти достигли идеала, круто. Везде есть мера и здравый смысл, не надо выходить за их пределы.
Именно, они только добавят сложности. И да, не надо этого. Только для прям сложных компонентов можно добавлять дополнительные деления, ибо для них это логично и интуитивно понятно.
Эмммм, нет)
Упаси)
Это называется выдавать желаемое за действительное.
Можно сказать так(это не мои мысли, а выдумка для иллюстрации манипуляции через слова об опыте):
Из личного опыта я вижу, что писать аналог Figma на голом языке brainfuck с транспиляцией в JS это приятно, удобно и помогает легко и быстро ориентироваться.
Из личного опыта я вижу, что чем короче мы называем переменные, тем проще и быстрее писать код, да и с поддержкой у нас в команде нет проблем. Идеально это если имена переменных состоят из одного символа.
Ну и так далее.
NikitaLikosov
так это вы просто не хотите привыкать к новому и выдаете желаемое за действительное! Обоюдоострый меч)
Описанные вами папки это не слои в понимании fsd. Ну и если вы считаете что слои имеют столько же смысла что правило добавлять в папку 4 папки то я не знаю что ответить)
про public API. да!
clerik_r
Привыкать к новому?) Вы так говорите как будто речь про переход с кнопочного телефона на смартфон идет) Привыкать к ущербной структуре файлов и папок и к ограничениям?) Нет, спасибо) Зачем, если я человек вольный и не люблю вставлять себе палки в колеса и портить жизнь.
Я привожу конкретные примеры с конкретной структурой папок и файлов. Их может любой сторонний наблюдатель посмотреть и сравнить с тем, что предлагает fsd.
А вы говорите о каком-то якобы опыте и о том, что якобы этот опыт показал что fsd это якобы удобно. Т.е. то, что нельзя проверить и объективно оценить.
Ахахах. Оперировать такими понятиями как "в понимании fsd" это абсурд высшей степени. Типо это эталон, по которому мы сверяемся, что в его понимании подпадает по ту или иную трактовку, а что нет.
С таким же успехом вы можете говорить что земля не шар, потому что это не вписывается в концепцию плоской земли.
А что по вашему описанные мною файлы и папки такое? Набор рандомных букв? Наверное не понятно что в них должно лежать да?)
Вы его увидели первый раз в FSD и у вас все ассоциации с этим словом только завязаны на fsd?) Поэтому вы не понимаете что такое components, pages, layouts, globalState и т.п.
Ясно, понятно)))) С import и export мы работать не умеем)
NikitaLikosov
вы не сказали ничего по существу, переврали мои слова, напридумывали того что я не говорил.
Хорошо, поздравляю, вы победили! Вы во всем правы пойду учиться работать с import и export.
clerik_r
Это здравая мысль, учиться никогда не поздно
NetInstant1100
Отлично. Вы разбили компоненты на подпапки. И так же у вас разбиты utils, hooks, helpers, state. Причем структура разбиения зачастую совпадает. Так почему бы не зайти с другой стороны и первым уровнем сделать разбиение по сущностям, а уже потом по типу используемого кода?
Более того, у вас частично так и есть на картинке: вместо shared у вас выступает первый уровень проекта, а бизнес зависимые вещи лежат в pages.
Сколько я не работал на разных проектах, классическое разбиение скатывались в помойку с трудно отслеживаемых и зависимостями.
clerik_r
Потому что это не удобно и не применимо в реальности. На реальном проекте не только черное и белое, а все градации серого. А если проект сложный, то и вся цветовая палитра может быть.
И не все привязано к "сущностям", и не все "сущности" изолированы друг от друга, в реальной жизни все сложение, с ветвлениями где многое зависит от многого, в том числе одна "сущность" зависит от другой или сразу от нескольких, более того эти "сущности" могут зависеть от сторонней логики, не связанной напрямую с какой-то "сущностью". И вообще проект это далеко не только набор "сущностей". Поэтому ваш вариант не подходит и может быть лучшей заменой классики.
И вообще мыслить "сущностями", а не конкретными вещами - это путь в никуда.
Так же? Это как?
Как образом например:
hooks/useOutsideClick
hooks/useBoolean
hooks/useSwite
И т.д. и т.п. имеют отношения с сущностям?
Как образом например:
utils/formValidator
utils/asyncHelpers
И т.д. и т.п. имеют отношения с сущностям?
Аналогично и касаемо всего остального. Опять же, все просто, сущности просто тут не нужны и не применимы, страница логина это просто страница логина, не больше не меньше. Фильтр списка товаров, это просто фильтр списка товаров, не больше не меньше. Сам по себе список товаров, это просто список товаров, не больше не меньше. Карточка товара, это просто карточка товара, не больше не меньше. Все это по сути просто детали реализации проекта, например интернет магазина. Вот всё, всего лишь на всего.
А вы начинаете всё переусложнять, вводить дополнительные слои абстракций, какую-то чудоковатую структуру файлов и папок придумывать. Зачем? Рад чего? Я понимаю если бы это реально упрощало и ускоряло разработку, делала бы ее приятной и интуитивно понятной. Но нет, этот подход делать все ровно наоборот. Вместо того, что элементарную вещь типа страницы входа или фильтра реализовать так же элементарно, ты вынужден перешагивать через 100500 ограничений и дополнительных абстракций.
Это просто слова, которые ничего не значат. Любой может использовать этот трюк.
"Сколько я не работал на разных проектах, проекты где был применен fsd банкротились на следующий день"
"Сколько я не работал на разных проектах, сова прилетала к моему окну ровно в 12:44"
"Сколько я не работал на разных проектах, только использование $mol делало проект идеальным"