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

Об одном способе реализации архитектуры крупного Flutter-приложения

Уровень сложностиСредний
Время на прочтение8 мин
Количество просмотров5.1K
Всего голосов 16: ↑16 и ↓0+16
Комментарии14

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

Но почему GetX? Чем вам Bloc в комплекте с тем же autoRoute не угодил? Вообще, удивительно использовать фреймворк на фреймворка, это так оптимизировано.

Изначально был mobx, далее мы искали более так сказать "коробочное" решение и выбрали GetX, по началу всё было ок, но потом обнаружились проблемы с ним.

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

Во-вторых некоторые проблемы с роутингом, таб навигацией (если интересно могу подробнее рассказать)

Понравилась идея MVC и DI, и хотелось их оставить в дальнейшем в разработке.

Решили отказаться от использования фреймворков в пользу отдельных пакетов, если с навигацией понятно пакетов много, то с DI боле менее подошёл GetIt, но не совсем на 100%, пришлось подкрутить

Теперь отвечая на ваш вопрос, не bloc потому что хотелось что-то в духе MVC подобное GetX только без оверхеда, только самое необходимое. Bloc как альтернатива в принципе можно использовать, но там несколько другой подход на ивентах. Тоже самое с auto_route это лишь выбор, дело вкуса, можно использовать go_router тем более что он неплохо доработался

GetX не просто так не любят. Это своеобразный аналог бутсрапа со всем и вся, только ещё и нарушающий бест практис флаттера, что приводит к утечкам, лагам и т.д.

GetX - это DI библиотечке.

Есть простые правила описанные в README.

Интересно:

  1. Какие такие бест практис флаттера GetX нарушает

  2. Каким боком GetX (DI библиотечка) можно сравнить с бутстрапом (ui библиотеке/фреймверк)

  3. откуда у вас взялись эти «утечки», «лаги» и тд

Вроде, обычный Bloc теперь используют без rx и всего этого.

Вероятно так, не отслеживаю новости по bloc'у. Возможно другие точнее подскажут

Rx можно для слоя данных юзать, чтобы в блоке подхватывать изменения

Добрый день. Спасибо за статью. Можно несколько вопросов?

  1. Чем не подошел go_router, какие плюсы auto_route по сравнению с ним?

  2. В ViewWidgetState будет не только UI, но и презентейшн-логика, которой может быть довольно много (в вашем примере onPressed), в контроллере нет доступа к методам жизненного цикла виджета - не хотелось ли вам вынести такую логику в другую сущность и оставить голую вьюшку?

  3. Не возникало ли ситуаций когда в контроллере был нужен контекст? Как решали.

  4. Тема модулей не раскрыта - выносятся ли фича модули в отдельные пакеты, используются ли для них отдельные репозитории, используете ли melos, если нет и просто в фича-папках лежит, почему называете это модулями - можно подробнее про этот момент?

Спасибо.

Добрый! Спасибо за вопросы)

  1. Выбор пакета для роутинга (а точнее для вложенной таб навигации) мы рассматривали года 2 назад, тогда с go_router возник ряд сложностей (вроде как не сохранялось состояние открытых вкладок на каждом табе, сложности с навигацией между вкладками таба, к примеру перейти с экрана 2 на табе 1, на экран 3 таба 2).

    Вдобавок документация описывала простые случаи и требовалось искать как сделать что-то посложнее. Сейчас попробовал найти сайт с документацией, но не нашел, видимо переехал в другое место. Кажется даже что по-другому описывалась конфигурация роутов нежели сейчас. Насколько помню в DevTools с использованием go_router отрисовывались все табы сразу и были проблемы с жестами ios и хардверной android кнопкой назад. Возможно сейчас все стало намного лучше и есть смысл попробовать go_router.

    Говоря про auto_route на самом деле мы не сравнивали его с другими пакетами, просто он первый который подошел и решил основные проблемы.

    Из плюсов стоит отметить: поддержка Cupertino, наличие наблюдателей за навигацией, гибкие возможности создания вложенной навигации в т.ч навигация между вкладками и родительской навигацией, осуществление навигации без контекста, возможность создания "промежуточного" виджета (например таба) где можно разместить общие состояния необходимые дочерним виджетам/экранам открывающихся по дочерним роутам.

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

  2. Про onPressed, как вариант, всю эту логику можно вынести в controller. Только если необходим контекст придется возвращаться в view. Для навигации например есть способ навигации без контекста, да это не очень правильно, но жутко удобно. В контроллере расширении (ViewController) есть хуки-методы жизненного цикла (наподобии как в GetX), там можно располагать логику. Т.е можно в виджете, можно в контроллере либо оба варианта.

  3. Да, возникали ситуации. Здесь необходимо возвращаться во view и там уже выполнять необходимую логику с context. Context пробрасывать в контроллер крайне нежелательно

  4. Тут на самом деле несколько подходов, можно делать монорепозиторий (привет melos) тогда модули будут в виде пакетов в одном воркспейсе (есть у нас такие проекты) плюс в том что разработчик не сможет импортировать что-то "левое" из другого модуля, как бы контроль. При желании каждый пакет-модуль можно вынести в отдельный репозиторий. Другой вариант - монолит с папочкой features, в которой как раз и лежат папки (а-ля модули) с фичами. Т.е какой-то специальной сущности модуль нет, так нам удобнее называть чтобы не путаться.

    Напишите если нужно что-то подробнее пояснить еще)

По поводу гороутера

1) Гороутер сырой и часто меняет апи

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

А в statefulShellRoute я ещё и баги неприятные ловил пару раз, которые именно с реализацией шелл роута свзязаны

Ага.
1) "Большое" это какое? Сотня экранов - это сотня экранов, под ними может быть не так много домена в облачных решениях. Три оси измерения: объем кода, соотношение объема домен/ui и количество людей показывают сложность проекта (если честно - моя личная метрика). Растет домен/объем или объем/люди - сложность растет. При этом, понятное дело, что прибавлению людей нелинейно работает. То есть вы доводите объем/чел до расслабленного 10-15К строк на человека как в тиньке, но у вас общий объем так же как там два ляма - тут чисто инфраструктурной ереси будет с лям. Уже просто потому что у вас так много людей и надо много бойлерплейта чтобы они друг другу не мешались. Но и 300К на 2-3 человека при толстом домене это офигеть как сложно, я делал. Так что тут закономерный вопрос - насколько большое и почему именно оно такое. Экраны это эмоциональная метрика, имхо.

2) Логика ui-слоя утаптывается в bloc(mvi)|mvvm(cubit)|mvp|mvc(mvc морально устарел уже потому что связность у него выше прочих, mvp, впрочем, тоже, хотя жить и с ним можно). Вы точно уверены что mvc в его классическом понимании где все завязаны на всех без модификаций это нормально по солиду? И логика ui-слоя - она логика отображения стейта. Стейт формируется от домена.

3) Если взять во внимание клин (я беру, он хороший). Стейт вьюшек композится из массы доменных стейтов как раз в "логике ui" то есть в контроллерах, презентерах, вьюмоделах, блоках. В этом архитектурном плане нет домена. Вообще. Фича-модули построенные на стейт-менеджмент паттернах - ок, но это, по сути, подходит под очень простое фронтенд-приложение и не более. Объем тут вторичен, бьется и параллелится оно легко на любом подходе, коммона там мизер, в основном сессия+ui-кит.

4) Собственно фича-модули. Часто (хотя не всегда, в виде исключения) есть доменные сущности типа той же сессии юзера, или сложносочиненных взаимозависимых репозиториев с синхронизацией замудренной в зависимости от авнономности аппа, и это делает дерево модулей сложным в обслуживании. Если оно не таково, то коммон-штуки дублируются не только прим большом количестве людей которые не знают что уже есть имплементации, но и даже в одно жало. Потому что проще копипаст, чем думать как слабосвязно прокинуть, я такое поддерживал, это боль. Вот это почему? Большое значит бьем, вы уверены что оно из большого не станет именно сложным?

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

Почему так? Дело в том, что при взаимодействии компании с дилерами очень много процессов и они достаточно сложные + интеграция с внешними системами/сервисами. Одни только тарифы чего стоят, их много, у всех разные параметры и в зависимости от комбинации этих параметров происходят те или иные действия (предлагается более выгодный тариф, подключаются дополнительные опции, устанавливается периодичность списания оплаты и тп).

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

Бэкенд тоже подкидывает задачи, когда по одному запросу надо вытащить справочники из других запросов, все склеить и подать в нужном виде.

Довольно интересные у вас метрики: объем кода, соотношение объема домен/ui. Как вы их считаете? Используете ли какие-то приложения/пакеты чтобы посчитать?

2) На наш взгляд mvc в том виде, котором предложена соответствует принципам SOLID ведь каждая часть занимается своим делом (своя зона ответственности), большие классы можно дробить на более мелкие (либо легко расширять функциональность/поведение), части взаимозаменяемы благодаря DI. Благодаря DI, кстати, "ослабляются" связи между компонентами, а зависимости для того/иного компонента легко "читаются" и понятны.

Разработка по таким принципам оказалась невероятно удобной и гибкой.

Насчет UI логики, тут как хотите можете хранить в вьюшке, можно в контроллере.

3) У нас возможно другое понимание, при создании стейта отталкиваемся от экрана/совокупности экранов. Если стейт на экран, то там должно быть только то, что необходимо для данного экрана, ничего лишнего. Если стейт на 2 экрана, то необходимое двум экранам и тд. Есть и глобальные стейты: стейт состояния авторизации, стейт приложения и тд. Через контроллер как раз происходит связка данных из нужных стейтов (типа как у вас "композится из массы доменных стейтов") и подается "наружу" вьюшке уже подготовленные данные того, что нужно. То что не нужно - прячется. Как таковые домены мы не выделяем, у нас не клин в классическом понимании.

4) Да вот, кстати с доменами возникают различные философские приколы, типа а токен авторизации это на доменном слое должно быть или инфраструктурном.

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

"Потому что проще копипаст" - из копипаста собственно все и выросло, когда поняли что делаем одно и тоже и лучше вынести некоторый common модуль и подключать к проектам

>>Большое значит бьем, вы уверены что оно из большого не станет именно сложным?

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

Выглядит как MVC.

Так и есть

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