Pull to refresh

Comments 42

Это перевод своей же статьи. Я новичек на хабре, не был уверен стоит ли в таком случае указывать что это перевод. На всякий случай указал.

Большое спасибо за статью, сам об этом задумывался по рекомендации Дядюшки Боба.

Так а все таки как на хабре принято в случае выкладывания здесь перевода своей же статьи? Маркировать её как перевод или не стоит?

Стоит упомянуть об этом в тексте. Значок по желанию.

После того, как мне два года довелось работать над подобным кросс-фреймворковым проектом, могу заметить, что все это гладко выглядит только на бумаге.


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

It depends (tm).

Я полностью согласен с вами во втором утверждении и частично — в первом.

По второму — дырявая абстракция хуже отсутствия абстракции. Особенно когда в проектной документации и в презентациях новичкам «дырявость» изящно скрывается. Если дырка есть — о ней нужно кричать красным H1 заголовком — чего конечно же не любит менеджмент от слова «совсем».

По первому сложнее — если мы возьмем «идеальный фреймворк который полностью удовлетворяет нашим нуждам» — то да, абстрагирование от него безоговорочно замедлит разработку (сильно замедлит).
Но я не вижу в данный момент фреймворка который выглядел бы «о**нно» и мне бы хотелось его просто взять и спроксировать. Прочитайте PS статьи — сравнение подхода с работой в контексте популярных фреймворков. Буду рад подискутировать на этот счет.

Стоит ли возможность перенести приложение на новый фреймворк «потом» замедления разработки «сейчас»? It depends (tm)

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

Интеграционные тесты нужны в любом случае. А также полноценная регрессия по факту смены библиотеки для реализации какой то части кор функционала.

Не питаю иллюзий о том что смена фреймворка будет ванклик активностью. Но по крайней мере она будет реализуема в измеримое время (недели, месяцы). А не «нам нужно два года, полная спецификация продукта и отдельная команда разработки», что делает саму идею рерайта irrelevant. Ведь активный транк приложения за эти годы уйдёт далеко вперёд.
по крайней мере она будет реализуема в измеримое время (недели, месяцы). А не «нам нужно два года, полная спецификация продукта и отдельная команда разработки»

Вы таки не поверите. У нас была ровно такая ситуация. Отдельные view и модели, и необходимость обновить наш UI-фреймворк с Mithril на React. Несмотря на наличие вроде-как фреймворк-независимых моделей, миграция заняла год, при этом продукт переписался полностью, включая часть с моделями, потому что lifecycle API Mithril совсем никак не ложился на React, и никакой адаптер не помог, пришлось все переписывать на идиоматичный React-код, за время сравнимое с переписыванием с нуля.

Поверю конечно. Например подход из этой статьи слабо поможет переписать приложение на вьюжс. И в случае возникновения такой задачи… ваш кейс.

Однако для удовлетворения ответственностей определённых в статье (рендеринг JSX, обеспечение реактивности) — разработчик волен выбирать лучший инструмент из представленных на рынке.

Ну и вы же таки смогли переписать приложение. Это уже победа! Я встрявал в более глубокие жопы. Из недавнего — приложение на бекбон/марионет, где никто из команды не знает почему оно именно такое как есть и что должно делаться. И невозможностью нанять новых разработчиков — люди реально увольнялись когда видели стек. Рекрутёры врали про мифический ангулар в миграция в который продлилась два месяца но не ушла дальше первой странички. (спойлер, лично я фанат бекбон/марионет, на мой взгляд эта связка, вместе с нокаутом, была для ФЕ комьюнити толчком к рич приложениям).

Решением была инкрементальная миграция, когда конкретные регионы приложения избирательно переписывались со всей болью связанной с паралельным наличием билда в двух фреймворках и передачей контроля marionette -> react -> marionette -> react.
Больно, долго, не факт что когда нибудь будет завершено, но по крайней мере новые фичи пиляться и люди нанимаються. Но опять же, я не верю что миграция «когда нибудь» будет завершена. А что если реакт и мобкс выйдут из тренда? :)

upd. Очень больно, очень дорого и очень сложно. И далеко не факт что после ухода меня из компании такая инкрементальная миграция прогрессирует.

А как проблемы предыдущего инкрементального подхода решаются в вашем новом решении?

Одно с другим не связано. Я имел в виду что ваш перенос приложения целиком на другой фреймворк (пусть и за год) можно назвать успехом.
Инкрементальный перенос который я упомянул (франкенштейн) можно назвать компромисом который позволил приложению «жить», но что это была за жизнь!

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

Попробую перефразировать вопрос – что именно было больно и дорого "marionette -> react -> marionette -> react" и как это решается в вашем новом решении которое в первую очередь рекламируется вами как "возможность сменить мобкс на условный метеор"?

Было две болевых точки:

1 — очень плотная связность между существующими обьектами: (марионеттовские view, которые логически — контроллеры, и модули). Были уроборосы наподобие такого: view1:riseEvent[E1] -> view2:proxyEvent[E1] -> view3:handleEvent[E1]:riseRequest[R1] -> module1:handleRequest[R1]:fireGlobalEvent[GE1] -> view1:handleEvent[GE1] and do some work. Это очень упрощенная цепочка, в реальности цепочки исчислялись десятками вызовов между разными вью/модулями/бекбоновскими моделями с ветвлениями. В ходе выполнения этой цепочки, различные вью случайно брали данные из подчинённой себе разметки И/ИЛИ модели.

При переводе на мобикс/реакт, мобикс сторы должны были либо воспроизводить эти цепочки либо развязывать их по ходу перевода. Это было громадной болью.

В описанном в статье подходе во первых нет разделения на модели и вьюхи(marionette.views которые, напомню, на самом деле контроллеры) с недоопределёнными ответственностями для обоих. Есть только вьюмодели, данные из которых реактивно потребляются представлениями. Каждое потребление вьюмодели отслеживается через применение соответствующей функции среза, соответственно отследить все использования вьюмодели легко по использованиям функций среза и тайпскриптовским ссылкам. Нет ситуации когда непонятно где, для чего и как может использоваться конкретная функция, определённая на вью или на модели (скажем через глобальное событие трижджы спроксированное и приправленное reqres обработчиком). Вьюмодели инкапсулируют состояние, в отличии от бекбоновских моделей в которые можно установить любое свойство и на изменение которых можно подписаться из любого места приложения.

2 — непосредственно механики интеграции марионетта и реакта. У нас были адаптеры — марионет вьюшки, которые хостили реакт компоненты. И обратные адаптеры — реакт компоненты, которые могли хостить марионет вьюшки.
Процесс миграции был определён так:
— выбирается вью либо страница.
— все данные, содержащиеся на вью (контроллере) и на модели (если такая наличиствует, зачастую на иерерхии моделей) перетекают в мобкс стор (иерархию сторов). Все подписки вью и модели добиваются мобикс реакциями. Вью перерендеривается при именении данных на сторе. Регрессионное тестирование.
— перевод разметки из марионет шаблона в реакт компонент, реагирующий на уже подготовленный мобкс стор. Монтирование этого компонента в марионет приложение с помощью соответствующего адаптера. Если вью использует внутри себя какие то общие вью — монтирование их в реакт компонент с помощью обратного адаптера.

По пункту 2 нет «специфических проблем» решаемых предложенным в статье подходом, это просто требовало громадного headroom от разработчика. Условный джун или условный уставший сеньор просто не в состоянии были взяться за миграцию сколько нибудь большого фрагмента.

Предложенное решение говорит что смена ТСХ рендерера или смена движка реактивности — это изменение инфраструктурного кода, которое не затрагивает код вьюмоделей или вьюшек, т.е. конкретных фич.

Например, сейчас набирает популярность Svelte, как вы сможете переехать на него, используя абстракцию view моделей?

В статье подчеркивается что библиотеки подбираются под ответственности и заменяемы в пределах ответственностей.

Т.е. Straightforward будет смена TSX рендерера либо либы отвечающей за реактивность. Э замена на свелте не будет аккуратной, т.к. свелте нельзя назвать библиотекой для рендера JSX, соответсвенно изменится логическая нагрузка на один из елементов структуры кода.

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

По итогу:
— good: вьюмодель не затронута
— good2: вью изменились исключительно с точки зрения движка-для-отрисовки, т.е. ситнтаксис слегка другой но конструкции идентичны.
— bad: функции среза перекочевали внутрь тега script конкретного компонента. С одной стороны заняло ноль усилий их перенести. С другой — в svelte варианте они не изолированы от шаблона. Для меня это неприемлимое нарушение принципа единственной ответственности. Но из за того что свелте не даёт определить в одном файле более одного компонента — нет и возможность «определить компонент и в том же файле завернуть его в HOC не смешивая со скриптом компонента». Решить можно или так как сейчас или создавая на каждую обёртку по файлу, что есть ещё большая дичъ т.к. связанный между собой код разбрасывается по разным файлам.
— bad2: тайпскрипт со свелте и связанные плагины для сборки это то ещё уныние. На сам перенос потратил часа полтора, а сборку поднимал два вечера.

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


Хорошее описание этого подхода есть в этой статье (не знаю, есть ли перевод)

Собственно в комменте выше я описал именно это, давайте в тот тред:)
UFO just landed and posted this here
Я абсолютно согласен с вами что фреймворки/либы — это мощное комьюнити и 100500 часов разработки, багофиксов и всего что скрывается за словом «мощное компьюнити».

Именно потому я не пытаюсь педалить свой велосипед с блекджеком и женщинами в части «реализации ответственностей» — все делается проверенными и поддерживаемыми инструментами.

Однако «определение» этих ответственностей, на мой взгляд, должно исходить из специфики приложения. Тесть из связки [Бизнес домен]+[Раздел 2, подраздер «ограничения» данной статьи]+[проверенные архитектурные принципы].
Соответственно, начиная работу над потенциально long-living приложением, разработчик обязан грамотно спроектировать структуру этого приложения и подобрать инструменты. Желательно — с возможностью замены этих инструментов.

Обратная ситуация: фейсбук переизобретает eventsourcing для фронта потому что NIH. Ден абрамов переизобретает elm на джаваскрипте, потому что талантливый гик который пробует клёвые идеи. Совпало, завертелось. 3-4 года значительная часть клиентов заказывают приложения на реакт-редакс потому что «фейсбук знает, так надо». Без анализа «а подойдёт ли это нам». 2020 год — интернет пестрит статьями «вай редакс зло» — хотя редакс совсем не зло а просто очень нишевая вещь, которая не была спозиционирована должным образом.
И вот написанный в реакт-редаксе код невероятно плотно забит приложение-специфическими вещами (миддлвары, разные типы екшнов, саги со своим синтакисом).
Бизнес логику просто невозможно «извлечь», а редакс уже «зло» и через несеколько лет из малого зла станет синдромом «легаси проекта».

Касательно онбординга в предложенном подходе:

Начинающий(или мидл) разработчик, прийдя на проект, услышит что у нас реакт+мобикс. И это будет абсолютной правдой. Относительно связки реакт+мобикс предложенная структура предлагает только лиш руководство к «месту где разработчик должен определять сторы», т.е. где в структуре компонентов (или вне структуры компонентов) эти сторы живут. И ирония в том что так или иначе работая с реакт-мобкс команде придётся такое решение делать.
Плюс к этому, подход запрещает использовать хуки (обьяснение в статье). Имея доступ к сторам, возможность использования хуков — это всего лиш способ делать одни и те же вещи двумя разными способами. Т.е. в контексте одного конкретного проекта — зло.

Однако я оставляю за собой возможность сменить реакт на преакт/инферно. Равно как возможность сменить мобкс на условный метеор. Why not?

Фреймворк-независимое браузерное SPA

Все же, может ли слой представления быть полностью независим от фреймворков в реальном приложении?

Если относится к фронтенд-приложению чуть проще, т.е. как это было раньше — это просто View слой всего приложения, то тогда вроде и жить легче всем.
А вы предлагаете усложнить этот view слой ещё больше и нагородить ещё и в нём абстракций:

Абстрагироваться от mobx декораторов…

Абстрагировать все фреймворко-зависимые библиотеки…

Абстрагироваться от синтетических событий…

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

Эти механики — это «бизнес логика» интерфейса / UI только и всего. Настоящая бизнес логика так или иначе реализуется на бекенде, с сопутствующей валидацией бизнес процессов, транзакциями и т.д.
Именно поэтому, мне кажется, что к фронту нужно относится проще — как к view-слою всего приложения вцелом. Фронтов-то можеть быть несколько, включая мобильные приложения.
И если «разрешить» себе и другим жить с такой установкой (фронт — это всего-лишь вью слой, какой-бы крутой ui там не был) — то таким образом можно здорово облегчить жизнь и использовать выбранный фреймворк на максималках со всеми его правилами и ограничениями.

Мой поинт в том, что переписывать с нуля фронт (особенно к уже существующему апи) — это не супер-сложная задача. Не стоит этого боятся. Это нормальная практика. Это дешевле, предсказуемее, комфортнее — чем то что предлагаете вы — нагородить абстракций наперёд и надеятся что они сработают.

Опять же, вот я сейчас переписываю с бекбон на реакт+мобх. Вы предлагаете мне заранее сейчас подумать и спроектировать приложение таким образом, чтоб если вдруг кому-то в голову в будущем стукнет моча переписать это приложение на ангуляр — мне бы нужно было «легко и просто» обновить только компоненты. Пожалуй нет, я лучше сделаю сейчас быстро и понятно, максимально взяв то что дают мне реакт+мобкс.
При необходимости переписать всё выкидываются и делается с нуля на ангуляре, да опять (но это ответственность и цена того, кто будет принимать такое решение).
Кстати, как бы это странно не звучало но именно вам имеет смысл вчитаться в статью переписывая в «реакт+мобкс». Потому что эта связка не определяет «где должны определяться сторы». Т.е. где физически должны лежать файлики и как сторы «деляться» между компонентами. Описанный в статье подход как раз доопределяет это. Но хозяин, конечно, барин.

Про переписывание фронта под существующее апи — зависит от розмера. Если фронт маленький — проблемы нет. Если фронт большой-и-старый то это фактически нереализуемо, из моего опыта.
Потому что эта связка не определяет «где должны определяться сторы». Т.е. где физически должны лежать файлики и как сторы «деляться» между компонентами.

А что там определять-то… Всё делать по доке. После того как они выпилили inject и MobxProvider (или как он там назывался) — официально рекомендуется использовать реактовский контекст (для всего корня приложения) для хранения сторов. Сами сторы создавать уже по смысловым бизнес-доменам (т.е. те сущности, про которые должен знать любой компонент в любой момент времени на протяжении всей жизни приложения).
Также иногда уместно использовать useLocalObservable для чего-то не глобального, но когда useState не очень, т.к. затрагивает достаточно большое дочернее поддерево.
Если при использовании исключительно глобальных сторов ваше приложение остается читаемым, а ответственности сторов — осмысленными (в частности — общая бизнес логика не перемешана с логикой-специфической-для-конкретных-вью), то у вас нет проблемы и соответственно не требуется решения для этой проблемы.

Когда (если) появится проблема перемешивания «общей» логики и «вью-специфик» логики, либо локальные обсерваблы сделают вью не-читаемо-громоздкими — тут вам поможет данная статья.
А не возникнет проблемы — gl&hf, happy coding!

Картинка про 14 конкурирующих технологий

Я снимаю свой плащ и волшебную шляпу перед опенсорс комьюнити и не пытаюсь изобретать велосипед, но скорее «определить че делать» и «подобрать лучшие[из существующих] инструменты для решения конкретных задач».

Наличие в мейнстрими фреймворков, делающих из себя «one ring to rule them all» и совершенно не пытающихся ограничить область своего применения — причина появления этой статьи. Есть классные инструменты решающие конкретные задачи. (тот же мобикс, например, или rxjs). Давайте дружить с ними:)

Для компонентов уже есть веб-компоненты, которые точно так же можно писать хоть на чём от голого js, до реакта и компании.
Для хранилища данных — local storage. Да, есть ограничения, но в остальном прекрасно справится с задачей, как мне кажется.
Какие плюсы несёт данная схема?

Вопрос не «докопаться ради». Вы прочитали статью?
Мне интересно, поскольку если «да» и у вас возник этот вопрос — значит я плохо выражаю свои мысли и нужно исправляться.
Если «нет» — прочитайте если будет время после чего я открыт к обсуждению

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

Ок, спасибо за комментарий,

Вы не единственный в комментах кто воспринимает предложенный подход как «свой фреймворк» но я бы его так не определял.

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

Отвечая на вопрос про использование веб-компонентов и локалстореджа — в пункте 6 статьи определены цели, которых мы хотим достигнуть, в частности:
— возможнсть использования строго типизированных представлений (для чего мы используем TSX).
— возможность ограничить представления разметкой, т.е. сделать максимально тупым.

На мой взгляд функциональные TSX компоненты решают эти задачи лучше всего. По крайней мере я не знаю как определять строго типизированные веб-компоненты, равно как не знаю как определять веб компоненты без дополнительного жс кода (пусть и примитивного).

Что касается хранения данных. Моей целью было:
— определить обьекты, отвечающие не только за хранение данных, но и за инкапсуляцию действий по модификации этих данных
— обеспечить реактивность (перерисовку представлений при перерисовке данных)
— обеспечить возможность привязки конкретных представлений к «глобальным (вьюмодель на много компонентов)» либо к «локальным»(вьюмодель на екземпляр компонента) хранилищам данных.
— обеспечить возможность внедрения сервисов с логикой бизнеса в такие обьекты через DI.

Локалсторедж позволяет хранить данные, но все остальные требования никак не удовлетворить локалстореджем, т.е. нужно что-то ещё.
Описанный подход предлогает использовать ванильные TS классы (из примера), а уже методы withVM и connect скрывают детали реализации реактивности, привязки к представлениям, материализации.

Обратите внимание на папку с приложением — тут у нас реально живет приложение.
Тогда как реализация вышеупомянутых функций withVM и connect лежит тут. И именно тут лежит код, отвечающий за реализацию реактивность через мобикс или за реализацию внедрения вьюмоделей в дерево компонентов с помощью контекста реакт.

Называть это фреймворком… перебор, же. Возможно количество текста в статье подспудно наделяет структуру кода дополнительной сложностью:)
А может дело в моей личной привычке и восприятии, dunno.

Вот что мне не нравится в архитектурах n-layer/onion/MVC и тд, так это то, что они созданы для других целей, а их пытаются применять везде. Это ведь все клиент-серверные архитектуры, где UI является всего лишь частью, они не являются рекурсивными(один блок внутри не может состоять из всей архитектуры).

Чаще всего они stateless и поток данных у них прямой, на фронтенде все работает асинхронно и самое главное у нас есть СОСТОЯНИЕ. Использование данных архитектур приводит к тому, что проект становиться невероятно сложен для своих размеров, не говоря уже о том, что крупный пример UI проекта НЕВОЗМОЖЕН на такой архитектуре.

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

Если хочется действительно в построение архитектуры для клиентских приложений, присмотритесь на игры и паттерны которые там используют. Они идеологически ближе к фронтенду, чем бекенд. Там вы увидите всем знакомые паттерны Композиция вместо наследования, Состояние, Наблюдатель, Команда, Двойная буфферизация и тд. Также о том, как управлять поведением огромного количества объектов и как это все масштабировать.
По поводу аналогии с разработкой игр я с вами согласен на все сто и рад что вы это заметили. Было бы максимально нелепо тянуть стейтлес подходы в стейтфул мир.

Минутка истории:
MVC была придумана в доисторические времена именно для statefull приложений. На моём веку использовался в таких динозаврах как java, прости господи, swing. Но «говорят» что бывало и до того. В 2000-2010 с подачи RubyOnRails, MVC феерично перекочевал в клиент-серверный (стейтлес) сегмент синхронно с девиацией слоя модели из богатой (rich, еффективно для стейтфул) в бедную (anemic, еффективно для стейтлесс). Вы, вероятно, путаете подход и его реализацию в RoR (и последовавших за ним клонах на других языках — Asp.Net MVC, Spring MVC и прочая).

C выходом технологии WPF, авторам даже пришлось придумывать новый термин MVVM что-бы их структуру кода не путали с MVC, которое де-факто стало строго ассоциироваться с клиент-серверным взаимодействием (хотя по факту девиация МVVM от именно «классического» МVС незначительная).

Подход к структурированию кода описанный в этой статье как раз основан на MVVM.
Он не имеет ничего общего с клиент-серверной разработкой. Слой представления состоит из связки View и ViewModel, где ViewModel как раз отвечает за состояние и является стейтфул. А статья — отвечает за то «как» и «где» обьявлять вьюшки и вьюмодели. Обратите внимание, я избегаю разделения одного и другого в структуре файлов и явно называю это одним слоем — иначе мы получим бессмысленное и трудное для восприятия расслоение, свойственное ортодоксальным n-layered системам.

Статья ограничена разбором исключительно слоя представления поскольку для 90% приложений выделенная доменная модель тупо не нужна. Но если доменная модель таки нужна и она не имеет привязки к конкретным представлениям — выделенный слой домена является лучшим «местом» для её определения. Повторюсь — это не «обязаловка» и нужен этот слой только лишь если ваш заказчик начинает говорить терминами и категориями, никак не кореллирующими с формочками и табличками, видимыми в приложении. Соответственно мы не можем «разместить» эти категории внутри «основы» нашего УИ приложения — дерева компонентов.

Для большинства же приложений более чем достаточно грамотно организованного дерева компонентов и вынесения интеграций со сторонними сервисами в отдельные, инфраструктурные, файлы и классы (папка boundaries в проекте-примере). При этом интерфейсы всё так же являются частью дерева компонентов и определяются внутри него, но реализации этих интерфейсов сгруппированы по принадлежности к конкретному внешнему сервису, с которым мы интегрируемся (это может быть local storage, API, another API powered by third-party provider e.t.c).

А подскажите, каким образом в wpf(я не мастер этой технологии) или в идеологии mvvm, реализовать следующий алгоритм взаимодействия с интерфейсом.


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


Кто должен завершить запрос и где?

ViewModel не отвечает за загрузку данных, а делегирует эту ответственность сервису (DAO). Например как во вьюмодели из статьи на строке 18.

offtop: В приведённом примере дао синхронно исключительно потому что так сделано в шаблонном todoMVC которое я воспроизводил 1 в 1. По хорошему там должен возвращаться промис а не значение, и код бы выглядел как:
this.todoList = await this.todoDao.getList();


При «уходе» со страницы соответствующая вьюмодель разрушается. (Кстати не только по этой причине, например дерево компонентов может быть разрушено и заменено и без перехода на другую страницу, в случае каких то динамических форм билдеров например). В WPF это отрабатывается емнип через метод Dispose, в коде из статьи — через метод cleanup.

что сделать для реализации вашего кейса:

  1. Возвращать из DAO не обычный промис, а что-то похожее промис с дополнительным методом «cancell()».
  2. В методе загрузки тудушек заменить
    this.todoList = await this.todoDao.getList();

    на
    this.todoListRequest = this.todoDao.getList();
    this.todoListRequest.then(list => this.todoList = list);

  3. Добавить во вьюмодель метод
    cleanup() {
        this.todoListRequest && this.todoListRequest.cancell();
    }

Отлично, большое спасибо за ответ! Теперь хочу задать вопрос немного другой, но думаю по итогу нашего диалога картинка какая то сложиться.

Есть такой кейс, у нас какое то сложное отображение UI. Например Dropdown с возможностью выбора нескольких элементов из списка. Куда необходимо положить состояние, которое будет означать открытость/закрытость элемента, список выбранных элементов и можно еще что то придумать?
Речь кастомном переиспользуемом мультиселект дропдауне?

Если да, то есть нюанс:
  • открытость/закрытость — это детали реализации контрола «дропдаун мультиселект», и жить они должны на вьюмодели этого контрола
  • список выбранных елементов — это данные, о которых должна знать «конкретная форма» для того что бы их отправить, провалидировать и тэ де. В исключительных случаях она может эту информацию игнорить (как например вы можете поместить на форму текстбокс и не читать его значение) но это редкость. Т.е. список выбранных елементов должен жить на вьюмодели формы НО! наш переиспользуемый компонент должен его дублировать на случай если компонент формы его не предоставляет (оставляет компонент дропдауна в uncontrolled виде)


Вот тут набросал как будут выглядеть елементы зашаренного дропдаун контрола и пример его использования контролируемый и неконтролируемый. Всё в одном файле, комментами разделено. Будут вопросы — велкам, надеюсь код прояснит написанное.
Большое спасибо за ответы и отдельное спасибо за пример! Теперь в целом у меня вопросов к архитектуре нет, я ее понял как абстракцию к апи фреймворка, в этом нет ничего противозаконного, просто за каждую абстракцию необходимо платить)

И вот связи с этим интересует экономическая сторона вопроса, есть аналитика или практический пример, насколько действительно быстрее такой подход помогает перейти на другой фреймворк?

Так же интересно сколько обходиться поддержка такой архитектуры(написание кода/ревью кода/подбор людей/обучение и тд)?

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

К слову у меня был опыт перевода проекта с одного фреймворка на другой, проект не сильно крупный, около 150к строк кода, переход с vue на react, но при этом не могу сказать, что возникли крупные проблемы или у бизнеса на это были крупные затраты или задержка ttm. Думаю, что пока в целом на фронтенде преобладают компонентные фреймворки в этом нет проблемы, все сводится к переименованию методов/атрибутов/структуры файлов и тд, но честно не могу представить как ваш подход поможет при переходе например на backbone)

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

Вот поэтому и интересна экономическая сторона.
Я рад что вас заинтересовала данная тема:)

Использование композиции в целом приветствуется и является жизненно важным. Композиции данных — через Dependency Injection во вьюмодели (есть десятки лет опыта и наработок «как» это делать наиболее безболезненно). С композицией компонентов думаю понятно, они априори друг в друге живут.

По затратам на перевод… Понимаете, что-бы получить обьективную метрику нужно взять два продакшн проекта выполненных по одной бизнес спецификации но написанные по разному технически и их переписать. При этом нужно как то обеспечить что-бы люди работающие в этих командах были +- одной квалификации. А проекты должны быть именно что большие. Иначе говоря — это невозможно. И этой диллеме в контексте оценки мейнтейнабилити уже сто лет. Если бы подобные «обьективные проверки» были реализуемы — у нас бы был один «самый лучший» фреймворк а все остальные бы канули в лету ввиду худших «обьективных» метрик.

Потому так взлетают хайповые темы — главное их распиарить и проинвестировать. А реальные убытки от недооценки минусов проявятся у отдельных команд через 1-3 года. Ещё 1-3 года те будут думать что это не проблема фреймворка а проблема в головах команды. За это время можно и фреймворк переписать по принципу «миша всё ***ня давай сначала», распиарить, проинвестировать и пошла вторая итерация.
Самые известные приверы таких итераций: ангуларжс->ангулар, реакт->реакт+флакс(дефакто редакс)->реакт+хуки.

Потому важно понимать «а для чего вообще я беру фреймворк, чего я от него хочу и какую проблему решаю». Потому написана данная статья — что-бы показать как фреймворки «используются» при написании приложения, а не «диктуют условия написания приложения».

По бекбону:
Предложенная структура подразумевает что 3party либам мы отдаем такие ответственности как «рендер строго типизированных вьюх» и как «реактивность». И то и то бекбон делать не умеет (реактивность — умеет но криво и с оговорками).
Т.е. использовать бекбон для рендиринга вместо реакта — невозможно. Для реактивности вместо мобикс — возможно но нужно будет написать ооооочень много своего кода.

Если вас интересует практический кейс переезда на другой фреймворк — то я перевёл рендеринг на svelte с подачи одного из комментаторов выше в треде. Вот коммент с описанием результатов и ссылкой на бранч с кодом.

p.s.
Более конкретно: миграцию провести тем легче, чем меньше инфраструктурный код просачивается в «бизнес-значимый» код приложения. В идеале — миграция проводится тупо заменой реализаций инфраструктурных методов. В случае данной статьи — заменой реализации методов withVM и createConnect().

Но я сделал оговорку в самой статье что несмотря на полный отказ от апи реакта в явном виде, мы всё равно зависим от синтетической системы ивентов. Т.е. переход не будет абсолютно бесплатным за счет того что другой движок рендеринга может оперировать ивентами иначе.
Sign up to leave a comment.

Articles