TGViewer
isqualog • front-end • productivity isqualog • front-end • productivity @isqualog · 586 subscribers
Post #119 604
Layers vs Vertical Slices

Кирилл Мокевнин (Hexlet) набросил, как файлы класть — по типу или по домену?

Для меня меня на фронтенде это вопрос давно решенный: если сервис больше чем 2 странички, то, конечно, по фичам. Иначе, чтобы поправить какую-то мелочь, надо по 5 папкам лазить.

📗 Главный принцип: low coupling, high cohesion

Недавно был на проекте: 90к строк кода, 4 фронтендера. Код разложен по слоям: consts, utils, hooks, api, components, pages. Так там утилит было на ~20к строк (четверть кодовой базы!), в них лежала почти вся логика приложения. В константах лежало 202 файла. Тип пропсов компонента из src/components/statistics/CohortsTable.tsx лежал в src/types/statistics/CohortsTable.d.ts. Постоянные проблемы с циклическими зависимостями. Ребята генерили новый код, похожий на старый — трудно было найти нужное.

Когда в ру-язычной фронтенд-тусовке говорят про vertical slices, вспоминают FSD. Имхо, челы перегибают палку. Зачем деление на widgets/features/entities? Это тащит нас обратно — в low cohesion, high coupling. Поэтому туда не ходим, keep it simple, stupid.

И вот мы пару недель обсуждали, какие есть проблемы, почему больно это поддерживать, почему даже мелкие задачки занимают столько времени. В итоге проблема «хз че где» вышла на первое место по скору ICE (Impact × Confidence × Ease).

Мы выработали структуру и правила. Самое сложное было — выделить доменные сущности, об этом никто раньше не думал. Ещё за неделю раскидали 80% файлов, жить сразу стало легче. Подробнее писал в блоге компании (опубликовали в мой последний день там лол)

В src осталось три папки:
• app — рутовый компонент, провайдеры
• features — самая соль по доменам
• shared — общие компоненты

В каждой фиче (опционально): pages, ui, store, api, utils, const, types. При этом если какие-то константы, типы или хелперы нужны только для одного компонента, они идут в папку этого компонента. На уровень фичи поднимается только то, что нужно для нескольких модулей в ней.

Один коллега боялся, что пути/будут/вот/такие/длинные/ужас/кошмар. Но .../ui/Component это последний левел глубины. Внутри компонента всё плоско. Если меньше 10-15 файлов в папке, доп. уровни не нужны:

src/features/apps/ui/AppsTable/
AppsTable.tsx
AppsTable.module.css
DesktopRow.tsx
MobileRow.tsx
CellWrapper.tsx
useColumns.tsx
formatter.ts


Дальше учимся давать имена сущностям из вонючего ящика utils и выделять их из файлов по тыще строк. Типовые куски:

• adapters/mappers — из ответов API в структуру, удобную для фронтенда и обратно — обычно в src/features/<feature>/api
• validators — валидаторы/схемы для формочек — к соотв. компоненту
• formatters — как мы даты форматируем и т.п. — к компоненту

Выкидываем barrel-файлы (index.ts с реэкспортами) из папок, которые не являются модулями:

• src/features/apps/ui/AppsTable — модуль (компонент), кладём index.ts, реэкспортируем «публичный интерфейс» — скорее всего, это сам <AppsTable /> и его пропсы. А всякую внутреннюю шелуху типа форматтеров или useColumns не реэкспортим
• src/shared/routing — модуль, кладём index.ts, реэкспортируем всякие константы маршутов, функции для их построения и тп
• src/features/apps/ui/ — не модуль, а просто папочка организационная

👉 Писал про barrel-файлы у себя в блоге.

Откуда я знал, что всё это сработает? Потому что уже строил такую архитектуру на проекте в Яндексе, который был крупнее, и мы жили так пару лет и всё отлично работало и скейлилось. Сейчас, посмотрев на проекты в других компаниях, понимаю, что dev experience на этом проекте был лучшим.

Зачем мы 2 недели обсуждали, если я сразу «знал, как надо»? Чтобы:
• провалидировать, что это решит проблемы именно текущего проекта, и именно самые больные
• адаптировать подход к проекту и команде, срезать углы, что-то улучшить
• ребята вовлеклись и участвовали в принятии решения, люди гораздо лучше делают то, что решили сами, чем что-то навязанное

Кстати, AI-агентам в такой структуре проще ориентироваться.

А вы в каком лагере?
🔥 features first: src/features/<feature>/ui
🤔 layers first: src/ui/<feature>/blabla
⭐️ уже иду рефакторить
  • 🔥 13
  • ❤ 2
More from @isqualog
  1. Jul 24, 2026Диктовка На работе нам раскатили Wispr Flow — аппка для распознавания речи в любом текстов…
  2. Jul 10, 2026Видели уже страничку с пулреквестами в гитхабе? Я только сегодня обнаружил редизайн, обычн…
  3. Jun 26, 2026пожалуйста уберите огонёчек, и так жарко
  4. Jun 26, 2026Европу накрыла heat wave, поэтому сегодня у нас рубрика РАСПАКОВКА В Германии кондиционеры…
  5. May 5, 2026Жизнь научила меня не смешивать разные изменения в одном PR Фичи, багфиксы, рефакторинг —…
  6. Apr 16, 2026Привет, а вот и новое видео! В прошлой серии мы залезли в одну функцию сортировки и улучша…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →