mirror of
https://github.com/smmarty/friflex_flutter_starter.git
synced 2025-12-22 01:20:46 +00:00
docs(cursorrules,copilot-instructions): обновить правила разработки и нструкции для AI-агента (#41)
This commit is contained in:
421
.cursorrules
421
.cursorrules
@@ -1,64 +1,389 @@
|
|||||||
# Правила для Cursor AI
|
# Правила разработки Flutter проекта
|
||||||
|
|
||||||
## Соглашение о коммитах
|
Этот файл содержит правила и стандарты разработки для проекта. Все правила обязательны к соблюдению при написании кода.
|
||||||
|
|
||||||
При генерации сообщений коммитов ВСЕГДА используй следующий формат:
|
**ВАЖНО:** Перед каждым PR необходимо отформатировать код (`dart format .`) и проверить анализатор на отсутствие сообщений (`dart analyze`).
|
||||||
`<тип>(<контекст1>,<контекст2>,...): <короткое описание>`
|
|
||||||
|
|
||||||
Для Pull Request формат:
|
---
|
||||||
`<тип>(<контекст1>,<контекст2>,...): <короткое описание>`
|
|
||||||
|
|
||||||
Где:
|
# Стиль кода
|
||||||
- `<тип>` - тип коммита (см. ниже)
|
|
||||||
- `<контекст>` - модули/компоненты, которые изменяются (можно указать несколько через запятую)
|
|
||||||
- `<короткое описание>` - краткое описание изменений на русском языке
|
|
||||||
|
|
||||||
### Типы коммитов согласно convention:
|
## Именование
|
||||||
|
|
||||||
- **feat** - новая функция
|
### Интерфейсы
|
||||||
- **fix** - исправление ошибок
|
|
||||||
- **refactor** - изменение кода, которое не исправляет ошибку и не добавляет функции (рефакторинг кода)
|
|
||||||
- **build** - изменения, влияющие на систему сборки или внешние зависимости (примеры областей: android, ios, linux и так далее)
|
|
||||||
- **docs** - изменения только в документации
|
|
||||||
- **chore** - добавление/обновление/настройка инструментов и библиотек (пример: pubspec.yaml)
|
|
||||||
- **test** - добавление недостающих тестов или исправление существующих тестов
|
|
||||||
- **ci** - изменения в файлах конфигурации и скриптах CI (примеры областей: папка CI)
|
|
||||||
|
|
||||||
### Контекст (scope):
|
Все интерфейсы в приложении должны начинаться с заглавной буквы "**I**".
|
||||||
|
|
||||||
Указывай модуль или компонент, который изменяется. Можно указать несколько через запятую:
|
Примеры: **IAuthRepository**, **IProfileRepository**, **IMainRunner** и т.д.
|
||||||
- `app` - основное приложение
|
|
||||||
- `di` - dependency injection
|
|
||||||
- `auth` - аутентификация
|
|
||||||
- `api` - API endpoints
|
|
||||||
- `db` - база данных
|
|
||||||
- `config` - конфигурация
|
|
||||||
- `i18n` - интернационализация
|
|
||||||
- `scripts` - скрипты
|
|
||||||
- `pubspec` - зависимости проекта
|
|
||||||
- `android`, `ios`, `linux` - платформы
|
|
||||||
- другие модули проекта
|
|
||||||
|
|
||||||
### Примеры правильных коммитов:
|
Таким образом, сразу видно, что работаешь с интерфейсом.
|
||||||
|
|
||||||
- `feat(app,di,auth): Добавить локальный репозиторий`
|
Пример:
|
||||||
- `fix(api): Исправить валидацию запросов`
|
|
||||||
- `docs(i18n): Обновить руководство по генерации`
|
|
||||||
- `refactor(handler): Оптимизировать обработку запросов`
|
|
||||||
- `test(security): Добавить тесты для rate limiter`
|
|
||||||
- `chore(pubspec): Обновить зависимости`
|
|
||||||
|
|
||||||
## Язык
|
```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 - Русский
|
||||||
|
- Описание должно отражать краткую суть изменений
|
||||||
|
- Коммит/PR должен содержать:
|
||||||
|
- исчерпывающую информацию об изменениях
|
||||||
|
- ссылку на задачу в таск-трекер
|
||||||
|
- Перечисление deprecated-кода (если есть)
|
||||||
|
|
||||||
## Стиль кода
|
### Типы коммитов согласно convention
|
||||||
|
|
||||||
- Следуй Dart/Flutter conventions
|
- **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
|
||||||
|
|||||||
427
.github/copilot-instructions.md
vendored
427
.github/copilot-instructions.md
vendored
@@ -1,63 +1,390 @@
|
|||||||
Инструкция для AI-агента (ревью Flutter-кода)
|
# Правила разработки Flutter проекта
|
||||||
|
|
||||||
Вы — строгий ревьюер кода Flutter-приложения по принципам **Clean Architecture**.
|
Этот файл содержит правила и стандарты разработки для проекта. Все правила обязательны к соблюдению при написании кода.
|
||||||
Ваша задача — проводить ревью кода, выявлять ошибки и давать корректные рекомендации.
|
|
||||||
Bloc и Cubit должны находиться в слое domain.
|
|
||||||
Контекст (справочно, не включать в ответ):
|
|
||||||
- Архитектура: три слоя (presentation, domain, data), каждый в своей папке.
|
|
||||||
- State management: flutter_bloc.
|
|
||||||
- Навигация: go_router.
|
|
||||||
- HTTP: dio.
|
|
||||||
- Анализатор: flutter_lint_rules.
|
|
||||||
- Feature-first структура: lib/features/<feature_name>/{data,domain,presentation}.
|
|
||||||
|
|
||||||
---
|
**ВАЖНО:** Перед каждым PR необходимо отформатировать код (`dart format .`) и проверить анализатор на отсутствие сообщений (`dart analyze`).
|
||||||
## Обзор запроса на вытягивание
|
|
||||||
1. В начале ответа укажите решение: **«Принять»** или **«Отклонить»**.
|
|
||||||
2. Укажите оценку: **Оценка: X/100** (X — фактический балл).
|
|
||||||
3. Кратко и строгим тоном перечислите ключевые проблемы:
|
|
||||||
- Архитектура (слои `presentation`, `domain`, `data`).
|
|
||||||
- Принципы **DRY, KISS, SOLID**.
|
|
||||||
- Разделение ответственности (BLoC, репозитории, DTO, UI).
|
|
||||||
- Безопасность (`dio`, SSL, валидация данных).
|
|
||||||
- Кодстайл и соглашения (`flutter_lint_rules`).
|
|
||||||
- Цикломатическая сложность: избегать чрезмерно сложных функций и классов (оптимально ≤ 10).
|
|
||||||
|
|
||||||
4. При необходимости предоставьте исправленный фрагмент кода или улучшенное решение.
|
|
||||||
5. Ответ должен быть в **Markdown** и полностью на **русском языке**.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Критерии оценки
|
# Стиль кода
|
||||||
- **Чистая архитектура**: корректное разделение слоёв, отсутствие Flutter-зависимостей в `domain`.
|
|
||||||
- **KISS**: минимальная сложность и читаемость решений.
|
|
||||||
- **DRY**: отсутствие дублирования логики, использование утилит/виджетов.
|
|
||||||
- **SOLID**: правильная декомпозиция классов и интерфейсы вместо жёстких связей.
|
|
||||||
- **Безопасность**: корректная работа с API, валидация данных.
|
|
||||||
- **Кодстайл**: именование, структура файлов и папок.
|
|
||||||
- **Цикломатическая сложность**: методы и классы должны быть простыми, без избыточных ветвлений.
|
|
||||||
|
|
||||||
|
## Именование
|
||||||
|
|
||||||
---
|
### Интерфейсы
|
||||||
|
|
||||||
## Пример ответа
|
Все интерфейсы в приложении должны начинаться с заглавной буквы "**I**".
|
||||||
**Отклонить**
|
|
||||||
**Оценка: 58/100**
|
Примеры: **IAuthRepository**, **IProfileRepository**, **IMainRunner** и т.д.
|
||||||
- Нарушен принцип DRY: HTTP-запрос продублирован в двух репозиториях.
|
|
||||||
- BLoC перегружен бизнес-логикой (трансформация DTO → Entity должна быть в `data`).
|
Таким образом, сразу видно, что работаешь с интерфейсом.
|
||||||
- Domain-слой содержит зависимость от Flutter — это недопустимо.
|
|
||||||
- Отсутствует обработка ошибок и валидация данных в `dio`.
|
Пример:
|
||||||
|
|
||||||
**Исправленный фрагмент:**
|
|
||||||
```dart
|
```dart
|
||||||
// Вместо дублирования запроса используем общий DataSource
|
/// Интерфейс - **IUserRepository**
|
||||||
class UserRemoteDataSource {
|
abstract interface class IUserRepository {}
|
||||||
final Dio dio;
|
|
||||||
UserRemoteDataSource(this.dio);
|
|
||||||
|
|
||||||
Future<UserEntity> fetchUser(String id) async {
|
/// Основная реализация (prod и stage окружения)
|
||||||
final response = await dio.get('/users/$id');
|
class UserRepository implements IUserRepository {}
|
||||||
return UserDto.fromJson(response.data).toEntity();
|
|
||||||
}
|
/// Иная реализация (мок, локальное хранилище) должна содержать
|
||||||
|
/// постфикс функциональности:
|
||||||
|
/// - 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 = 'user';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Виджеты
|
||||||
|
|
||||||
|
Виджеты именуются 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
|
||||||
|
|||||||
Reference in New Issue
Block a user