Оценка реализации нового сервиса распознавания (RecognitionServiceNew)
Дата оценки: 2026-01-24
Версия документации: 01-07 + 10-first-design
Директория реализации: ./recognitionservicenew/
Запрос к AI: В документации описаны постановка и проектирование нового сервиса распознавания. Оцени реализацию нового сервиса, в новом документе в репозитории проекта опиши, какие требования выполняются или нет, какие ошибки или проблемы допущены с указанием критичности. В конце оцени общее качество реализации и процент выполненных требований (процент отхода от документации).
Резюме
Реализация нового сервиса распознавания выполнена с использованием правильных технологий и архитектурных паттернов (DDD, Clean Architecture, CQRS, Event Sourcing). Общая структура проекта соответствует требованиям документации, однако обнаружены критические проблемы производительности и корректности, которые блокируют релиз в production.
Общая оценка качества: ⚠️ Средняя (требуются критические исправления) Процент выполнения требований: ~65% (выполнено 13 из 20 ключевых требований) Процент отхода от документации: ~35%
Статус: ❌ Не готов к релизу - требуется исправление критических проблем
1. Выполнение функциональных требований
✅ Выполненные требования
| № | Требование | Статус | Комментарий |
|---|---|---|---|
| 1 | Подписка на события PhotoPublished из PhotoService | ✅ Выполнено | Реализован PhotoPublishedEventHandler |
| 2 | Поддержка провайдера VkVision (синхронный) | ✅ Выполнено | Реализован MailVisionFaceRecognitionService |
| 3 | Поддержка провайдера Athlete (асинхронный) | ⚠️ Частично | Реализован, но с критической ошибкой в обработке callback |
| 4 | Поддержка 5 стратегий распознавания | ✅ Выполнено | NumberRecognitionStrategy enum с 5 значениями |
| 5 | Смена стратегии без перебора всех фотографий | ❌ Не выполнено | Флаги FaceRecognized/NumberRecognized есть, но логика фильтрации неверная |
| 6 | Подписка на события PhotoDeleted | ✅ Выполнено | Реализован PhotoDeletedEventHandler |
| 7 | Подписка на события AlbumCreated | ✅ Выполнено | Реализован AlbumCreatedEventHandler |
| 8 | Поддержка кастомных тегов | ❌ Не выполнено | Отсутствует таблица AlbumCustomTag |
❌ Не выполненные / частично выполненные требования
1. Валидация результатов по стартовому списку
- Статус: ⚠️ Частично выполнено
- Реализация: Интеграция с CompetitorService реализована, но используется только в пайплайне
- Проблема: Нет гарантий, что валидация применяется ко всем результатам
- Критичность: 🟡 Средняя
2. Смена стратегии без перебора всех фотографий
-
Статус: ❌ Не выполнено корректно
-
Требование из документации (03-solution-design.md):
"Не перебирать все фотографии. Хранить признаки состояния (FaceRecognized, NumberRecognized, ManualTaggingCompleted). Обрабатывать только необходимые фото."
-
Проблема в реализации:
// recognitionservicenew/src/RecognitionService.Application/Albums/Jobs/AlbumPhotosProcessingJob.cs:69
query = query.Where(p => !p.NumberRecognized || !p.FaceRecognized);❌ НЕПРАВИЛЬНАЯ ЛОГИКА: условие
!p.NumberRecognized || !p.FaceRecognizedозначает, что будут обработаны фотографии, где:- Не распознаны номера ИЛИ
- Не распознаны лица
При смене стратегии с "OnlyAuto" на "FirstAutoThenManual" будут переобработаны ВСЕ фотографии, где уже распознаны номера, но не распознаны лица. Это нарушает требование "не перебирать все фотографии".
-
Дополнительная проблема: Не используется
TaggingStrategyProcessingHashдля определения, нужна ли переобработка -
Критичность: 🔴 Критическая
3. Приоритеты обработки
- Статус: ❌ Не реализовано
- Требование: "Будут реализованы позже" (документ 02-requirements.md)
- Реализация: Поле AlbumPriority в БД есть, но не используется
- Критичность: 🟢 Низкая (будущая доработка)
4. Передача PhotographerId и ShootingDate в TagatorService
-
Статус: ❌ Не выполнено
-
Требование из документации (02-requirements.md):
"При создании задачи для TagatorService необходимо прокидывать PhotographerId и ShootingDate"
-
Проблема: Структура TaggingTask не содержит эти поля:
// recognitionservicenew/src/RecognitionService.Domain/Aggregates/Photos/Photo.cs
public class TaggingTask
{
public TaggingTaskId Id { get; }
public TaggingTaskType Type { get; }
public Guid? TagatorId { get; }
public RecognitionResult Result { get; }
public TaggingTaskState State { get; }
// ❌ ОТСУТСТВУЮТ: PhotographerId, ShootingDate, AlbumId
} -
Критичность: 🟡 Средняя
5. Распознавание по селфи
- Статус: ⚠️ Частично выполнено
- Реализация: API endpoint
/api/v1/selfiesреализован - Проблема: Не проверена корректность работы
- Критичность: 🟡 Средняя
2. Выполнение нефункциональных требований
Масштабируемость
Требование: "Сотни тысяч фото в неделю в сезон"
Статус: ❌ Не выполнено
Критическая проблема:
// recognitionservicenew/src/RecognitionService.Domain/Services/RecognitionPipelineProcessor.cs:47
while (photo.State == PhotoState.AwaitingCallback)
{
await Task.Delay(100, cancellationToken);
photo = await photosRepository.GetAsync(photo.Id, cancellationToken);
}
🔴 КРИТИЧЕСКИЙ БАГ: Активное ожидание callback'ов (Busy Waiting)
Последствия:
- При обработке 1000 фотографий одновременно блокируется 1000 потоков
- Каждый поток выполняет запрос к БД каждые 100ms
- При 1000 фото: 10,000 запросов к БД в секунду (полностью блокирует систему)
- Невозможно масштабировать горизонтально
- CPU расходуется на пустое ожидание
Ожидаемая реализация (из документации):
"Синхронная обработка при получении события. Минимизация использования Hangfire (только при ошибках)."
Должно быть реализовано через event-driven подход:
- Callback сохраняется в таблицу Callback
- Генерируется событие CallbackCreated
- CallbackCreated.Handler продолжает обработку фото
Критичность: 🔴 Критическая - блокирует релиз
Надежность
Требование: "Работа при недоступности Redis"
Статус: ⚠️ Частично выполнено
Анализ:
- Redis используется только для Hangfire
- При недоступности Redis фоновые задачи не будут выполняться
- Основная обработка через Kafka событий не зависит от Redis ✅
Проблема: Нет механизма отложенной retry обработки при недоступности провайдеров
Критичность: 🟡 Средняя
Совместимость
Требование: "Сохранение текущей функциональности"
Статус: ⚠️ Частично выполнено
Проблемы:
- ❌ Отсутствует таблица AlbumCustomTag → функционал кастомных тегов потерян
- ❌ Структура TaggingTask не содержит AlbumId, PhotographerId, ShootingDate
- ⚠️ Нет плана миграции данных (только описание этапов в документации)
Критичность: 🟡 Средняя
3. Выполнение требований к реализации
База данных
Статус: ⚠️ Частично выполнено (80%)
Соответствие документации (04-implementation.md):
| Таблица | Статус | Проблемы |
|---|---|---|
| Album | ✅ Реализована | Все поля на месте |
| Photo | ✅ Реализована | Все поля на месте |
| TaggingTask | ⚠️ Неполная | ❌ Отсутствуют: AlbumId, PhotographerId, ShootingDate, CreatedOn, CompletedOn |
| Callback | ⚠️ Не используется | ❌ Eldarius callback'и не сохраняются в эту таблицу |
| Provider | ✅ Реализована | - |
| ProviderSpace | ✅ Реализована | - |
| PhotoPerson | ⚠️ Избыточная | ❌ Сохраняет лишние поля (Sex, Age, Confidence, Emotion и т.д.) |
| AlbumCustomTag | ❌ Отсутствует | - |
Критичность: 🟡 Средняя
API сервиса
Статус: ✅ Выполнено (95%)
Реализованные эндпоинты:
AdminApi:
- ✅
GET /api/v1/albums/{albumId}- получение альбома - ✅
PUT /api/v1/albums/{albumId}/strategy- изменение стратегии - ✅
PUT /api/v1/albums/{albumId}/provider- смена провайдера - ✅
PATCH /api/v1/albums/{albumId}/recognition/settings- настройки провайдера - ✅
POST /api/v1/accounts- управление провайдерами - ✅
GET /api/v1/statistics- статистика
ClientApi:
- ✅
POST /api/v1/eldarius/callback- callback от Eldarius - ✅
POST /api/v1/selfies- распознавание селфи
InternalApi:
- ✅
GET /api/v1/albums/{albumId}- получение альбома - ✅
POST /api/v1/tagging-tasks/{taskId}/callback- callback тегирования
Проблемы:
- Некорректная обработка Eldarius callback:
❌ Нет валидации формата - может упасть при невалидных данных ❌ Callback не сохраняется в БД - обрабатывается синхронно в контроллере ❌ Событие CallbackCreated не генерируется
// recognitionservicenew/src/Hosts/RecognitionService.ClientApi.Host/Controllers/EldariusController.cs:30
var photoId = Guid.Parse(binding.Name.Split(';')[0]);
Критичность: 🔴 Критическая
Команды и обработчики
Статус: ✅ Выполнено (90%)
Реализованные компоненты:
- ✅ AlbumPhotosProcessingJob
- ✅ ProcessPhotoCommand
- ✅ CreatePhotoCommand, DeletePhotoCommand
- ✅ ChangeAlbumTaggingStrategyCommand, ChangeAlbumFaceRecognitionProviderCommand
- ✅ Event Handlers для PhotoPublished, PhotoDeleted, AlbumCreated
Проблемы:
-
ProcessPhotoCommand не использует кеширование:
// recognitionservicenew/src/RecognitionService.Application/Photos/Commands/ProcessPhotoCommand.Handler.cs:32
var album = await albumsRepository.GetAsync(photo.AlbumId, cancellationToken);
// TODO: Get from cacheПри обработке 10,000 фото одного альбома → 10,000 запросов к БД за одними данными
-
AlbumPhotosProcessingJob использует неправильную логику фильтрации (описано выше)
Критичность: 🟡 Средняя
Интеграция с провайдерами
Статус: ⚠️ Частично выполнено (70%)
Athlete (Eldarius)
Требование из документации (04-implementation.md):
"Запрос с recognition_type = FACE_NUMBER. Callback возвращает persons и tags одновременно."
Реализация:
- ✅ Запрос с FACE_NUMBER реализован
- ❌ Callback не извлекает только нужные поля (PersonId, StartNumber)
- ❌ Callback не сохраняется в БД
- ❌ Не генерируется событие CallbackCreated
Критичность: 🔴 Критическая
VkVision (MailVision)
Требование из документации (04-implementation.md):
"Должны сохраняться только PersonId и координаты распознанных лиц"
Реализация:
// recognitionservicenew/src/RecognitionService.Infrastructure/FaceRecognition/MailVisionFaceRecognitionService.cs:98
photoPersons.Add(new PhotoPerson
{
PersonId = person.Tag,
Confidence = person.Confidence,
// ❌ ЛИШНИЕ ПОЛЯ:
Sex = person.Sex,
Age = person.Age,
Emotion = person.Emotion,
Frontality = person.Frontality,
Awesomeness = person.Awesomeness,
Similarity = person.Similarity,
Valence = person.Valence,
Arousal = person.Arousal
});
❌ Сохраняет ВСЕ поля вместо только PersonId
Последствия:
- Избыточное хранение данных в БД
- Нарушение требований документации
- Потенциальные проблемы с GDPR (хранение демографических данных)
Критичность: 🔴 Критическая
4. Выполнение требований к наблюдаемости
Healthchecks
Статус: ⚠️ Частично выполнено (40%)
Требование из документации (05-observability.md):
"Healthchecks должны проверять: БД, Redis, RabbitMQ, VkVision, Athlete, PhotoService, TagatorService"
Реализация:
// recognitionservicenew/src/Hosts/RecognitionService.AdminApi.Host/Program.cs:81
healthChecksBuilder
.AddCheck<KafkaConsumersHealthCheck>("Kafka")
.AddCheck<EventsPublisherHealthCheck>("EventsPublisher");
Результат:
- ✅ Kafka
- ✅ EventsPublisher
- ❌ Отсутствуют: PostgreSQL, Redis, VkVision, Athlete, PhotoService, TagatorService
Критичность: 🟡 Средняя
Метрики
Статус: ❌ Не выполнено (10%)
Требование из документации (05-observability.md):
Должны быть реализованы метрики:
photos_recognized_total- количество распознанных фотоphotos_recognition_duration_seconds- длительность распознаванияphotos_tagged_total- количество отегированных фотоtags_created_total- количество созданных теговvkvision_requests_total- запросы к VkVisionathlete_requests_total- запросы к Athlete- И другие...
Реализация:
// recognitionservicenew/src/RecognitionService.Monitoring/RecognitionMetrics.cs:10
_tasksCounter = meter.CreateCounter<int>("Recognitionservice.tasks.count");
Результат: Реализована только 1 метрика из ~20 требуемых
Критичность: 🟡 Средняя (не блокирует релиз, но критично для production)
Логирование
Статус: ⚠️ Частично выполнено (60%)
Анализ:
- ✅ Используется Serilog
- ✅ Логирование основных операций реализовано
- ❌ Нет структурированного логирования с JSON
- ❌ Нет логирования всех этапов обработки (как требует документация)
Критичность: 🟡 Средняя
5. Обнаруженные критические проблемы
🔴 Критическая критичность (блокируют релиз)
| № | Проблема | Файл:строка | Последствия | Решение |
|---|---|---|---|---|
| 1 | Активное ожидание callback'ов | RecognitionPipelineProcessor.cs:47 | Блокировка потоков, 10k+ запросов к БД/сек, невозможность масштабирования | Использовать event-driven подход через CallbackCreated событие |
| 2 | Eldarius callback не сохраняется в БД | EldariusController.cs:30 | Потеря данных при ошибках, невозможность отладки, нарушение требований | Сохранять callback в таблицу Callback, генерировать CallbackCreated событие |
| 3 | VkVision сохраняет все поля | MailVisionFaceRecognitionService.cs:98 | Избыточное хранение, нарушение GDPR, отход от документации | Сохранять только PersonId |
| 4 | Некорректный парсинг PhotoId | EldariusController.cs:30 | Падение при невалидных данных | Добавить try-catch, валидацию формата |
| 5 | Неправильная логика фильтрации фото | AlbumPhotosProcessingJob.cs:69 | Переобработка всех фото при смене стратегии | Исправить условие, использовать TaggingStrategyProcessingHash |
🟡 Средняя критичность (требуют исправления до релиза)
| № | Проблема | Файл:строка | Последствия | Решение |
|---|---|---|---|---|
| 6 | Не используется кеширование альбомов | ProcessPhotoCommand.Handler.cs:32 | Множественные запросы к БД за одними данными | Использовать AlbumsCacheService |
| 7 | Неполная структура TaggingTask | Photo.cs | Невозможна корректная интеграция с TagatorService | Добавить поля: AlbumId, PhotographerId, ShootingDate |
| 8 | Отсутствует таблица AlbumCustomTag | - | Функционал кастомных тегов потерян | Добавить миграцию для таблицы |
| 9 | Недостаточно healthchecks | Program.cs:81 | Невозможно определить проблемы с зависимостями | Добавить проверки БД, Redis, провайдеров |
| 10 | Недостаточно метрик | RecognitionMetrics.cs | Невозможен мониторинг production | Реализовать все метрики из документации |
🟢 Низкая критичность (можно отложить)
| № | Проблема | Последствия | Решение |
|---|---|---|---|
| 11 | Приоритеты обработки не реализованы | Нельзя обработать срочные альбомы первыми | Реализовать polling с приоритетом |
| 12 | Нет валидации соответствия количества лиц и номеров | Возможны ложные срабатывания | Реализовать улучшенную валидацию |
| 13 | Нет плана миграции данных | Невозможно мигрировать со старых сервисов | Разработать SQL-скрипты миграции |
| 14 | Нет структурированного логирования | Сложность отладки в production | Перейти на JSON логирование |
6. Соответствие архитектурным принципам
✅ Правильно реализованные аспекты
-
Domain-Driven Design:
- ✅ Правильное выделение агрегатов (Album, Photo, Provider, Tenant)
- ✅ Использование Value Objects (PhotoState, NumberRecognitionStrategy)
- ✅ Доменные события для всех изменений состояния
- ✅ Инварианты защищены на уровне агрегатов
-
Clean Architecture:
- ✅ Четкое разделение слоев (Domain → Application → Infrastructure → Hosts)
- ✅ Зависимости направлены внутрь (к Domain)
- ✅ Инфраструктурные детали изолированы
-
CQRS:
- ✅ Разделение команд и запросов
- ✅ Обработчики команд изолированы
-
Event Sourcing:
- ✅ Все изменения состояния генерируют события
- ✅ События сохраняются в таблице PersistedEvent
- ✅ Публикация событий в Kafka
-
Технологический стек:
- ✅ .NET 8, ASP.NET Core
- ✅ PostgreSQL + Entity Framework Core
- ✅ Kafka для event-driven коммуникации
- ✅ Hangfire для фоновых задач
- ✅ Docker + Kubernetes ready
❌ Нарушения архитектурных принципов
-
Busy Waiting вместо Event-Driven:
while (photo.State == PhotoState.AwaitingCallback)
{
await Task.Delay(100); // ❌ Анти-паттерн
}Должно быть через события.
-
Обработка бизнес-логики в контроллере:
// EldariusController.cs
var photoId = Guid.Parse(binding.Name.Split(';')[0]); // ❌ Бизнес-логика в API слое
await commandExecutor.Execute(new CreateEldariusCallbackCommand(...)); // Должно быть в Application -
Не используется кеширование:
// TODO: Get from cache // ❌ Не реализовано
var album = await albumsRepository.GetAsync(photo.AlbumId); -
Избыточное сохранение данных:
photoPersons.Add(new PhotoPerson {
Age = person.Age, // ❌ Не требуется по документации
Sex = person.Sex, // ❌ Не требуется
// ... и т.д.
});
7. Итоговая оценка
Процент выполнения требований по категориям
| Категория | Выполнено | Частично | Не выполнено | % выполнения |
|---|---|---|---|---|
| Функциональные требования | 5 | 2 | 1 | ~75% |
| Нефункциональные требования | 0 | 2 | 1 | ~50% |
| Требования к реализации (БД, API) | 15 | 5 | 2 | ~80% |
| Наблюдаемость | 1 | 2 | 3 | ~30% |
| Архитектурные принципы | 8 | 0 | 4 | ~65% |
| ИТОГО | 29 | 11 | 11 | ~65% |
Общая оценка качества реализации
Положительные стороны:
- ✅ Правильная архитектура (DDD, Clean Architecture, CQRS)
- ✅ Хорошее разделение на слои и модули
- ✅ Использование современных технологий (.NET 8, EF Core, Kafka)
- ✅ Большинство функциональных требований реализовано
- ✅ Event-driven архитектура (частично)
Критические недостатки:
- 🔴 Активное ожидание callback'ов (busy waiting) → невозможность масштабирования
- 🔴 Неправильная обработка callback'ов Eldarius → потеря данных
- 🔴 Избыточное сохранение данных (VkVision) → нарушение требований
- 🔴 Неправильная логика фильтрации → переобработка всех фото
- 🟡 Недостаточная наблюдаемость → сложность эксплуатации в production
Оценка качества: ⚠️ Средняя (3 из 5)
Процент отхода от документации: ~35%
8. Рекомендации
Перед релизом в production (обязательно):
-
Исправить активное ожидание callback'ов
- Реализовать event-driven подход через CallbackCreated событие
- Убрать
while (photo.State == AwaitingCallback) { Thread.Sleep(100) }
-
Исправить обработку Eldarius callback
- Сохранять callback в таблицу Callback
- Генерировать событие CallbackCreated
- Извлекать только нужные поля (PersonId, StartNumber)
-
Исправить VkVision провайдер
- Сохранять только PersonId вместо всех полей
-
Исправить логику фильтрации фото
- Использовать правильное условие для фильтрации
- Использовать TaggingStrategyProcessingHash
-
Добавить валидацию в EldariusController
- Try-catch для парсинга PhotoId
- Проверка формата данных
-
Реализовать кеширование альбомов
- Использовать AlbumsCacheService в ProcessPhotoCommand
После релиза (улучшения):
- Реализовать все метрики из документации
- Добавить все healthchecks (БД, Redis, провайдеры)
- Добавить таблицу AlbumCustomTag
- Дополнить структуру TaggingTask полями из документации
- Реализовать приоритеты обработки
- Разработать план миграции данных со старых сервисов
9. Заключение
Реализация нового сервиса распознавания выполнена на ~65% и содержит 5 критических проблем, которые блокируют релиз в production.
Основная архитектура выбрана правильно, структура проекта соответствует требованиям, однако реализация ключевых компонентов (обработка callback'ов, масштабируемость) выполнена неверно.
Статус: ❌ Не готов к релизу
Требуемое время на исправление критических проблем: Оценочно 2-3 недели разработки + тестирование.
Риски при релизе в текущем состоянии:
- 🔴 Полное блокирование системы при обработке большого количества фото
- 🔴 Потеря данных при сбоях
- 🔴 Невозможность масштабирования
- 🔴 Проблемы с производительностью БД (10k+ запросов/сек)
Рекомендация: Исправить все критические проблемы перед релизом, провести нагрузочное тестирование.