mirror of
https://github.com/smmarty/friflex_flutter_starter.git
synced 2025-12-21 17:10:45 +00:00
390 lines
19 KiB
Plaintext
390 lines
19 KiB
Plaintext
# Правила разработки 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
|