Сравнение архитектурных паттернов GetX и BLoC

Всем привет! Я Айдар Мавлетбаев, Flutter-разработчик в AGIMA. В самом начале любого проекта очень важно выбрать архитектурный паттерн, ведь именно это может спасти ваш проект на более поздних этапах. В статье сравним архитектуры BLoC и MVC, подробно рассмотрим библиотеку GetX, выделим ее плюсы и минусы. В этом нам помогут два простых примера: это функция авторизации и List Data.

07e98156995eabb4257bdafbec791094.png

Немного об архитектуре

Существует огромное количество архитектурных паттернов: Clean Architecture, DDD, MVC, BLoC и так далее. Обычно архитектуру выбирают, исходя из сложности приложения, бюджета, времени и других факторов. Здесь важно понимать, что от подхода, который мы выберем в начале разработки, зависит удобство поддержки уже готового приложения в будущем.

4ee4a077e319771cf5375078368abff4.png

Каждый паттерн имеет свои плюсы и минусы. Но в статье мы рассмотрим именно MVC и BLoC.

Про BLoC

BLoC (Business Logic Component) — это архитектура управления состоянием, которая отделяет бизнес-логику от UI-слоя в разработке программного обеспечения. Паттерн BLoC часто используется в разработке приложений на Flutter для управления состоянием в реактивном и эффективном режиме.

0249a3b8554af5d56df78a999996aea6.png

BLoC разделяет приложение на UI, Bloc и Data, а сам он состоит из трех ключевых компонентов:

  • Событие. Это входные данные, которые вызывают изменение состояния приложения. События могут быть как пользовательскими взаимодействиями (например, нажатие кнопок), так и системными событиями (например, сетевые запросы).

  • Bloc. Это компонент бизнес-логики, который обрабатывает события и обновляет состояние приложения. Bloc отвечает за управление состоянием приложения и взаимодействие с UI-слоем для обновления представления.

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

Менеджер состояния BLoC прослушивает события и обновляет состояния приложения. Затем UI-слой прослушивает состояние и обновляет представление при изменении состояния.

Про MVC и GetX

Здесь схема похожая. Паттерн MVC (Model-View-Controller) тоже разделяет приложение на три слоя:

  1. Модель (Model) — это компонент, который отвечает за хранение и обработку данных приложения. Модель представляет собой набор классов, которые описывают структуру данных и предоставляют методы для работы с ними.

  2. Вид (View) — отвечает за отображение данных на экране. Вид представляет собой набор виджетов, которые отображают данные, полученные из модели.

  3. Контроллер (Controller) — отвечает за взаимодействие между моделью и видом. Контроллер обрабатывает события, генерируемые пользователем, и изменяет состояние модели в соответствии с этими событиями. Затем контроллер обновляет вид, чтобы отобразить изменения.

Ну, а GetX — это микрофреймворк, в котором есть всё для разработки полноценного MVP, Routing, State Manager, Localizations и т. д. По своей сути, это усовершенствованный Server Locator 2.0, который дает возможность не использовать Context.

883cafb9f86acf6ba0c8e6308788350d.png

BLoC vs MVC

Так выглядит файловая структура BLoC и MVC:

2b3618d5301db5989cc9d3b56254554d.png

В BLoC мы видим четкое разделение на три слоя: в Data лежат модели и репозитории, Bloc содержит бизнес-логику, ну и сверху всего UI. 

В MVC нет репозиториев, потому что на небольших проектах всю логику Local Data или Remote Data можно прокинуть сразу в контроллер, а здесь их два.

BLoC vs GetX

Теперь давайте сравним эти подходы на двух простых примерах — авторизации и List Data, — каждый отдельно на BLoC и GetX. Это типичный кейс для большинства Flutter-приложений в заказной разработке.

Первый пример: функция авторизации

df209781b847fad2685b8f74c870e3cf.png

BLoC:

AuthEvent. В этом классе прописаны все события, которые будут вызываться в UI — Sign up, Sign in и Sign out. Здесь ничего сложного. 


AuthBloc.
Выполняет логику события, возвращает состояние.

Здесь обрабатывается Event, который мы вызывали, появляется зависимость от To do и мы задаем какую-то переменную.


AuthState. Класс состояния нашего UI.

State — обычный глобальный класс. Он один, и здесь уже есть конструктор с зависимостью. Как и в AuthBloc, мы задаем какие-то изменения в наших переменных User или Failure.

GetX:

AuthController. Здесь один класс, без строгого разделения на слои, как в BLoC. На картинке выше видно, что запись минимальная, а результат такой же.

5231015b1efd862b3f88d86c651e8ddb.png

BLoC:

AuthPage. Страница авторизации. Здесь используется несколько Bloc-виджетов, но основной — BlocConsumer.


При помощи BlocProvider мы получаем зависимость в виде блока. А уже внутри BlocConsumer мы работаем с бизнес-логикой.

GetX:

AuthView. Аналогичный UI, но меньше кода.

Здесь всё тоже суперпросто. Мы получаем через Get.put зависимость authController и при нажатии кнопки вызываем метод. И всё готово. 

Второй пример: List Data

ac395879703b59bd0e8b6fa72587d0c8.png

BLoC:

HomeEvent. Обычный класс событий, ничем не отличается по структуре от
AuthEvent.

HomeBloc. Здесь стоит обратить внимание, что в Emit мы передаем класс состояния, а не просто вызываем State и меняем там переменную. 


HomeState.
Класс состояния. Отличается от AuthState тем, что содержит несколько классов — это DataFetched и DataFailure. У каждого из них есть Pagestate.

GetX:

HomeController. Здесь снова один класс, который использует GetXController. Но главное — он использует StateMixing. StateMixing похож на реализацию в BLoC, но имеет у себя под капотом уже замоканное состояние, которое, увы, мы не можем дополнить или поменять. Он хранит в себе RxStatus.success, RxStatus.error, Empty, Loading.

Еще в StateMixing мы передаем дженерик — то, что мы хотим видеть в нашем UI. Когда мы загружаем страницу, внутри бизнес-логики срабатывает OnInit. Мы вызываем состояние RxStatus.success и прописываем методы, как и с block.refreshData и addData. Дальше также через Try Catch вызываем либо RxStatus.success, либо RxStatus.error. 

7abc92cb76fde934a9387110017cc69f.png

BLoC:

HomePage. Немного схоже с AuthPage, только здесь используем BlocBuilder.

Получаем зависимость тоже через BlocProvider. Здесь мы используем не BlocConsumer (потому что он нам не нужен), а только BlocBuilder. Но логика такая же, и события также вызываются Refresh-индикатором.

GetX:

HomeView. Чтобы получить зависимость, в HomeView мы используем Get.put. Для построения UI мы используем HomeController и вызываем в нем .obx. homeController.obx — это то же, что и BlocBuilder. Здесь мы не прописываем условия, а вызываем параметры. 

Плюсы и минусы GetX и BLoC

Подведем итоги по каждому подходу и сравним их по четырем критериям:

4acfa4ead21275dd2f4869bdd7e1418b.png

Простота в использовании

У GetX более низкий порог входа, он проще в освоении, чем BLoC. Он предоставляет простой и понятный API для управления состоянием, маршрутизации и управления зависимостями.

Масштабируемость

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

Тестирование

В BLoC есть мощные средства для тестирования потоков событий и состояний, например bloc_test.

Возможности

GetX предоставляет широкий спектр возможностей для маршрутизации и управления состоянием и зависимостями.

Что же выбрать при масштабировании Flutter-приложения?

a2159596db30476f87fb577ddb6d1a86.png

Выбор между GetX и BLoC для масштабирования приложения зависит от конкретных требований проекта и ваших предпочтений. Если приложение относительно небольшое и простое, то GetX может быть более подходящим решением. Он предоставляет простой и понятный API для управления состоянием.

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

P.S. Недавно я выступал с этой темой на Стачке 2024. Тогда аудиторию очень заинтересовал GetX, было много вопросов. При этом, из всего зала с GetX имели дело единицы.

Я буду рад ответить на ваши вопросы и здесь, в комментариях. Интересно узнать, что вы думаете о выборе между GetX и BLoC. Делитесь мыслями.

P.P. S. Мой коллега Саша Ворожищев ведет классный канал про Flutter — загляните как-нибудь.

Что еще почитать

© Habrahabr.ru