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

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

Многие "паттерны" притянуты за уши, при чем тут MVVM, Anemic Model, Repository, Abstract Factory, Strategy? Просто наброс умных слов без содержания. В целом весь приведенный код - это один большой антипаттерн "пихаем все в реакт". У Вас на обход сложности реакта ушло больше костылей, чем было сложности в изначальной задаче.
Но, это хотя бы попытка сделать "архитектуру", удачи в пути.

Спасибо за прямоту! Коротко - зачем здесь "умные" слова и куда ушли "костыли".
MVVM / Page split В одном web view разделили React страницы (View Model) и хуки шлюзы кTON/REST (Model). Так проще тестировать и менять UI, не трогая домен.
Strategy useTonClient() скрывает выбор mainnet/testnet: одна переменная окружения вместо трёх правок в коде.
Abstract/Factory Темы MUI - готовая фабрика стилей; поменяли цвет в одном месте - перекрасилось всё.
Repository /Gateway useContract() инкапсулирует ABI и TonClient. Переезд на gRPC - правим один файл.
Anemic Model (как антипаттерн) Специально напомнили, чего избегаем: разбросанные finance.getConfig() по компонентам - и есть "анемия".

Почему кажется, что "всё в React":
Telegram mini app = SPA внутри webview. "Костыли" закрывают ограничения SDK (одноразовый init, deep links, TTL подписи) - каждый обёрнут в хук вместо дублирования кода.
Отправка транзакций вынесена отдельно. Сеть переключаем стратегией. Цвета и шрифты управляются единой темой.
На практике это снизило боль: обновление TON SDK заняло минуту, без глобального поиска замены. Критику учту - всегда есть, что улучшить, удачи!

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

  1. MVVM - там должно быть 3 составляющих Model, View и ViewModel. React не реализует ViewModel, а хуки-шлюзы сложно назвать моделью даже с натяжкой. Пример касается вынесения навигации в 1 view - хороший SRP, но проблема с использованием данных роутера вне View. Например, в Gateway/Repository вам понадобится текущая страница для какого-нибудь редиреста с returnUrl. Ее придется прокидывать через параметры из View.

  2. Abstract Factory - используется когда нужно создавать много объектов и существует несколько реализаций. Если бы у вас было несколько разных createTheme: createMobileTheme, createDesktopTheme и они использовались с разными параметрами createTheme('dark'), createTheme('light') то это была бы фабрика. Сейчас скорей всего Provider. Он ничем не хуже/лучше фабрики, просто уместнее в данном случае.

  3. Repository/Gateway - это разные паттерны. Здесь скорее Adapter для внешнего сервиса. и так далее.

Но все эти паттерны не должны лежать в области View - в реакте. Если поменять представление на API или CLI или Mobile или кофеварку - надо будет переписывать все. Стоило реализовать логику как отдельную коробочку на JS/TS с нужными адаптерами. И в реакте уже использовать RichModel, если не нравится анемичная, или сервис с методами { async init(); async getFinance(); getUser(); getTransaction() }.

То есть простой try / catch теперь называется "Graceful Degradation / Fail‑safe вместо Fail‑Fast Crash"? Можно было просто показать как делать и как не делать. Сейчас объяснения примеров сделаны через AI и надуманы, статью было тяжело дочитать.

Спасибо за замечание. Действительно, «Graceful Degradation» здесь по сути сводится к обычному try/сatch с понятным fallback-UI; термин, похоже, лишь усложнил текст. Учту: в следующей версии сделаю меньше теории и больше «как правильно / как не делать» на конкретных строчках кода.

1. Паттерн — Lifecycle hook / Template Method.

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

Если предположить, что вам будут нужны два шаблонных метода то копипасты не избежать.

Если предположить, что будут разные реализации объектов WebApp то все они будут использовать одну глобальную переменную.

Таким образом это решение навеяно шаблонным методом, но им не является.

2. Паттерн — Singleton + Guard Clause.

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

3. Паттерн — Front Controller (Router)

Т.е. не писать простыню всего чего только можно в одном месте - это фронт контроллер?

4. Паттерн — Strategy. Сеть меняется конфигом, код не трогается.

А в чем стратегия то? У вас прямая зависимость от TonClient. Простыня из настроек объекта этого класса. Да и само название useTonClient, т.е. явно ориентированное ровно на TonClient. Где стратегия?

5. Паттерн — Lazy Factory.

Честно говоря, не понял что тут вообще происходит. Вы написали хук useAsyncInitialize. По семантике хуков, они выполняются всегда и сразу. А где тогда lazy? Кто собственно выполняет отложенное создание компонента с этим хуком? И если он это выполняет, то причем тут тогда данный хук?

6. Паттерн — Adapter

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

Всю статью уже не могу осилить...

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

Lifecycle / Template
Вы правы: метод не классический Template Method. Скорее «life­cycle-hook с проверкой среды». Согласен, формулировку можно точнее отделить от паттерна.

Singleton + Guard
Корректнее было писать просто «guarded-init»; полноценного синглтона там действительно нет. Спасибо, уточню.

Front Controller
Согласен - название претендует на большое, а решает лишь «deep-link-точку входа». Переформулируем, чтобы не запутывать.

Strategy
Идея была спрятать выбор сети за одним вызовом; согласен, привязка к TonClient делает «стратегию» слабой. Уточню, что это скорее конфиг-обёртка.

Lazy Factory
Точка lazy - компонент, который впервые вызывает хук. Но замечание справедливо: название может ввести в заблуждение, подумаю, как лучше обозначить.

Adapter vs Bridge
Мы действительно строили собственный слой под UI-потребности. Формально это ближе к Bridge. Приму правку.

Ещё раз спасибо - такие комментарии помогают вычистить термины и сделать статью полезнее.

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

Публикации