ТЗ: Разработка агента синхронизации данных (Sync Agent)
ТЗ: Разработка агента синхронизации данных (Sync Agent)
Источник файл Ожерельев В.А. ТЗ: Разработка агента синхронизации.md
Дата: 03.05.2026
1. Назначение
Агент синхронизации обеспечивает загрузку и актуализацию данных из Grace CRM в:
-
PostgreSQL (операционный слой)
-
OpenSearch (поисковый слой)
Система должна поддерживать:
-
near real-time обновление (webhooks)
-
инкрементальную синхронизацию
-
полную переиндексацию
2. Состав синхронизируемых сущностей
Обязательные сущности:
| Сущность | Описание |
|---|---|
| Projects | Сделки |
| Clients | Клиенты |
| Objects | Объекты клиента (ключевая связь клиент -> актив) |
| Activities | Активности |
| Tasks | Задачи |
| Comments | Комментарии |
| Calculations | Расчёты / КП |
| Orders | Заказы |
| Users | Пользователи |
3. API интеграция с CRM
Основные endpoints
GET /api/projects
GET /api/projects/{id}
GET /api/projects?updated_since=timestamp
GET /api/activities
GET /api/activities?project_id=
GET /api/activities?updated_since=timestamp
GET /api/clients
GET /api/clients/{id}
GET /api/tasks
GET /api/tasks?project_id=
GET /api/comments
GET /api/comments?entity_type=&entity_id=
GET /api/calculations
GET /api/calculations?project_id=
GET /api/orders
GET /api/orders?project_id=
GET /api/users
3.1 Объекты (обязательное расширение CRM)
⚠️ В рамках проекта требуется наличие сущности:
Objects (объекты клиента)
Если отсутствует -- требуется добавить API:
GET /api/objects
GET /api/objects/{id}
GET /api/objects?client_id=
GET /api/objects?updated_since=timestamp
Назначение:
-
связь Client -> Object -> Project
-
понимание контекста сделки
Пример:
-
клиент: застройщик
-
объект: ЖК "Северный"
-
проекты: сделки по этому ЖК
4. Архитектура Sync Agent
Компоненты:
-
API Connector
-
Sync Scheduler
-
Webhook Listener
-
Data Transformer
-
Indexer (OpenSearch)
-
Storage Writer (PostgreSQL)
5. Mapping схемы OpenSearch (JSON)
5.1 crm-projects
{
"mappings": {
"properties": {
"project_id": {"type": "keyword"},
"client_id": {"type": "keyword"},
"object_id": {"type": "keyword"},
"name": {"type": "text"},
"status": {"type": "keyword"},
"stage": {"type": "keyword"},
"amount": {"type": "double"},
"manager_id": {"type": "keyword"},
"created_at": {"type": "date"},
"updated_at": {"type": "date"},
"last_activity_at": {"type": "date"},
"activities_count": {"type": "integer"}
}
}
}
5.2 crm-objects (новый индекс)
{
"mappings": {
"properties": {
"object_id": {"type": "keyword"},
"client_id": {"type": "keyword"},
"name": {"type": "text"},
"type": {"type": "keyword"},
"status": {"type": "keyword"},
"location": {"type": "text"},
"created_at": {"type": "date"},
"updated_at": {"type": "date"}
}
}
}
5.3 crm-clients
{
"mappings": {
"properties": {
"client_id": {"type": "keyword"},
"name": {"type": "text"},
"industry": {"type": "keyword"},
"segment": {"type": "keyword"},
"created_at": {"type": "date"},
"updated_at": {"type": "date"}
}
}
}
5.4 crm-activities
{
"mappings": {
"properties": {
"activity_id": {"type": "keyword"},
"project_id": {"type": "keyword"},
"type": {"type": "keyword"},
"description": {"type": "text"},
"result": {"type": "text"},
"created_at": {"type": "date"}
}
}
}
5.5 crm-tasks
{
"mappings": {
"properties": {
"task_id": {"type": "keyword"},
"project_id": {"type": "keyword"},
"assignee_id": {"type": "keyword"},
"status": {"type": "keyword"},
"due_date": {"type": "date"}
}
}
}
5.6 crm-comments
{
"mappings": {
"properties": {
"comment_id": {"type": "keyword"},
"entity_type": {"type": "keyword"},
"entity_id": {"type": "keyword"},
"text": {"type": "text"},
"author_id": {"type": "keyword"},
"created_at": {"type": "date"}
}
}
}
{
"mappings": {
"properties": {
"calculation_id": {"type": "keyword"},
"project_id": {"type": "keyword"},
"amount": {"type": "double"},
"version": {"type": "integer"},
"created_at": {"type": "date"}
}
}
}
5.8 crm-orders
{
"mappings": {
"properties": {
"order_id": {"type": "keyword"},
"project_id": {"type": "keyword"},
"amount": {"type": "double"},
"status": {"type": "keyword"},
"created_at": {"type": "date"}
}
}
}
6. Распределение данных между PostgreSQL и OpenSearch
Данный раздел определяет, какие данные и в каком виде сохраняются в:
-
PostgreSQL (операционный слой)
-
OpenSearch (витрины / search layer)
Ключевой принцип:
-
PostgreSQL -> нормализованные данные и состояние системы
-
OpenSearch -> денормализованные витрины для поиска и аналитики
6.0 Диаграмма последовательности обмена данными
sequenceDiagram
participant CRM
participant Webhook
participant SyncAgent
participant PostgreSQL
participant OpenSearch
CRM->>Webhook: project-updated
Webhook->>SyncAgent: событие
SyncAgent->>CRM: GET /projects/{id}
CRM-->>SyncAgent: project data
SyncAgent->>SyncAgent: transform + normalize
SyncAgent->>PostgreSQL: upsert
SyncAgent->>OpenSearch: bulk index
Note over SyncAgent: update last_sync_timestamp
6.1 Таблица распределения данных
| Сущность | PostgreSQL (что сохраняется) | OpenSearch (что индексируется) |
|---|---|---|
| Projects (сделки) | Полная структура проекта (raw JSON + нормализованные поля), статус синхронизации, технические поля (sync_state, timestamps) | Денормализованная витрина: project_id, client_id, object_id, стадия, статус, сумма, менеджер, last_activity_at, activities_count |
| Clients (клиенты) | Полная карточка клиента, связи, служебные поля | Упрощённая витрина: client_id, название, отрасль, сегмент |
| Objects (объекты) | Полная модель объекта, связь с клиентом | Витрина: object_id, client_id, тип, статус, локация |
| Activities (активности) | Полные записи активностей (raw), связь с проектом | Индекс коммуникаций: тип, текст, результат, дата |
| Tasks (задачи) | Полная структура задач, статусы, SLA | Витрина: task_id, project_id, исполнитель, статус, due_date |
| Comments (комментарии) | Полные тексты, связь с entity | Индекс текстов для поиска: entity_type, entity_id, текст |
| Calculations (расчёты) | Полные расчёты, версии, параметры | Витрина: сумма, версия, привязка к проекту |
| Orders (заказы) | Полная структура заказов | Витрина: статус, сумма, дата |
| Users (пользователи) | Полная модель пользователей и ролей | Витрина: user_id, роль, команда |
| Sync metadata | offset, updated_since, last_sync_time, retry state | ❌ не индексируется |
| Ошибки / логи sync | Полный лог операций | ❌ не индексируется |
6.2 Принципы формирования витрин OpenSearch
1. Денормализация
В OpenSearch данные агрегируются:
Пример:
-
project + client + object -> единый документ
-
activities -> агрегаты (count, last_activity_date)
2. Обогащение
При индексации добавляются:
-
derived fields
-
агрегаты
-
вычисленные признаки
Пример:
-
activities_count -
days_without_activity -
is_overdue
3. Оптимизация под сценарии поиска
OpenSearch индекс строится под:
-
фильтрацию
-
агрегации
-
полнотекстовый поиск
НЕ под:
-
транзакции
-
сложные связи
6.3 Поток записи данных
CRM → SyncAgent → PostgreSQL (raw + normalized)
↓
Transformer
↓
OpenSearch (витрины)
6.4 Ключевые различия слоёв
| Критерий | PostgreSQL | OpenSearch |
|---|---|---|
| Тип данных | нормализованные | денормализованные |
| Назначение | состояние системы | поиск и аналитика |
| Обновление | строгое (upsert) | bulk indexing |
| Источник истины | да (для AI слоя) | нет |
| Использование | Sync, Orchestrator | Recommendation, Analytics |
6.5 Важное архитектурное требование
-
OpenSearch не должен использоваться как источник истины
-
Все изменения идут через:
- CRM -> SyncAgent -> PostgreSQL -> OpenSearch
-
Разделение позволяет:
-
избежать перегрузки PostgreSQL аналитикой
-
обеспечить быстрый поиск и рекомендации
-
гарантировать консистентность системы
7. Модель синхронизации: когда, что и как обновляется
Синхронизация реализуется через 3 параллельных механизма, каждый из которых решает свою задачу:
-
Webhooks -> реакция в реальном времени
-
Polling -> гарантированная консистентность
-
Full Sync -> восстановление и выравнивание
7.1 Webhooks (near real-time синхронизация)
Назначение
Мгновенная реакция на изменения в CRM.
Когда срабатывает
При событиях в CRM:
-
создание проекта
-
обновление проекта
-
создание активности
-
(опционально) изменение задачи
Поток
CRM → webhook → SyncAgent → точечный fetch → update
Какие данные синхронизируются
| Сущность | Что делаем |
|---|---|
| Projects | загружаем 1 проект по ID |
| Activities | загружаем активность или проект целиком |
| Tasks | загружаем задачу |
| Comments | при наличии webhook |
| Calculations | при изменении проекта |
Особенность
Webhook не доверяем полностью -> всегда делаем дочитывание через API
SLA
-
задержка: 1–5 секунд
-
режим: near real-time
7.2 Polling (инкрементальная синхронизация)
Назначение
Гарантия, что:
-
ничего не потерялось
-
данные актуальны
Механика
Используется:
GET /api/*?updated_since=timestamp
Частота по сущностям
| Сущность | Частота | Причина |
|---|---|---|
| Projects | каждые 5 мин | ключевая сущность |
| Activities | каждые 5 мин | динамика общения |
| Tasks | 5–10 мин | SLA |
| Comments | 5–10 мин | контекст |
| Objects | 10–15 мин | реже меняются |
| Clients | 15–30 мин | редко меняются |
| Calculations | 10–15 мин | средняя динамика |
| Orders | 10–15 мин | финансовые события |
| Users | 1 раз/час | почти статично |
Поток
Scheduler → SyncAgent → API (bulk) → batch processing → upsert → index
Особенности реализации
-
используется
updated_at -
хранится
last_sync_timestamp -
обрабатываются батчи (100–1000 записей)
SLA
- задержка актуализации: до 5–10 минут
7.3 Full Sync (полная синхронизация)
Назначение
-
восстановление консистентности
-
устранение ошибок sync
-
перерасчёт витрин
Когда выполняется
-
1 раз в сутки (ночью)
-
вручную (по триггеру)
Что синхронизируется
| Сущность | Поведение |
|---|---|
| Все | полная выгрузка |
| Projects | пересборка агрегатов |
| Activities | полная история |
| Objects | восстановление связей |
| Clients | обновление справочника |
Поток
Full load → overwrite / reindex → rebuild OpenSearch
SLA
-
допускается длительность: до нескольких часов
-
выполняется вне рабочего времени
7.4 Приоритет механизмов
| Механизм | Приоритет | Роль |
|---|---|---|
| Webhooks | высокий | скорость |
| Polling | средний | надёжность |
| Full Sync | низкий | восстановление |
7.5 Конфликты и консистентность
Правило:
последнее изменение по updated_at побеждает
Дополнительно:
-
idempotent операции
-
защита от дублей
-
retry (3+ попытки)
7.6 Сводная схема
┌──────────────┐
│ CRM │
└──────┬───────┘
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
Webhooks Polling Full Sync
(реалтайм) (дельта) (полный)
│ │ │
└──────┬─────┴─────┬──────┘
▼ ▼
Sync Agent (единая логика)
│
┌──────────┴──────────┐
▼ ▼
PostgreSQL OpenSearch
7.7 Ключевой принцип (важно для разработки)
Нельзя полагаться только на один механизм:
-
Webhooks -> могут теряться
-
Polling -> не realtime
-
Full Sync -> тяжёлый
➡️ Только вместе они дают:
-
актуальность
-
надёжность
-
консистентность
Итог
| Тип данных | Как обновляется |
|---|---|
| Критичные (проекты, активности) | webhook + polling |
| Средние (задачи, расчёты) | polling + webhook (если есть) |
| Справочники (клиенты, users) | редкий polling |
| Связи (objects) | polling + full sync |
8. Требования к реализации
Обязательные:
-
idempotency (повторяемость без дубликатов)
-
поддержка bulk операций
-
retry (3 попытки минимум)
-
логирование ошибок
-
контроль
updated_at
Производительность:
-
batch загрузка (100–1000 записей)
-
bulk indexing OpenSearch
9. Ключевые особенности
-
поддержка связи:
- Client -> Object -> Project
-
денормализация данных в OpenSearch
-
готовность к аналитике и AI
10. Результат
После реализации:
-
все CRM-данные доступны в OpenSearch
-
данные связаны через Object layer
-
AI-агенты получают полноценный контекст
No comments to display
No comments to display