Feature-Based Flutter Architecture

New-comers onboarding

Поговорим подробнее про первую задачу.

1 Architecture & project structure, modularisation, module-to-module interoperating, code ownership in the team

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

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

Вторая идея — это классическая чистая архитектура, когда в рамках каждого модуля илу зависимости от дяди Боба. Стараемся по возможности, каждый раз проектируя класс, задаваться вопросом -, а на каком уровне нашей горизонтальной иерархии он живет?

1ebf705fb5c67de695d66469b81da9d5.png

Может это доменная сущность, отвечающая за базовую бизнес логику нашего модуля (бизнес-домена)? Тогда не стесняемся прямо положить его в поддерево domain нашего модуля и, по можно даже сделать правилом (в дополнение к правилу «кричащей архитектуры».) называть эти классы Entity (UserEntity, TripEntity). И пишем его на чистом Dart с минимальным или вообще нулевым количеством внешних зависимостей.

Для Presentation или UI слоя мы любим пользоваться тут BLoC паттерном для написания «бизнес-логики компонентов», по сути контроллеров визуальных виджетов. Удобное средство отделения «мух от котлет». Хорошо ведь, когда возвращаешься к своему коду, написанному год назад — открываешь states.dart, потом events.dart и уже понятно, что там будет в bloc.dart и примерно ясно, как будет отображать это widget.dart . То же справедливо, когда это вообще чужой код и ты видишь его впервые. И вообще тот человек уже уволился. Однако все понятно и понятно, что и как делать.

Если это больше похоже на объект, который реализует взаимодействие с конкретным фреймворком или API, завязан на конкретного поставщика, то ему место в data слое.

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

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

Посмотрим на наш пример-паттерн и посмотрим, что принцип «кричащей архитектуры» может о нем рассказать.

Untitled

Untitled

У нас монорепозиторий, управляемый melos, в котором

  • два приложения app1(1) и app2(2)

  • app1 приложение, которое собирает свой функционал из других модулей/пакетов (features) и содержит минимум функций — платформенные вещи (ios, android, web, macosx), точку входа, конфигуратор из окружения/DI и сам сборщик зависимостей.

  • видим, что есть три большие фичи (модуля) — это авторизация (3), inbox (4), nofitications (6).

  • фича app1_main_screen (5) — выделена в отдельную фичу для «чистоты» app1, но может быть перенесена внутрь

  • feature_auth построена по принципу чистой архитектуры и содержит слои domain (7) и ui (8)

  • выделен в отдельный модуль auth_provider1 (9), который тоже построен по принципу чистой архитектуры и содержит реализацию авторизации фичи 3 через конкретного поставщика (например, firebase). Таким образом, если для app2, например, нужна будет реализация авторизации через другого поставщика, то весь код, экраны и тп feature_auth использоваться могут, а auth_provider1, а вместе с ним и firebase даже не будет включаться в зависимости и не участвовать в сборке.

  • вспомогательные пакеты 10 для решения остальных задач изначального списка [1]

Отдельный большой вопрос, это как модули вида features должны/могут зависеть от других features. В идеале никак, но по факту, жизнь сложнее и это тема отдельного обсуждения.

Habrahabr.ru прочитано 1833 раза