Files

390 lines
19 KiB
Plaintext
Raw Permalink Normal View History

# Правила разработки 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<String, dynamic> 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