Правильная файловая структура - скелет любого фронтенд-приложения. В Vue 3 нет строгих предписаний, как раскладывать файлы по папкам, кроме базового разделения components/, views/. Но с ростом проекта хаотичное размещение кода быстро превращается в проблему. В этой статье разберём популярные подходы к организации Vue-проектов: от простейшего плоского до микрофронтендов.

Плоская структура: быстрота и минимализм

С этой схемы стартуют почти все небольшие проекты (пет-приложения). Суть в том, что все компоненты лежат в одной-двух папках, а утилиты и композаблы сгруппированы по назначению, без дополнительных уровней вложенности.
/src |-- /components | |-- BaseButton.vue | |-- BaseModal.vue | |-- ProductCard.vue |-- /composables | |-- useCart.js | |-- useProducts.js |-- /utils | |-- formatters.js |-- /layouts | |-- MainLayout.vue | |-- AuthLayout.vue |-- /plugins | |-- analytics.js |-- /views | |-- HomePage.vue | |-- ProductPage.vue |-- /router | |-- index.js |-- /store | |-- index.js |-- /assets |-- /tests |-- App.vue |-- main.js
Плюсы:
Минимум настройек
Быстрый старт
Низкий порог входа
Минусы:
Не масштабируется
Быстро возникает дублирование
Сложно искать файлы при росте >30 компонентов
Когда использовать: демо, обучающие проекты.
Атомарный дизайн (Atomic Design)

Представьте, что ваш интерфейс - это живой организм, собранный из мельчайших «клеток». В масштабных проектах на Vue такой взгляд помогает держать хаос под контролем. Методология Atomic Design предлагает разложить любой экран на пять осмысленных уровней - от неделимых частиц до полноценных рабочих страниц:
Атомы - фундаментальные кирпичики интерфейса, которые уже нельзя разбить на более мелкие части без потери смысла. В мире Vue это одиночные элементы: кнопка, поле ввода, иконка, заголовок. Они абстрактны, чисты и не зависят от контекста.
Молекулы - первые полезные конструкции, рождающиеся при объединении нескольких атомов. Так, текстовое поле с кнопкой и меткой превращается в строку поиска. Молекулы уже начинают решать конкретные микро-задачи, оставаясь при этом переиспользуемыми.
Организмы - самостоятельные, визуально законченные блоки интерфейса, собранные из молекул и/или атомов. Например, шапка сайта, в которой живут логотип (атом), строка поиска (молекула) и навигационное меню из кнопок-атомов. Такой компонент уже определяет характер целого участка макета.
Шаблоны - скелет страницы, где организмы расставляются по сетке, образуя абстрактную композицию. Здесь появляются отступы, колонки и логическая структура, но данные пока условные или отсутствуют - это как архитектурный макет здания без мебели и жильцов.
Страницы - конечный уровень, где шаблон наполняется реальным контентом, живыми данными из API, состояниями загрузки и ошибками. Именно здесь интерфейс становится тем, что видит пользователь, и именно на этом уровне видны все нюансы работы системы.
Такой подход превращает хаотичный набор компонентов в стройную экосистему, где каждому элементу находится своё место, а внесение изменений на любом уровне автоматически отражается по всей иерархии - но всегда предсказуемо и безболезненно.
/src |-- /components | |-- /atoms | | |-- UiBadge.vue | | |-- UiAvatar.vue | |-- /molecules | | |-- SearchBar.vue | | |-- CartItem.vue | |-- /organisms | | |-- ProductCard.vue | |-- /templates | | |-- ProductListTemplate.vue |-- /pages | |-- HomePage.vue |-- /composables | |-- useProducts.js |-- /utils | |-- priceHelpers.js |-- /layouts | |-- DefaultLayout.vue |-- /router | |-- index.js |-- /store | |-- index.js |-- App.vue |-- main.js
Плюсы: атомы и молекулы отлично переиспользуются. Если дизайнеры меняют кнопку - достаточно поправить один компонент, и изменения распространятся по всему приложению.
Минусы: постоянная мысленная нагрузка «а это атом или молекула?» замедляет разработку. Часто возникает жёсткая связанность на уровне шаблонов, а бизнес-логика размазывается по слоям, что усложняет рефакторинг.
Модульная архитектура

Модульный подход группирует файлы не по технической роли, а по предметной области. Каждый функциональный блок (модуль) полностью инкапсулирует свою логику: компоненты, хранилище, API-клиенты, тесты и даже стили. Это похоже на разделение кода по микросервисам, но внутри одного репозитория.
/src |-- /core // App Shell и общие ресурсы | |-- /components | | |-- BaseButton.vue | |-- /models | |-- /store // Глобальное состояние (сессия, уведомления) | |-- /services // API-клиент, интерсепторы | |-- /views | | |-- AdminLayout.vue | |-- /utils | | |-- validators.js |-- /modules | |-- /products // Модуль «Каталог товаров» | | |-- /components | | | |-- ProductThumbnail.vue | | | |-- ProductCard.vue | | |-- /models | | |-- /store | | | |-- productStore.js | | |-- /services | | | |-- productApi.js | | |-- /views | | | |-- ProductDetailPage.vue | | |-- /tests | | | |-- productTests.spec.js | |-- /cart // Модуль «Корзина» | | |-- /components | | | |-- CartIcon.vue | | | |-- CartItem.vue | | |-- /store | | | |-- cartStore.js | | |-- /services | | |-- /views | | | |-- CartPage.vue | | |-- /tests | |-- /account // Модуль «Личный кабинет» | | |-- /components | | | |-- ProfileCard.vue | | |-- /store | | | |-- accountStore.js | | |-- /services | | |-- /views | | | |-- ProfilePage.vue |-- /assets | |-- /images | |-- /styles |-- /plugins | |-- translate.js |-- App.vue |-- main.js
Плюсы:
Слабая связанность. Модуль
cartиспользует API корзины и внутреннее хранилище, ничего не зная о деталях модуляproducts. Вы можете переписать логику товаров, не затронув корзину.Колокация ответственности. Разработчик, отвечающий за продуктовый каталог, видит все файлы в одной папке, что ускоряет навигацию и рефакторинг.
Масштабирование команд. Если над проектом работают несколько разработчиков, каждый может вести свой модуль практически без конфликтов слияния.
Миграция в микрофронтенды. Когда модуль разрастается до такого объёма, что его выгодно развернуть независимо, он уже изолирован по коду, остаётся лишь наладить его отдельную сборку и деплой.
Feature-Sliced Design (FSD)

FSD - более формализованный архитектурный подход, который разбивает приложение на слои по степени близости к бизнес-логике. Он пришёл из React-экосистемы, но применим и во Vue.
Слои FSD (снизу вверх):
Shared - переиспользуемый код без бизнес-логики (UI-кит, утилиты, API-клиент)
Entities - бизнес-сущности (пользователь, товар, заказ)
Features - пользовательские сценарии (добавление в корзину, авторизация)
Widgets - самостоятельные блоки (шапка, список товаров, профиль)
Pages - страницы приложения
App - глобальные настройки, провайдеры, стили
Правила архитектуры:
Код может импортироваться только снизу вверх
Запрещены импорты с того же или более высоких слоев
Shared можно использовать везде
Entities не знают о Features и выше
/src |-- /app | |-- App.vue | |-- main.js | |-- providers/ | | |-- RouterProvider.vue | | |-- ThemeProvider.vue | |-- styles/ | | |-- index.css |-- /pages | |-- /home | | |-- HomePage.vue | | |-- components/ | | |-- model/ | | |-- lib/ | |-- /catalog | | |-- CatalogPage.vue | | |-- components/ | | |-- model/ | | |-- lib/ |-- /widgets | |-- /header | | |-- Header.vue | | |-- components/ | | |-- model/ | |-- /footer | | |-- Footer.vue | | |-- components/ | |-- /product-list | | |-- ProductList.vue | | |-- components/ | | |-- model/ |-- /features | |-- /auth | | |-- /login | | | |-- LoginForm.vue | | | |-- model/ | | | |-- lib/ | | |-- /registration | | | |-- RegistrationForm.vue | | | |-- model/ | | |-- /logout | | |-- LogoutButton.vue | |-- /product | | |-- /add-to-cart | | | |-- AddToCartButton.vue | | | |-- model/ |-- /entities | |-- /product | | |-- ui/ | | | |-- ProductCard.vue | | | |-- ProductImage.vue | | | |-- ProductPrice.vue | | |-- model/ | | | |-- types.ts | | | |-- selectors.js | | | |-- api.js | | |-- lib/ | | |-- helpers.js | |-- /user | | |-- ui/ | | | |-- UserAvatar.vue | | | |-- UserInfo.vue | | |-- model/ | | | |-- types.ts | | | |-- selectors.js | | | |-- api.js |-- /shared | |-- /ui | | |-- /button | | | |-- Button.vue | | | |-- Button.spec.js | | |-- /input | | | |-- Input.vue | |-- /lib | | |-- api/ | | | |-- apiClient.js | | | |-- endpoints.js | | |-- utils/ | | | |-- formatters.js | | | |-- validators.js | | | |-- constants.js | | |-- hooks/ | | |-- useDebounce.js | | |-- useLocalStorage.js | |-- /config | | |-- theme.js | |-- /assets | |-- /images | |-- /fonts | |-- /icons |-- /tests | |-- mocks/ |-- router.js |-- store.js |-- main.js
Слои строго регламентируют импорты: элементы с нижнего слоя не могут обращаться к элементам верхнего. Это защищает от циклических зависимостей, но требует уверенного знания методологии всей командой. Для средних проектов и небольших команд такой уровень формализации часто избыточен - та же инкапсуляция достигается проще через модульную структуру.
Микрофронтенды

Микрофронтенды переносят философию микросервисов на клиентскую часть. Каждый раздел магазина - каталог, корзина, личный кабинет - разрабатывается и деплоится независимо, возможно, разными командами и на разных стеках.
Основные элементы:
Оболочка (Shell): координация маршрутизации, базовый каркас.
Независимые мини-приложения: Product Listing, Checkout, User Account.
Это серьёзный инструмент, где над фичами работают десятки разработчиков. Для подавляющего большинства приложений сложность деплоя, версионирования и отладки микрофронтендов не окупается.
Плюсы:
Независимое развертывание
Разные технологии в разных модулях
Масштабирование команд
Изоляция ошибок
Минусы:
Сложность настройки
Дублирование зависимостей
Сложность отладки
Что же выбрать для Vue 3-проекта?

Представь, что проект Vue - это большой шкаф с вещами. В начале вещей мало, их можно сложить как попало. Но когда проект растёт, нужна система хранения, чтобы быстро находить то, что относится к «корзине», «каталогу», «авторизации». Модульная архитектура - это способ разложить код по папкам-модулям так, чтобы в каждой лежало всё, что касается отдельной бизнес-задачи. Вот почему.
Она органично вписывается в экосистему Vue. Composition API со своими композаблами идеально сочетается с изолированными модулями. В одном модуле вы держите и презентационные компоненты, и логический слой (
useProduct,useCart,useAuth), и стор (Pinia-хранилища, если необходим модульный стейт). Никаких дополнительных абстракций не требуется.Инкрементальное внедрение. Вы можете начать с плоской структуры и безболезненно выделить модули по мере кристаллизации бизнес-требований. Уже работающий код просто переезжает в
/modules/…, без переписывания всей архитектуры.Совместимость с другими подходами. Если команда дизайна настаивает на атомарном дизайне, реализуйте его внутри
core/components/и, скажем, внутриmodules/products/components/. Если позже потребуется жёсткий FSD для части приложения - вы вольны организовать так отдельный модуль.Живой пример. Представьте, что в модуле
cartпонадобилось добавить промокоды. Вы создаёте подпапкуmodules/cart/composables/usePromocode.js, компонентPromocodeInput.vueи тест. Вся функциональность замкнута в одной ветке, не затрагивая каталог или профиль. Через месяц, когда корзина разовьётся до отдельного микрофронтенда, вы просто настраиваете для папкиmodules/cartотдельный Webpack/Vite-entry. Код менять не придётся.

P.S. И напоследок, чтобы ни у кого не возникло желания накинуться с критикой: это не истина в последней инстанции. Это исключительно мои мысли, основанные на моём опыте, наблюдениях и чувствах. Я не претендую на то, чтобы переубедить всех. Моё мнение - всего лишь одно из многих, но оно имеет полное право на существование, как и ваше. Если у вас иначе - замечательно, делитесь в комментариях конструктивно. Но без негатива, пожалуйста)
