Как стать автором
Обновить

Комментарии 26

Архитектура — это не способ упорядочения модулей в файловой иерархии. Архитектура — это способ взаимодействия компонентов системы. По этой причине MVC/MVVM/etc (которые как раз представляют архитектуру) ортогональны FSD, Local-First, Feature First и прочим способам организации файловой структуры проекта.

Например, если есть некоторые сущности предметной области Foo и Bar, то при архитектурном подходе MVC можно разложить компоненты в файловой системе по-разному:

Layer First

controllers/
├─ BarController
├─ FooController
models/
├─ BarModel
├─ FooModel
views/
├─ BarView
├─ FooView

Feature First

bar/
├─ BarController
├─ BarModel
├─ BarView
foo/
├─ FooController
├─ FooModel
├─ FooView

Взаимосвязи между сущностями остались теми же, изменились только пути к файлам.

Спасибо за ваш комментарий! Вы абсолютно правы: архитектура действительно в первую очередь описывает взаимодействие компонентов системы, а не просто организацию файлов. Я полностью согласен, что MVC/MVVM и другие архитектурные подходы ортогональны к способам организации файловой структуры, таким как FSD или Local-First.

Моя статья, возможно, не акцентировала внимание на этом различии достаточно четко. Однако мой подход не ограничивается только файловой структурой. Он также включает в себя принципы взаимодействия между слоями и модулями. Например:

  • Компоненты отвечают только за отображение и не содержат бизнес-логики.

  • Виджеты инкапсулируют логику, связанную с конкретными функциями, и могут взаимодействовать с сервисами.

  • Сервисы предоставляют общую логику для работы с данными и API.

  • Страницы объединяют виджеты и компоненты, обеспечивая их взаимодействие.

Таким образом, моя архитектура — это не просто способ организации файлов, а комбинация файловой структуры и принципов взаимодействия. Она не противоречит MVC/MVVM, а скорее дополняет их, предлагая конкретный способ организации кода, который помогает сохранять его чистым и поддерживаемым.

Ваш пример с Foo и Bar отлично иллюстрирует, как одна и та же архитектура (MVC) может быть реализована с разной файловой структурой. В моем случае я выбираю подход, который, с одной стороны, упрощает навигацию по проекту, а с другой — четко разделяет ответственность между слоями.

Когда статьи с помощью AI генерят, уже вроде как-то привыкли

Но когда и комментарии... (((

Комментарий мой. Просто расписал его, чтобы комментатору было понятней.

А что если гибридное решение, когда каждый контрол в БД со своим набором свойств и правилами обработки запросов.
Основные отличия:
- в БД хранится максимум настроек;
- переиспользуемый код для каждого типа контрола;
- кастомные настройки в базе: модель (хранится в базе), вьюшка (определяется в дизайнере, хранится в базе) и сервис, как обработчик запросов, основной функционал в базе, с кастомизацией.

Объяснение сумбурное, но основная цель в снижении поддержки за счет хранения структуры модели вьюшки и обработчика в реляционной базе данных с соблюдением нормальных форм.
Как результат, проблемы синхронизации страниц при изменении модели или вьюшки исчезли.
Результат проектировался с 2017 года и в реальных приложениях с 2019 года.

А что если Feature First + Data-Driven Backend with Universal Controller

С архитектурной точки зрения, у вас кастомные настройки модели и вьюшки — это внешние данные. Они передаются на фронтенд через API, а дальше их нужно обработать и отобразить. Можно представить работу с ними всё теми же паттернами MVC или MVVM, разложив по иерархии как у автора или любым другим способом. Только здесь они ещё будут играть роль метаданных для собственно данных, с которыми работает пользователь.

Уже все придумано больше 10 лет назад, это называется классика, она же логика, она же интуитивно понятная, она же очевидная. КПД максимальное. Любому вновь прибывшему сразу понятно что где лежит, что куда класть.

Работает для небольших проектов. Как только сложность становится больше N, понимаешь, что без предварительного разбиения на модули не обойтись

Работает для небольших проектов

Работает на любых проектах.

Как только сложность становится больше N, понимаешь, что без предварительного разбиения на модули не обойтись

А тут что, код не разбит? Все в одном файле/папке? Кто-то мешает создавать подпапки для логической группировки?

  1. В каждой папке вырастает во многом похожая, но не идентичная иерархия, из за сложности их поддержки.

  2. Код получается гвоздями прибит к проекту и вынос фичей в отдельные проекты сопряжён с серьёзным рефакторингом.

  3. Связанные файлы разбросаны по разным местам репозитория далеко друг от друга.

В каждой папке вырастает во многом похожая, но не идентичная иерархия, из за сложности их поддержки.

Чрезмерно притянуто за уши, с чего в друг в каждой папке? Если есть очень большая и сложная страница скажем, то разумеется в ней будет дополнительное разбиение на компоненты и т.п. В чем проблема то в итоге?)

Код получается гвоздями прибит к проекту и вынос фичей в отдельные проекты сопряжён с серьёзным рефакторингом.

Ну во первых "прибит", только тот, который относится чисто к бизнес логике конкретного проекта, всё остальное и так по умолчанию делают нейтральным и универсальным. Поэтому, даже если что-то в этих местах которые реально, в реальный жизни захочется переиспользовать, то они изначально уже не будут прибиты, либо прибиты слабо, и провести рефакторинг вынеся это в другой проект займет от силы 30 минут в 95% случаях.

Связанные файлы разбросаны по разным местам репозитория далеко друг от друга.

Всё что касается конкретной страницы лежит внутри страницы. Это как бы рядом. Всё что касается конкретного компонента, лежит внутри этого самого компонента. Это как бы рядом. То, что на страницах или других компонентах используются общие части, которые используются много где, они и не должны лежать рядом. Что за бред. Вы когда писал этот пункт, вообще видели структуру которую я показал или от балды настрочили?

Всё что должно быть рядом, то и лежит рядом.
Всё что должно быть рядом, то и лежит рядом.

Вы не отрефакторите эти свалки за пол часа при всём желании.
Вы не отрефакторите эти свалки за пол часа при всём желании.

Это не свалки со страниц) Это то, что могут использовать страницы, лейауты, компоненты и т.п.

Это не мусор, это яичная скорлупа, рваные пакеты и кожура от банана.

Хотел прочитать про Local-First в надежде увидеть теорию и практические примеры, а увидел рассказ про то как вы структурируете данные. И как справляетесь с возникновением взаимо зависимостей между сторами. А они появляются... Вот здесь как раз и нужен какой то механизм. К примеру в приложении метамаск сторы взаимодействуют по шине сообщений и не имеют никаких прямых взаимоссылок друг на друга. Все остальные проекты с которыми я работал так или иначе в процессе своего развития стали обрастать ими. Таким образом степень связанности модулей стала расти до уровня "большой кусок дерьма".

Про Local-First:

Я согласен, что в статье не было глубокого погружения в теорию Local-First, и, возможно, название ввело в заблуждение. Local-First в моем контексте — это скорее метафора, которая подчеркивает, что архитектура строится вокруг локальной разработки и минимальной связанности между модулями. Это не столько про оффлайн-приложения, сколько про то, как сделать каждый модуль максимально независимым и самодостаточным (добавил это в дисклеймер после Вашего комментария).

Про зависимости между сторами:

Вы абсолютно правы, что со временем зависимости между сторами могут стать проблемой. В моем подходе я стараюсь минимизировать такие зависимости, вынося общую логику в сервисы или composables. Я бы отметил это как следующую степень развития проекта, но это отличная идея, подумаю над ее реализацией, спасибо!

И как справляетесь с возникновением взаимо зависимостей между сторами

Проблема:

Раз
Раз
Два
Два
Три
Три

Ссылка - https://stackblitz.com/edit/vitejs-vite-rms3azjv?file=src%2FApp.tsx,src%2Fstores%2FstoreA.ts,src%2Fstores%2FstoreB.ts&terminal=dev

Решение:

Раз
Раз
Два
Два

Ссылка - https://stackblitz.com/edit/vitejs-vite-fucf9b87?file=src%2FApp.tsx,src%2Fstores%2FstoreA.ts,src%2Fstores%2FstoreB.ts&terminal=dev

Вопрос даже не в том, как обойти ошибки. Вопрос в том что растет связанность между сторами что при росте проекта превращается в большую свалку. И замедляет развитие проекта. Единственный способ это не иметь прямых ссылок между сторами. А через шину событий получать необходимые данные из другого стора и хранить их как часть своего состояния.

Вопрос в том что растет связанность между сторами что при росте проекта превращается в большую свалку И замедляет развитие проекта.

Ну на самом деле тут ключевой аспект, это размер проекта. К сожалению тут без вариантов, если он большой, то это априори свалка, он будет неповоротливым, каждая новая правка всё сложнее и медленнее и т.п., как снежный ком, что бы мы не делали, и чем больше человек пишут в него код, тем эта свалка хуже. Единственный реально работающий способ это микрофронты, но и для них действует то же правило, чем больше микрофронт, тем больше он свалка) И как раз, для микрофронтов шина ложится отлично, а в рамках монолита это просто дополнительный уровень сложности.

Единственный способ это не иметь прямых ссылок между сторами

Да, ну это на самом деле связь между сторами вообще не проблема, ибо она решается легко, всего лишь setTimeout, а взаимосвязь между сторами появляется не просто так, она продиктована:
а) Удобством разработки
б) Бизнес логикой

А через шину событий получать необходимые данные из другого стора и хранить их как часть своего состояния.

К сожалению это не решает истинную проблему больших проектов, это просто альтернатива setTimeout'у, не более. Но если речь идёт о микрофронтах, то шина must have для взаимодействия между собой.

Что ж у вас всех такая мания превращать проект в свалку, гоняя все события через единую шину - это мало того, что медленно, так ещё и концы с концами фиг сыщешь, всё влияет на всех. Освойте уже концепцию реактивных каналов, не позорьтесь. Архитекторы, блин.

это мало того, что медленно

Медленно?)) Это нативная скорость. Все работают в едином процессе и едином потоке. Мы не говорим про шину гоняющую только строки, где нужно делать каждый раз JSON.stringify / JSON.parse.

// Microfront #1
window.$transport.emit('any_key', anyData);
// Microfront #2
window.$transport.on('any_key', (anyData) => {
  myState.data = anyData;
});

так ещё и концы с концами фиг сыщешь, всё влияет на всех

Ну если писать кривыми руками, которые даже не знают что такое console.log, то да. Но в этом случае вообще ничего не спасет) А так, всё нормально будет на самом деле.

// Смотрим всё что проходит через шину
// Можно даже логировать тех, кто ловит ссобытия через .on()
window.$transport.logEmit = true;
window.$transport.logHandlers = true;

Вжух и концы с концами отыскали)

Плюс никто не мешает расшаривать реактивные стейты

window.$states.appState.toggleTheme();
window.$states.cartState.clear();
window.$states.favoritesState.addItem(item);

Тут уже и шины нет) Миксовать можно как угодно, простор для творчества большой

Да, гонять все события через единую точку - это очень медленно. События в принципе далеко не бесплатны. У них полно и других проблем: их легко пропустить, поздно подписавшись, легко потерять не обработав, можно впустую потратить время на обработку устаревшего события и тд.

Существование костыля с логированием лишь подтверждает существование проблемы. Ковыряться в килотоннах логов сматчивая кто на кого когда подписался и кто когда что заэмитил - такое себе.

Реактивные стейты - шаг в правильном направлении. Но хранить их в глобальнык переменных не очень композабельно.

Ожидал почитать про localfirstweb.dev, а почитал про "архитектуру" раскладывания файликов.

Аналогично

Про Local-First:

Моя статья действительно не затрагивает концепцию Local-First в том виде, как она описана на localfirstweb.dev. После Вашего комментария изменил название публикации, чтобы не путать читателей.

Про "раскладывание файликов":

Вы правы, что статья в основном фокусируется на организации файловой структуры. Однако, как я упоминал в других комментариях, мой подход — это не просто "раскладывание файликов", а попытка создать архитектуру, которая сочетает в себе четкое разделение ответственности и минимальную связанность между модулями. Это включает в себя не только организацию файлов, но и принципы взаимодействия между слоями (компоненты, виджеты, сервисы и т.д.).

Как раз искал подобный подход к организации проекта. Особенно понравилась идея с разделением на компоненты/виджеты/страницы - у самого постоянно возникала проблема, когда логика начинала расползаться по проекту. Решение с views как промежуточным звеном выглядит практичным, попробую на практике))

Кстати, GitHub-репозиторий с шаблоном прикольное дополнение, помогает сразу понять, как это работает на практике. Стартану проект сразу из него :D

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации