Структура проекта и соглашения¶
Обзор¶
Этот документ описывает организационную структуру проекта CulicidaeLab Flutter и соглашения по кодированию, используемые во всей кодовой базе. Понимание этой структуры необходимо для эффективной разработки и поддержания согласованности в проекте.
Структура корня проекта¶
culicidaelab/
├── .devcontainer/ # Конфигурация контейнера разработки
├── android/ # Конфигурация и файлы сборки для Android
├── assets/ # Ресурсы приложения (изображения, модели, шрифты и т.д.)
├── docs/ # Документация проекта
├── integration_test/ # Файлы интеграционных тестов
├── ios/ # Конфигурация и файлы сборки для iOS
├── lib/ # Основной исходный код Dart
├── scripts/ # Скрипты сборки и утилиты
├── test/ # Модульные тесты и тесты виджетов
├── analysis_options.yaml # Конфигурация анализатора Dart
├── dartdoc_options.yaml # Опции генерации документации
├── devtools_options.yaml # Конфигурация Flutter DevTools
├── l10n.yaml # Конфигурация локализации
├── pubspec.yaml # Зависимости и метаданные проекта
└── README.md # Обзор проекта и быстрый старт
Структура исходного кода (lib/)¶
Директория lib/ следует организации на основе функций с четким разделением ответственности:
lib/
├── l10n/ # Интернационализация и локализация
│ ├── app_en.arb # Переводы на английский
│ ├── app_es.arb # Переводы на испанский
│ ├── app_ru.arb # Переводы на русский
│ ├── app_localizations.dart # Сгенерированный класс локализации
│ └── app_localizations_*.dart # Сгенерированные языко-специфические классы
├── models/ # Модели данных и сущности
│ ├── disease_model.dart # Модель информации о заболеваниях
│ ├── mosquito_model.dart # Модель видов комаров
│ ├── observation_model.dart # Модель данных наблюдений пользователя
│ └── web_prediction_result.dart # Модель результата предсказания ИИ
├── providers/ # Провайдеры управления состоянием
│ └── locale_provider.dart # Управление состоянием языка/локали
├── repositories/ # Слой доступа к данным
│ ├── classification_repository.dart # Доступ к данным классификации ИИ
│ └── mosquito_repository.dart # Доступ к данным комаров и заболеваний
├── screens/ # Экраны и страницы UI
│ ├── classification_screen.dart # Захват фото и классификация
│ ├── disease_detail_screen.dart # Информация об отдельном заболевании
│ ├── disease_info_screen.dart # Список информации о заболеваниях
│ ├── home_screen.dart # Основной экран навигации
│ ├── mosquito_detail_screen.dart # Детали отдельного комара
│ ├── mosquito_gallery_screen.dart # Галерея видов комаров
│ ├── observation_details_screen.dart # Детали наблюдения пользователя
│ └── webview_screen.dart # Отображение веб-контента
├── services/ # Бизнес-логика и внешние интеграции
│ ├── classification_service.dart # Сервис вывода модели ИИ
│ ├── database_service.dart # Операции локальной базы данных
│ ├── pytorch_lite_model.dart # Обертка модели PyTorch (устаревшая)
│ ├── pytorch_wrapper.dart # Интеграция модели PyTorch
│ └── user_service.dart # Данные пользователя и настройки
├── view_models/ # Слой бизнес-логики MVVM
│ ├── classification_view_model.dart # Логика рабочего процесса классификации
│ ├── disease_info_view_model.dart # Логика информации о заболеваниях
│ └── mosquito_gallery_view_model.dart # Логика просмотра галереи
├── widgets/ # Переиспользуемые компоненты UI
│ ├── custom_empty_widget.dart # Виджет пустого состояния
│ └── icomoon_icons.dart # Определения пользовательских иконок
├── locator.dart # Конфигурация внедрения зависимостей
└── main.dart # Точка входа приложения
Архитектурные слои¶
1. Слой представления¶
- Расположение:
lib/screens/,lib/widgets/ - Назначение: Компоненты пользовательского интерфейса и обработка взаимодействия пользователя
- Соглашение именования:
*_screen.dartдля полных экранов,*_widget.dartдля переиспользуемых компонентов
2. Слой бизнес-логики¶
- Расположение:
lib/view_models/,lib/providers/ - Назначение: Управление состоянием приложения и бизнес-правила
- Соглашение именования:
*_view_model.dartдля логики MVVM,*_provider.dartдля провайдеров состояния
3. Слой доступа к данным¶
- Расположение:
lib/repositories/ - Назначение: Абстрактный доступ к данным и координация между сервисами
- Соглашение именования:
*_repository.dart
4. Сервисный слой¶
- Расположение:
lib/services/ - Назначение: Внешние интеграции, операции с базой данных и специализированная функциональность
- Соглашение именования:
*_service.dart
5. Слой данных¶
- Расположение:
lib/models/ - Назначение: Структуры данных и определения сущностей
- Соглашение именования:
*_model.dart
Организация ресурсов¶
assets/
├── database/ # Файлы инициализации базы данных
│ └── database_data.json # Начальные данные для локальной базы данных
├── fonts/ # Пользовательские шрифты
│ └── icomoon.ttf # Файл шрифта иконок
├── icons/ # Иконки приложения
├── images/ # Статические изображения
│ ├── diseases/ # Изображения, связанные с заболеваниями
│ └── species/ # Изображения видов комаров
├── labels/ # Файлы меток модели ИИ
├── launcher_icon/ # Иконки запуска приложения
└── models/ # Файлы модели ИИ
Структура тестирования¶
test/
├── fixtures/ # Тестовые данные и mock файлы
├── performance/ # Тестирование производительности
├── unit/ # Модульные тесты
│ ├── models/ # Тесты моделей
│ ├── repositories/ # Тесты репозиториев
│ ├── services/ # Тесты сервисов
│ └── view_models/ # Тесты моделей представления
└── widget/ # Тесты виджетов
└── screens/ # Тесты виджетов экранов
integration_test/
└── app_test.dart # Сквозные интеграционные тесты
Структура документации¶
docs/
├── api-reference/ # Автоматически сгенерированная документация API
├── assets/ # Ресурсы документации
│ ├── css/ # Пользовательские стили
│ ├── diagrams/ # Диаграммы архитектуры
│ ├── images/ # Скриншоты и иллюстрации
│ ├── js/ # Пользовательский JavaScript
│ └── videos/ # Обучающие видео
├── contribution/ # Руководящие принципы для участников
│ └── issue-templates/ # Шаблоны задач GitHub
├── developer-guide/ # Техническая документация
├── developer-guide/ # Техническая и исследовательская документация
├── user-guide/ # Документация для конечных пользователей
├── _config/ # Конфигурация сборки документации
│ ├── mkdocs.yml # Конфигурация MkDocs
│ └── templates/ # Шаблоны документации
├── project_structure.md # Этот документ
└── README.md # Обзор документации
Соглашения именования¶
Именование файлов¶
- Файлы Dart: Используйте
snake_caseдля всех файлов Dart - ✅
classification_service.dart - ❌
ClassificationService.dart -
❌
classification-service.dart -
Файлы ресурсов: Используйте
snake_caseдля согласованности - ✅
mosquito_species.json -
❌
mosquitoSpecies.json -
Имена директорий: Используйте
snake_caseилиkebab-caseсогласованно - ✅
user_guide/ - ✅
api-reference/ - ❌
userGuide/
Именование классов¶
-
Классы: Используйте
PascalCase -
Интерфейсы: Используйте
PascalCaseс описательными именами
Именование переменных и методов¶
-
Переменные: Используйте
camelCase -
Методы: Используйте
camelCaseс описательными глаголами -
Константы: Используйте
lowerCamelCaseдля локальных констант,SCREAMING_SNAKE_CASEдля глобальных констант
Именование виджетов¶
-
Stateless виджеты: Используйте
PascalCaseс описательным суффиксом -
Stateful виджеты: Используйте
PascalCaseдля виджета,_PascalCaseStateдля состояния
Паттерны организации кода¶
Организация импортов¶
Организуйте импорты в следующем порядке с пустыми строками между группами:
// 1. Основные библиотеки Dart
import 'dart:async';
import 'dart:io';
// 2. Фреймворк Flutter
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
// 3. Сторонние пакеты
import 'package:provider/provider.dart';
import 'package:cached_network_image/cached_network_image.dart';
// 4. Внутренние импорты (относительные пути)
import '../models/mosquito_model.dart';
import '../services/classification_service.dart';
import '../widgets/custom_empty_widget.dart';
Структура класса¶
Организуйте члены класса в следующем порядке:
class ExampleClass {
// 1. Статические константы
static const String CONSTANT_VALUE = 'value';
// 2. Переменные экземпляра (сначала приватные, затем публичные)
final String _privateField;
final String publicField;
// 3. Конструктор
ExampleClass({
required this.publicField,
String? privateField,
}) : _privateField = privateField ?? 'default';
// 4. Геттеры и сеттеры
String get privateField => _privateField;
// 5. Публичные методы
void publicMethod() { }
// 6. Приватные методы
void _privateMethod() { }
// 7. Переопределенные методы
@override
String toString() => 'ExampleClass($_privateField)';
}
Организация методов¶
- Держите методы сфокусированными и однозначными
- Используйте описательные имена, объясняющие, что делает метод
- Ограничивайте длину метода ~50 строками когда возможно
- Извлекайте сложную логику в приватные вспомогательные методы
// Хорошо: Описательный и сфокусированный
Future<ClassificationResult> classifyMosquitoImage(File imageFile) async {
final processedImage = await _preprocessImage(imageFile);
final prediction = await _runModelInference(processedImage);
return _parseClassificationResult(prediction);
}
// Вспомогательные методы для сложных операций
Future<Uint8List> _preprocessImage(File imageFile) async { }
Future<List<double>> _runModelInference(Uint8List imageData) async { }
ClassificationResult _parseClassificationResult(List<double> output) { }
Стандарты кодирования¶
Руководящие принципы стиля Dart¶
Проект следует официальному Руководству по стилю Dart с этими специфическими соглашениями:
Длина строки¶
- Максимальная длина строки: 120 символов
- Настроено в
analysis_options.yamlи настройках IDE
Форматирование¶
- Используйте
flutter format .для автоматического форматирования кода - Включите "Format on Save" в вашей IDE
- Используйте завершающие запятые для лучших diff и форматирования
// Хорошо: Завершающие запятые и правильное форматирование
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Classification'),
backgroundColor: Colors.teal,
),
body: Column(
children: [
ClassificationButton(),
ResultsDisplay(),
],
),
);
}
Комментарии документации¶
Используйте dartdoc комментарии для публичных API:
/// Классифицирует изображение комара, используя обученную модель PyTorch.
///
/// Принимает [imageFile] и возвращает [ClassificationResult], содержащий
/// предсказанный вид и оценку достоверности. Выбрасывает [ClassificationException]
/// если изображение не может быть обработано.
///
/// Пример:
/// ```dart
/// final result = await classifyMosquitoImage(imageFile);
/// print('Species: ${result.species}, Confidence: ${result.confidence}');
/// ```
Future<ClassificationResult> classifyMosquitoImage(File imageFile) async {
// Реализация
}
Обработка ошибок¶
Обработка исключений¶
- Используйте специфические типы исключений когда возможно
- Предоставляйте осмысленные сообщения об ошибках
- Логируйте ошибки соответствующим образом для отладки
try {
final result = await classificationService.classify(image);
return result;
} on ClassificationException catch (e) {
logger.error('Classification failed: ${e.message}');
throw ClassificationException('Unable to classify image: ${e.message}');
} catch (e) {
logger.error('Unexpected error during classification: $e');
throw Exception('An unexpected error occurred');
}
Null Safety¶
- Эффективно используйте функции null safety
- Предпочитайте non-nullable типы когда возможно
- Используйте null-aware операторы соответствующим образом
// Хорошо: Четкая обработка null
String? getUserName() {
return user?.name?.trim().isEmpty == true ? null : user?.name?.trim();
}
// Лучше: Использование null-aware операторов
String? getUserName() => user?.name?.trim().nullIfEmpty;
Паттерны управления состоянием¶
Паттерн Provider¶
- Используйте
ChangeNotifierдля простого управления состоянием - Реализуйте правильное освобождение ресурсов
- Используйте
Consumerвиджеты для целевых перестроений
class ClassificationViewModel extends ChangeNotifier {
bool _isClassifying = false;
ClassificationResult? _result;
bool get isClassifying => _isClassifying;
ClassificationResult? get result => _result;
Future<void> classifyImage(File image) async {
_isClassifying = true;
notifyListeners();
try {
_result = await _repository.classifyImage(image);
} finally {
_isClassifying = false;
notifyListeners();
}
}
@override
void dispose() {
// Очистка ресурсов
super.dispose();
}
}
Соглашения тестирования¶
Организация тестовых файлов¶
- Отражайте структуру
lib/вtest/ - Используйте суффикс
_test.dartдля тестовых файлов - Группируйте связанные тесты используя функцию
group()
// test/unit/services/classification_service_test.dart
void main() {
group('ClassificationService', () {
late ClassificationService service;
setUp(() {
service = ClassificationService();
});
group('classifyImage', () {
test('should return valid result for valid image', () async {
// Реализация теста
});
test('should throw exception for invalid image', () async {
// Реализация теста
});
});
});
}
Именование тестов¶
- Используйте описательные имена тестов, объясняющие сценарий
- Следуйте паттерну: "should [ожидаемое поведение] when [условие]"
test('should return Aedes aegypti when image contains yellow fever mosquito', () {
// Реализация теста
});
test('should throw ClassificationException when image is corrupted', () {
// Реализация теста
});
Руководящие принципы производительности¶
Управление памятью¶
- Правильно освобождайте контроллеры и потоки
- Используйте
constконструкторы когда возможно - Избегайте утечек памяти в долгоживущих объектах
Обработка изображений¶
- Сжимайте изображения перед обработкой
- Соответствующим образом кэшируйте обработанные изображения
- Освобождайте ресурсы изображений после использования
Операции с базой данных¶
- Используйте пакетные операции для множественных вставок
- Реализуйте правильное индексирование для запросов
- Правильно закрывайте соединения с базой данных
Соображения безопасности¶
Защита данных¶
- Никогда не фиксируйте чувствительные данные в контроле версий
- Используйте безопасное хранилище для чувствительной информации
- Валидируйте все пользовательские входы
Безопасность API¶
- Используйте HTTPS для всех сетевых коммуникаций
- Реализуйте правильную аутентификацию
- Валидируйте ответы сервера
Руководящие принципы локализации¶
Добавление новых языков¶
- Создайте новый
.arbфайл вlib/l10n/ - Добавьте переводы для всех существующих ключей
- Обновите конфигурацию
l10n.yaml - Запустите
flutter gen-l10nдля генерации классов
Ключи переводов¶
- Используйте описательные, иерархические ключи
- Группируйте связанные переводы
- Предоставляйте контекст для переводчиков
{
"classification_screen_title": "Классификация комаров",
"classification_button_capture": "Сделать фото",
"classification_button_gallery": "Выбрать из галереи",
"classification_result_species": "Вид: {species}",
"classification_result_confidence": "Достоверность: {confidence}%"
}
Рабочий процесс Git¶
Именование веток¶
feature/descriptionдля новых функцийbugfix/descriptionдля исправления ошибокhotfix/descriptionдля срочных исправленийdocs/descriptionдля обновлений документации
Сообщения коммитов¶
Следуйте формату conventional commit:
Примеры:
feat(classification): add confidence threshold setting
fix(database): resolve migration issue for disease table
docs(api): update classification service documentation
Заключение¶
Следование этим соглашениям обеспечивает согласованность, поддерживаемость и эффективность сотрудничества в проекте CulicidaeLab. В случае сомнений обращайтесь к официальным руководствам по стилю Dart и Flutter и поддерживайте согласованность с существующими паттернами кода в проекте.