# Правила разработки Flutter проекта Этот файл содержит правила и стандарты разработки для проекта. Все правила обязательны к соблюдению при написании кода. **ВАЖНО:** Перед каждым PR необходимо отформатировать код (`dart format .`) и проверить анализатор на отсутствие сообщений (`dart analyze`). --- # Стиль кода ## Именование ### Интерфейсы Все интерфейсы в приложении должны начинаться с заглавной буквы "**I**". Примеры: **IAuthRepository**, **IProfileRepository**, **IMainRunner** и т.д. Таким образом, сразу видно, что работаешь с интерфейсом. Пример: ```dart /// Интерфейс - **IUserRepository** abstract interface class IUserRepository {} /// Основная реализация (prod и stage окружения) class UserRepository implements IUserRepository {} /// Иная реализация (мок, локальное хранилище) должна содержать /// постфикс функциональности: /// - Network - сетевое взаимодействие. /// - Local - локальное хранилище. /// - Mock - мок репозиторий. class UserRepositoryLocal implements IUserRepository {} ``` ### Классы - Репозитории Репозитории должны содержать в конце название источника данных (если используется мок или локальное хранилище). Основная реализация не должна содержать постфикса. Примеры: - Интерфейс - **IAuthRepository** - Основная реализация (prod и stage окружения) - **AuthRepository** - Мок (мок данные) - **AuthRepositoryMock** - Локальное хранилище (например бд или просто имитация данных) - **AuthRepositoryLocal** ### Файлы Используется snake_case. Название файла должно иметь следующую структуру: `[раздел]_[тип].dart` Примеры: `user_details_screen.dart`, `shop_entity.dart` ### Классы Название классов UpperCamelCase. Для создания приватных классов используем префикс `_`. Название класса в конце должно содержать в себе тип. Примеры: **UserEntity**, **AdultDialog** ## Методы Название метода в начале должно содержать в себе действие (глагол): - fetch - put - update - delete - и так далее Примеры: ```dart int fetchFirstElement() {} ``` ```dart void updateFirstElement() {} ``` **ВАЖНО:** Название метода не должно содержать в себе `And`/`Or`, и метод соответственно не должен выполнять подобную логику. ## Переменные и константы Константы именуются также lowerCamelCase. Примеры: ```dart const String carItem = 'default'; ``` или ```dart final String userName = 'default'; ## Виджеты Виджеты именуются UpperCamelCase. В названии виджетов не должно содержаться слово `widget`. ### Экраны Экраны, используемые в роутинге, именуются с постфиксом `Screen`. Пример: **ShopListScreen** ### Содержимое экрана Виджеты, отображающие содержимое экрана, именуются с постфиксом `View`. Пример: **ShopListView** ### Глобальные виджеты Глобальные виджеты именуются с приставкой `App`. Пример: **AppButton** ## Структура класса Объявления элементов класса должны располагаться в следующем порядке: 1. **Constructors** - constructors - named-constructors - factory-constructors 2. **Static** - public-static-methods - private-static-methods - public-static-const-fields - private-static-const-fields - public-static-final-fields - private-static-final-fields - public-static-fields - private-static-fields 3. **Fields** - public-final-fields - private-final-fields - public-fields - private-fields 4. **Getters/Setters** - public-getters-setters - private-getters-setters 5. **Methods** - overridden-methods - public-methods - protected-methods - private-methods --- # Ведение документации и комментариев ## Документация ### Основные правила ведения документации в проекте - Документация оформляется над описываемым объектом с использованием `///` - Документацией необходимо покрывать все классы, конструкторы, поля, геттеры, сеттеры, методы, фабрики, все кастомные объекты - Документация должна: - указывать на назначение объекта - содержать исчерпывающее описание объекта - быть краткой и емкой - быть понятной для любого разработчика ### Шаблоны и примеры документации объектов #### Документация классов ```dart /// {@template new_class} /// Класс для {описание назначения и реализуемого функционала в классе}. /// {@endtemplate} class NewClass {} ``` Пример: ```dart /// {@template app_button} /// Класс для реализации кастомизированной кнопки. /// {@endtemplate} class AppButton {} ``` #### Документация конструкторов и фабрик Если конструктор один, то достаточно указать `{@macro new_class}`. `{@macro new_class}` дублирует на конструктор указанную в рамках описания класса документацию, поэтому описание класса должно в таком случае включать все передаваемые в конструктор параметры. Если конструкторов несколько, описания должны отражать их отличия друг от друга. Фабрики описываются по такому же принципу. ```dart /// {@macro new_class} const NewClass(); /// Создает {описание создаваемого фабрикой объекта}. /// Принимает: /// - [json] - {описание параметра} factory NewClass.fromJson(Map json) {} ``` #### Документация полей классов В классе необходимо описывать каждое поле по отдельности. Если поле ссылается на другое поле или зависит от него, необходимо это указывать в описании. ```dart /// Возраст пользователя. final int age; /// Индикатор совершеннолетия пользователя. Принимает значение true, когда [age] больше 18 лет. final bool isAdult; ``` #### Документация геттеров/сеттеров ```dart /// Получения доступа к {описание данных, которые получает геттер} int get newGetter => ... /// Установка значения для {описание работы сеттера} set newSetter(int setterValue) => ... ``` #### Документация методов Методы должны описывать их основное назначение. Если метод сложный, требуется указывать дополнительное описание его работы ниже через пропущенную строку. Если метод принимает какие-либо параметры, каждый параметр должен быть описан по отдельности списком с прямой ссылкой. Если метод возвращает какие-либо значения, они должны быть описаны. Желательно указать, что вернет метод в случае возникновения ошибки. ```dart /// Метод для {описание назначения метода}. /// {описание особенностей работы метода}. /// Принимает: /// - [param] - {описание назначения параметра}. void newMethod({required String param}) { ... } ``` Пример: ```dart /// Метод для расчета температуры. /// Принимает: /// - [grad] - параметр необходим для расчета температуры. /// Возвращает: /// - температуру в градусах. /// При ошибке расчета возвращается null. int? calcTemperature({required int grad}) { // Реализация } ``` ## Комментарии ### Основные правила комментирования кода в проекте - Комментарии оформляются над описываемым участком кода с использованием `//` - Комментировать необходимо те участки кода, которые действительно нуждаются в дополнительном описании - Комментарий не должен повторять легко читаемые участки кода Примеры неправильного использования: ```dart if (flag != true) // не нужно описывать как "Если значение не равно true..." ``` Примеры правильного использования: ```dart if (isAurora) { // Так как Аврора не может открывать WebView, // то заменяем на открытие внешнего браузера } ``` ## TODO ### Основные правила создания TODO в проекте - TODO оформляются согласно условиям форматирования линтера с указанием имени разработчика, кто находится в контексте проблемы и сможет ее прокомментировать - TODO необходимо оставлять на тех участках кода, которые требуют дальнейшей доработки - Если известно, в рамках какой задачи будет доработан помечаемый участок кода, ссылку на задачу необходимо оставить в скобках Пример: ```dart // TODO(username): Оптимизировать алгоритм сортировки ``` --- # Ведение проекта в git ## Создание commits/pull-request - Язык описания PR - Русский - Описание должно отражать краткую суть изменений - Коммит/PR должен содержать: - исчерпывающую информацию об изменениях - ссылку на задачу в таск-трекер - Перечисление deprecated-кода (если есть) ### Типы коммитов согласно convention - **feat**: — новая функция - **fix**: — исправление ошибок - **refactor**: — Изменение кода, которое не исправляет ошибку и не добавляет функции (рефакторинг кода) - **build**: — изменения, влияющие на систему сборки или внешние зависимости (примеры областей (scope): android, ios, linux и так далее) - **docs**: — изменения только в документации - **chore**: — добавление/обновление/настройка инструментов и библиотек (пример: pubspec.yaml) - **test**: — добавление недостающих тестов или исправление существующих тестов - **ci**: — изменения в файлах конфигурации и скриптах CI (примеры областей: папка CI) # Структура проекта Рекомендуемая структура проекта (может отличаться в зависимости от проекта и согласований) - **/** - папка проекта - **/assets** - директория расположения графических ресурсов - **/tools/** - все необходимые инструменты для проекта - **/docs** - документация проекта - **/env** - папка, с внешними переменными окружения - **/lib** - код на Dart, Flutter-приложение - **/app** - содержит основные настройки нашего приложения - **/data** - общие поставщики данных - **/domain** - общий слой - **/presentation** - общий слой - **/di** - файлы конфигурации зависимостей - **/router** - все, что касается роутинга - **/features** - фичи приложения, для каждой фичи создается отдельная папка - **/feature_name** - подробнее см в разделе Структура feature папок - **/data** - **/domain** - **/presentation** - **/gen** - для сгенерированных файлов - **/targets** - таргеты для сборок - **/prod.dart** - сборка для prod - **/dev.dart** - сборка разработки на моковых репозиториях - **/stage.dart** - сборка для stage окружения ## Пример структуры feature папок - **/data** - слой данных - **/dto** - реализация DTO (data transfer object) - **/repository** - реализации репозиториев - **/domain** - слой бизнес логики - **/entity** - модели которые используются для работы в domain/presentation слоях - **/repository** - интерфейсы репозиториев, которые используются в domain слое - **/state** - state-management - **/service** - реализации сервисов - **/presentation** - слой представления - **/screens** - все экраны должны заканчиваться на Screen, например UserProfileScreen - **/components** - виджеты, которые необходимы для работы в presentation слое. Например: SuperButton, AppTextFields итд ## Пояснение к структуре feature папок ### Data (слой данных) Этот слой является поставщиком данных. - **Repository** - сущность, которая реализует внутри себя предоставление данных. Должен реализовывать какой либо интерфейс репозитория из domain слоя - **DTO** - Dto(Data Transfer Object) модели, и модели с которыми происходит работа в data слое. Например: UserDto ### Domain (слой бизнес логики) - **Entity** - должны быть в максимально удобном виде для работы внутри Domain и Presentation. Например: UserEntity, ShopEntity - **State** - управления состоянием - state manager - **Service** - различные сервисы, для выполнения различных задач - **interfaces** - интерфейсы репозиториев, которые используются в domain слое ### Presentation (слой представления) - **components** - widget'ы которые реализуют работу какого либо визуального компонента (Buttons, TextFields, Lists, итд). Например: ShopList, RateButton. Модальные окна - **screens** - widget'ы которые представляют собой экран приложения. Например: UserInfoScreen ## Основные правила общения объектов между папками ### В рамках всего приложения - Объекты внутри фичи должны быть инкапсулированы и не могут использоваться в других feature - Если есть необходимость использовать объект в нескольких feature, его нужно вынести в папку app и использовать как глобальный для всего приложения - Сервис, который должен быть использован в нескольких feature, создается как отдельная feature - Если создаваемый сервис является платформно-зависимым, его необходимо выносить в app_services. В приложении должен быть только интерфейс ### В рамках одной feature - Объекты data слоя не должны ничего знать про объекты слоя presentation. Имеют доступ к объектам entity из domain слоя для преобразования DTO в Entity - Объекты domain слоя не должны ничего знать про объекты слоя data, используемый экземпляр репозитория передается в объекты domain слоя через интерфейс репозитория, расположенного в этом же domain слое - Объекты domain слоя не должны ничего знать про слой presentation, не должны использовать компоненты библиотек ui, material, cupertino, widget и прочих, не должны использовать context - Объекты presentation слоя не должны ничего знать про объекты слоя data, все взаимодействия непосредственно через объекты слоя domain