From d32e367383e713d574f71ac3cc3f2e701440ab4c Mon Sep 17 00:00:00 2001 From: Nikolay Vigdorov Date: Sat, 13 Dec 2025 11:19:11 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=B0=D1=80=D1=85=D0=B8=D1=82=D0=B5=D0=BA=D1=82=D1=83?= =?UTF-8?q?=D1=80=D0=BD=D1=8B=D0=B9=20=D1=81=D0=BA=D0=B5=D1=82=D1=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- C4-Architecture.md | 635 +++++++++++++++++ Анализ требований.md | 8 +- Архитектура системы TaskManager.md | 1053 ++++++++++++++++++++++++++++ Архитектурный скетч.drawio | 240 +++++++ Вопросы по архитектуре.md | 117 ++++ Скетч.svg | 1 + 6 files changed, 2050 insertions(+), 4 deletions(-) create mode 100644 C4-Architecture.md create mode 100644 Архитектура системы TaskManager.md create mode 100644 Архитектурный скетч.drawio create mode 100644 Вопросы по архитектуре.md create mode 100644 Скетч.svg diff --git a/C4-Architecture.md b/C4-Architecture.md new file mode 100644 index 0000000..a99db3f --- /dev/null +++ b/C4-Architecture.md @@ -0,0 +1,635 @@ +[Назад](/README.md) + +# C4 Architecture Diagrams - TaskManager + +## Содержание +1. [Level 1: System Context](#level-1-system-context) +2. [Level 2: Container Diagram](#level-2-container-diagram) +3. [Level 3: Component Diagram - Tasks Service](#level-3-component-diagram---tasks-service) +4. [Level 3: Component Diagram - Adapter Layer](#level-3-component-diagram---adapter-layer) + +--- + +## Level 1: System Context + +Показывает TaskManager в контексте пользователей и внешних систем. + +```mermaid +C4Context + title System Context diagram for TaskManager + + Person(hotel_staff, "Hotel Staff", "Сотрудники отеля (горничные, техники, менеджеры)") + Person(admin, "System Admin", "Администратор системы") + Person(guest, "Hotel Guest", "Гость отеля") + + System(taskmanager, "TaskManager", "Платформа для управления задачами между подразделениями отеля и интеграции с внешними системами") + + System_Ext(pms, "PMS System", "Система управления отелем (Opera, Fidelio)") + System_Ext(erp, "ERP System", "Система управления ресурсами") + System_Ext(housekeeping, "Housekeeping App", "Приложение для управления уборкой") + System_Ext(email, "Email System", "SMTP сервер для отправки email") + System_Ext(sms, "SMS Gateway", "Сервис отправки SMS (Twilio)") + System_Ext(telegram, "Telegram", "Telegram Bot API") + System_Ext(s3, "AWS S3", "Хранилище файлов") + + Rel(hotel_staff, taskmanager, "Создает и управляет задачами", "Web/Mobile") + Rel(admin, taskmanager, "Настраивает систему, связи, роли", "Web Admin") + Rel(guest, taskmanager, "Создает запросы на обслуживание", "Telegram Bot") + + Rel(taskmanager, pms, "Получает/отправляет данные о бронированиях, номерах", "REST API/Webhook") + Rel(taskmanager, erp, "Интегрируется с учетом ресурсов", "REST API") + Rel(taskmanager, housekeeping, "Отправляет задачи на уборку", "REST API/Push") + Rel(taskmanager, email, "Отправляет уведомления", "SMTP") + Rel(taskmanager, sms, "Отправляет SMS уведомления", "REST API") + Rel(taskmanager, telegram, "Получает запросы гостей, отправляет уведомления", "Webhook/Bot API") + Rel(taskmanager, s3, "Хранит файлы и фото", "S3 API") + + UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="2") +``` + +--- + +## Level 2: Container Diagram + +Показывает основные контейнеры (приложения и хранилища данных) внутри TaskManager. + +```mermaid +C4Container + title Container diagram for TaskManager + + Person(hotel_staff, "Hotel Staff", "Сотрудники отеля") + Person(admin, "System Admin", "Администратор системы") + Person(guest, "Hotel Guest", "Гость отеля") + + System_Ext(pms, "PMS System", "Opera/Fidelio") + System_Ext(erp, "ERP System", "ERP") + System_Ext(housekeeping_ext, "Housekeeping App", "External App") + System_Ext(email_ext, "Email System", "SMTP") + System_Ext(sms_ext, "SMS Gateway", "Twilio") + System_Ext(telegram_ext, "Telegram", "Bot API") + System_Ext(s3_ext, "AWS S3", "File Storage") + + Container_Boundary(taskmanager, "TaskManager System") { + + Container(web_admin, "Web Admin", "React, TypeScript", "Веб-приложение для настройки системы и управления") + Container(mobile_app, "Mobile App", "React Native/Flutter", "Мобильное приложение для сотрудников") + + Container(api_gateway, "API Gateway", "Kong/Envoy", "Единая точка входа, маршрутизация, аутентификация") + + Container(tasks_service, "Tasks Service", "Python, FastAPI, gRPC", "Управление задачами") + Container(connections_service, "Connections Service", "Python, FastAPI, gRPC", "Управление связями между адаптерами") + Container(users_service, "Users Service", "Python, FastAPI, gRPC", "Управление пользователями") + Container(permissions_service, "Permissions Service", "Python, FastAPI, gRPC", "Управление ролями и правами (RBAC)") + Container(notification_service, "Notification Service", "Python, FastAPI", "Отправка уведомлений через разные каналы") + Container(file_storage_service, "File Storage Service", "Python, FastAPI", "Управление файлами") + Container(audit_service, "Audit Service", "Python, FastAPI", "Логирование всех действий") + Container(scheduler_service, "Scheduler Service", "Python, FastAPI", "Управление периодическими задачами") + Container(events_service, "Events Service", "Python, FastAPI, gRPC", "Управление мероприятиями") + + Container(pms_adapter, "PMS Adapter", "Python", "Адаптер для PMS системы") + Container(erp_adapter, "ERP Adapter", "Python", "Адаптер для ERP системы") + Container(housekeeping_adapter, "Housekeeping Adapter", "Python", "Адаптер для приложения уборки") + Container(telegram_adapter, "Telegram Bot Adapter", "Python", "Адаптер для Telegram бота") + + ContainerQueue(kafka, "Message Broker", "Apache Kafka", "Асинхронная коммуникация между сервисами") + + ContainerDb(postgres, "Database", "PostgreSQL", "Хранение данных системы") + ContainerDb(redis, "Cache", "Redis", "Кэширование данных, сессии") + } + + Rel(hotel_staff, web_admin, "Использует", "HTTPS") + Rel(hotel_staff, mobile_app, "Использует", "HTTPS") + Rel(admin, web_admin, "Настраивает систему", "HTTPS") + Rel(guest, telegram_adapter, "Отправляет запросы", "Telegram") + + Rel(web_admin, api_gateway, "Делает API запросы", "REST/gRPC-Web") + Rel(mobile_app, api_gateway, "Делает API запросы", "REST/gRPC") + + Rel(api_gateway, tasks_service, "Маршрутизирует запросы", "gRPC") + Rel(api_gateway, users_service, "Маршрутизирует запросы", "gRPC") + Rel(api_gateway, connections_service, "Маршрутизирует запросы", "gRPC") + Rel(api_gateway, events_service, "Маршрутизирует запросы", "gRPC") + Rel(api_gateway, file_storage_service, "Маршрутизирует запросы", "REST") + + Rel(tasks_service, kafka, "Публикует события", "Kafka Protocol") + Rel(connections_service, kafka, "Публикует/читает события", "Kafka Protocol") + Rel(scheduler_service, kafka, "Публикует триггеры", "Kafka Protocol") + Rel(events_service, kafka, "Публикует события", "Kafka Protocol") + Rel(notification_service, kafka, "Читает события", "Kafka Protocol") + Rel(audit_service, kafka, "Читает все события", "Kafka Protocol") + + Rel(tasks_service, permissions_service, "Проверяет права", "gRPC") + Rel(users_service, permissions_service, "Проверяет права", "gRPC") + + Rel(pms_adapter, kafka, "Публикует сообщения", "Kafka Protocol") + Rel(erp_adapter, kafka, "Публикует сообщения", "Kafka Protocol") + Rel(housekeeping_adapter, kafka, "Публикует сообщения", "Kafka Protocol") + Rel(telegram_adapter, kafka, "Публикует сообщения", "Kafka Protocol") + + Rel(pms_adapter, pms, "Получает/отправляет данные", "REST API/Webhook") + Rel(erp_adapter, erp, "Получает/отправляет данные", "REST API") + Rel(housekeeping_adapter, housekeeping_ext, "Отправляет задачи", "REST API/Push") + Rel(telegram_adapter, telegram_ext, "Взаимодействует с ботом", "Webhook/Bot API") + + Rel(notification_service, email_ext, "Отправляет email", "SMTP") + Rel(notification_service, sms_ext, "Отправляет SMS", "REST API") + Rel(notification_service, telegram_ext, "Отправляет сообщения", "Bot API") + + Rel(file_storage_service, s3_ext, "Хранит файлы", "S3 API") + + Rel(tasks_service, postgres, "Читает/пишет данные", "SQL") + Rel(users_service, postgres, "Читает/пишет данные", "SQL") + Rel(connections_service, postgres, "Читает/пишет данные", "SQL") + Rel(permissions_service, postgres, "Читает/пишет данные", "SQL") + Rel(scheduler_service, postgres, "Читает/пишет данные", "SQL") + Rel(events_service, postgres, "Читает/пишет данные", "SQL") + Rel(audit_service, postgres, "Пишет логи", "SQL") + Rel(file_storage_service, postgres, "Пишет метаданные", "SQL") + + Rel(tasks_service, redis, "Кэширует данные", "Redis Protocol") + Rel(users_service, redis, "Кэширует данные", "Redis Protocol") + Rel(permissions_service, redis, "Кэширует права", "Redis Protocol") + + UpdateLayoutConfig($c4ShapeInRow="4", $c4BoundaryInRow="2") +``` + +--- + +## Level 3: Component Diagram - Tasks Service + +Показывает внутреннюю структуру Tasks Service. + +```mermaid +C4Component + title Component diagram for Tasks Service + + Container_Ext(api_gateway, "API Gateway", "Kong/Envoy", "Точка входа") + Container_Ext(kafka, "Kafka", "Apache Kafka", "Message Broker") + ContainerDb_Ext(postgres, "PostgreSQL", "Database", "Основная БД") + ContainerDb_Ext(redis, "Redis", "Cache", "Кэш") + Container_Ext(permissions_service, "Permissions Service", "Python, gRPC", "Проверка прав") + Container_Ext(file_storage_service, "File Storage Service", "Python", "Управление файлами") + + Container_Boundary(tasks_service, "Tasks Service") { + + Component(grpc_api, "gRPC API", "Python, gRPC", "Обработка gRPC запросов") + Component(rest_api, "REST API", "FastAPI", "Обработка REST запросов (fallback)") + + Component(task_controller, "Task Controller", "Python", "Обработка бизнес-логики задач") + Component(status_controller, "Status Controller", "Python", "Управление статусами задач") + Component(dependency_controller, "Dependency Controller", "Python", "Управление зависимостями") + Component(history_controller, "History Controller", "Python", "Управление историей изменений") + + Component(task_repository, "Task Repository", "SQLAlchemy", "Работа с БД задач") + Component(status_repository, "Status Repository", "SQLAlchemy", "Работа с БД статусов") + Component(dependency_repository, "Dependency Repository", "SQLAlchemy", "Работа с БД зависимостей") + Component(history_repository, "History Repository", "SQLAlchemy", "Работа с БД истории") + + Component(kafka_producer, "Kafka Producer", "aiokafka", "Публикация событий в Kafka") + Component(kafka_consumer, "Kafka Consumer", "aiokafka", "Чтение событий из Kafka") + + Component(cache_manager, "Cache Manager", "Redis Client", "Управление кэшем") + Component(permission_client, "Permission Client", "gRPC Client", "Проверка прав доступа") + + Component(task_validator, "Task Validator", "Pydantic", "Валидация данных задач") + Component(event_mapper, "Event Mapper", "Python", "Маппинг событий") + } + + Rel(api_gateway, grpc_api, "Отправляет gRPC запросы", "gRPC") + Rel(api_gateway, rest_api, "Отправляет REST запросы", "HTTP/JSON") + + Rel(grpc_api, task_controller, "Вызывает методы") + Rel(rest_api, task_controller, "Вызывает методы") + + Rel(task_controller, task_validator, "Валидирует данные") + Rel(task_controller, permission_client, "Проверяет права") + Rel(task_controller, task_repository, "Работает с задачами") + Rel(task_controller, status_controller, "Изменяет статусы") + Rel(task_controller, dependency_controller, "Управляет зависимостями") + Rel(task_controller, history_controller, "Записывает историю") + Rel(task_controller, cache_manager, "Кэширует данные") + Rel(task_controller, kafka_producer, "Публикует события") + + Rel(status_controller, status_repository, "Работает со статусами") + Rel(dependency_controller, dependency_repository, "Работает с зависимостями") + Rel(history_controller, history_repository, "Работает с историей") + + Rel(kafka_consumer, event_mapper, "Маппит события") + Rel(event_mapper, task_controller, "Обрабатывает события") + + Rel(task_repository, postgres, "Выполняет SQL запросы", "SQL") + Rel(status_repository, postgres, "Выполняет SQL запросы", "SQL") + Rel(dependency_repository, postgres, "Выполняет SQL запросы", "SQL") + Rel(history_repository, postgres, "Выполняет SQL запросы", "SQL") + + Rel(cache_manager, redis, "Кэширует/читает данные", "Redis Protocol") + Rel(permission_client, permissions_service, "Проверяет права", "gRPC") + + Rel(kafka_producer, kafka, "Публикует события", "Kafka Protocol") + Rel(kafka_consumer, kafka, "Читает события", "Kafka Protocol") + + UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") +``` + +--- + +## Level 3: Component Diagram - Adapter Layer + +Показывает внутреннюю структуру адаптера (на примере PMS Adapter). + +```mermaid +C4Component + title Component diagram for PMS Adapter (Example) + + System_Ext(pms_system, "PMS System", "Opera/Fidelio", "Внешняя PMS система") + Container_Ext(kafka, "Kafka", "Apache Kafka", "Message Broker") + Container_Ext(postgres, "PostgreSQL", "Database", "Хранение состояния адаптера") + + Container_Boundary(pms_adapter, "PMS Adapter") { + + Component(webhook_handler, "Webhook Handler", "FastAPI", "Принимает webhook от PMS") + Component(api_poller, "API Poller", "Python, APScheduler", "Опрашивает PMS API по расписанию") + Component(rest_client, "REST Client", "httpx", "HTTP клиент для PMS API") + + Component(inbound_transformer, "Inbound Transformer", "Python", "PMS формат → Стандартный формат") + Component(outbound_transformer, "Outbound Transformer", "Python", "Стандартный формат → PMS формат") + + Component(message_validator, "Message Validator", "Pydantic", "Валидация сообщений") + Component(event_publisher, "Event Publisher", "aiokafka", "Публикация событий в Kafka") + Component(event_consumer, "Event Consumer", "aiokafka", "Чтение событий из Kafka") + + Component(state_manager, "State Manager", "SQLAlchemy", "Управление состоянием синхронизации") + Component(error_handler, "Error Handler", "Python", "Обработка ошибок и retry") + Component(config_manager, "Config Manager", "Python", "Управление конфигурацией") + } + + Rel(pms_system, webhook_handler, "Отправляет webhook", "HTTP/JSON") + Rel(api_poller, rest_client, "Запрашивает данные") + Rel(rest_client, pms_system, "Делает API запросы", "REST/SOAP") + + Rel(webhook_handler, inbound_transformer, "Передает данные") + Rel(api_poller, inbound_transformer, "Передает данные") + + Rel(inbound_transformer, message_validator, "Валидирует сообщение") + Rel(message_validator, event_publisher, "Публикует валидное событие") + + Rel(event_consumer, kafka, "Читает исходящие события", "Kafka Protocol") + Rel(event_consumer, outbound_transformer, "Передает событие") + Rel(outbound_transformer, rest_client, "Отправляет в PMS") + + Rel(event_publisher, kafka, "Публикует входящие события", "Kafka Protocol") + + Rel(state_manager, postgres, "Сохраняет состояние", "SQL") + Rel(error_handler, state_manager, "Логирует ошибки") + + Rel(webhook_handler, error_handler, "Обрабатывает ошибки") + Rel(api_poller, error_handler, "Обрабатывает ошибки") + Rel(rest_client, error_handler, "Обрабатывает ошибки") + + Rel(config_manager, api_poller, "Предоставляет конфигурацию") + Rel(config_manager, rest_client, "Предоставляет конфигурацию") + + UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") +``` + +--- + +## Deployment Diagram (Kubernetes) + +Показывает развертывание в Kubernetes кластере. + +```mermaid +C4Deployment + title Deployment Diagram for TaskManager (Kubernetes) + + Deployment_Node(k8s_cluster, "Kubernetes Cluster", "GKE/EKS/AKS") { + + Deployment_Node(ingress_ns, "Namespace: ingress") { + Container(ingress, "Ingress Controller", "NGINX/Traefik", "Входная точка для внешнего трафика") + } + + Deployment_Node(gateway_ns, "Namespace: gateway") { + Container(api_gateway, "API Gateway", "Kong/Envoy", "3 replicas", "Маршрутизация и аутентификация") + } + + Deployment_Node(services_ns, "Namespace: services") { + Container(tasks_svc, "Tasks Service", "Python, FastAPI", "5 replicas", "Управление задачами") + Container(users_svc, "Users Service", "Python, FastAPI", "3 replicas", "Управление пользователями") + Container(connections_svc, "Connections Service", "Python, FastAPI", "2 replicas", "Управление связями") + Container(permissions_svc, "Permissions Service", "Python, FastAPI", "3 replicas", "RBAC") + Container(notification_svc, "Notification Service", "Python, FastAPI", "3 replicas", "Уведомления") + Container(file_storage_svc, "File Storage Service", "Python, FastAPI", "2 replicas", "Файлы") + Container(audit_svc, "Audit Service", "Python, FastAPI", "2 replicas", "Аудит") + Container(scheduler_svc, "Scheduler Service", "Python, FastAPI", "1 replica", "Планировщик") + Container(events_svc, "Events Service", "Python, FastAPI", "2 replicas", "Мероприятия") + } + + Deployment_Node(adapters_ns, "Namespace: adapters") { + Container(pms_adapter, "PMS Adapter", "Python", "2 replicas", "PMS интеграция") + Container(erp_adapter, "ERP Adapter", "Python", "1 replica", "ERP интеграция") + Container(housekeeping_adapter, "Housekeeping Adapter", "Python", "2 replicas", "Уборка") + Container(telegram_adapter, "Telegram Adapter", "Python", "2 replicas", "Telegram бот") + } + + Deployment_Node(data_ns, "Namespace: data") { + ContainerDb(postgres_primary, "PostgreSQL Primary", "PostgreSQL 15", "1 pod", "Основная БД (master)") + ContainerDb(postgres_replica, "PostgreSQL Replica", "PostgreSQL 15", "2 pods", "Реплики для чтения") + ContainerDb(redis_cluster, "Redis Cluster", "Redis 7", "3 pods", "Кэш и сессии") + ContainerQueue(kafka_cluster, "Kafka Cluster", "Apache Kafka", "3 brokers", "Message broker") + } + + Deployment_Node(monitoring_ns, "Namespace: monitoring") { + Container(prometheus, "Prometheus", "Prometheus", "Сбор метрик") + Container(grafana, "Grafana", "Grafana", "Визуализация метрик") + Container(jaeger, "Jaeger", "Jaeger", "Distributed tracing") + } + } + + Deployment_Node(external_services, "External Services") { + Container(s3, "AWS S3", "Object Storage", "Хранилище файлов") + Container(smtp, "SMTP Server", "Email", "Отправка email") + Container(sms_gateway, "SMS Gateway", "Twilio", "Отправка SMS") + } + + Rel(ingress, api_gateway, "Routes traffic", "HTTPS") + + Rel(api_gateway, tasks_svc, "Calls", "gRPC") + Rel(api_gateway, users_svc, "Calls", "gRPC") + Rel(api_gateway, connections_svc, "Calls", "gRPC") + Rel(api_gateway, events_svc, "Calls", "gRPC") + Rel(api_gateway, file_storage_svc, "Calls", "REST") + + Rel(tasks_svc, kafka_cluster, "Publishes/Consumes", "Kafka Protocol") + Rel(connections_svc, kafka_cluster, "Publishes/Consumes", "Kafka Protocol") + Rel(scheduler_svc, kafka_cluster, "Publishes", "Kafka Protocol") + Rel(notification_svc, kafka_cluster, "Consumes", "Kafka Protocol") + Rel(audit_svc, kafka_cluster, "Consumes", "Kafka Protocol") + + Rel(pms_adapter, kafka_cluster, "Publishes/Consumes", "Kafka Protocol") + Rel(erp_adapter, kafka_cluster, "Publishes/Consumes", "Kafka Protocol") + Rel(housekeeping_adapter, kafka_cluster, "Publishes/Consumes", "Kafka Protocol") + Rel(telegram_adapter, kafka_cluster, "Publishes/Consumes", "Kafka Protocol") + + Rel(tasks_svc, postgres_primary, "Writes", "SQL") + Rel(tasks_svc, postgres_replica, "Reads", "SQL") + Rel(users_svc, postgres_primary, "Writes", "SQL") + Rel(users_svc, postgres_replica, "Reads", "SQL") + Rel(audit_svc, postgres_primary, "Writes logs", "SQL") + + Rel(tasks_svc, redis_cluster, "Cache", "Redis Protocol") + Rel(users_svc, redis_cluster, "Cache", "Redis Protocol") + Rel(permissions_svc, redis_cluster, "Cache permissions", "Redis Protocol") + + Rel(file_storage_svc, s3, "Stores files", "S3 API") + Rel(notification_svc, smtp, "Sends emails", "SMTP") + Rel(notification_svc, sms_gateway, "Sends SMS", "REST API") + + Rel(prometheus, tasks_svc, "Scrapes metrics", "HTTP") + Rel(prometheus, users_svc, "Scrapes metrics", "HTTP") + Rel(grafana, prometheus, "Queries metrics", "HTTP") + + UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1") +``` + +--- + +## Sequence Diagram: Creating Task from External System + +Показывает последовательность действий при создании задачи из внешней системы. + +```mermaid +sequenceDiagram + participant PMS as PMS System (Opera) + participant Adapter as PMS Adapter + participant Kafka as Apache Kafka + participant Connections as Connections Service + participant Tasks as Tasks Service + participant Permissions as Permissions Service + participant Audit as Audit Service + participant Notification as Notification Service + participant DB as PostgreSQL + participant Housekeeping as Housekeeping Adapter + participant HK_App as Housekeeping App + + Note over PMS,HK_App: Сценарий: Гость выехал из номера, нужна уборка + + PMS->>Adapter: Webhook: Guest Checkout (Room 305) + Adapter->>Adapter: Validate webhook signature + Adapter->>Adapter: Transform to standard format + Adapter->>Kafka: Publish: adapters.pms.checkout + + Note over Kafka: Topic: adapters.pms.checkout + + Kafka->>Connections: Consume: adapters.pms.checkout + Connections->>DB: Get routing rules for PMS → TaskManager + DB-->>Connections: Routing rules + Connections->>Connections: Apply rules: checkout → create cleaning task + Connections->>Kafka: Publish: connections.route_task + + Note over Kafka: Topic: connections.route_task + + Kafka->>Tasks: Consume: connections.route_task + Tasks->>Permissions: CheckPermission(source_adapter, create_task) + Permissions->>DB: Query permissions + DB-->>Permissions: Permission granted + Permissions-->>Tasks: Allowed + + Tasks->>Tasks: Create task object + Tasks->>DB: INSERT INTO tasks (...) + DB-->>Tasks: Task created (id: uuid) + + Tasks->>Kafka: Publish: tasks.created + + Note over Kafka: Topic: tasks.created + + par Parallel Processing + Kafka->>Audit: Consume: tasks.created + Audit->>DB: INSERT INTO audit_log + + Kafka->>Notification: Consume: tasks.created + Notification->>DB: Get user notification preferences + DB-->>Notification: User prefers Telegram + Notification->>Notification: Send Telegram notification + + Kafka->>Connections: Consume: tasks.created + Connections->>DB: Get routing rules for TaskManager → Housekeeping + DB-->>Connections: Route to Housekeeping Adapter + Connections->>Kafka: Publish: connections.route_task + end + + Note over Kafka: Topic: connections.route_task + + Kafka->>Housekeeping: Consume: connections.route_task + Housekeeping->>Housekeeping: Transform to Housekeeping App format + Housekeeping->>HK_App: POST /api/tasks (Room 305 cleaning) + HK_App-->>Housekeeping: Task assigned (assignee: Maria) + + Housekeeping->>Kafka: Publish: adapters.housekeeping.task_assigned + + Kafka->>Tasks: Consume: adapters.housekeeping.task_assigned + Tasks->>DB: UPDATE tasks SET assignee_id = ... + Tasks->>Kafka: Publish: tasks.assigned + + Note over PMS,HK_App: Task created and assigned to housekeeping staff +``` + +--- + +## Sequence Diagram: Creating Event with Auto-Generated Tasks + +Показывает создание мероприятия и автоматическую генерацию задач. + +```mermaid +sequenceDiagram + participant User as Sales Manager + participant Web as Web Admin + participant Gateway as API Gateway + participant Events as Events Service + participant Tasks as Tasks Service + participant Kafka as Apache Kafka + participant DB as PostgreSQL + participant Notification as Notification Service + + Note over User,Notification: Сценарий: Создание конференции на 50 человек + + User->>Web: Create Event: "Marketing Conference" + Web->>Gateway: POST /api/v1/events (event details) + Gateway->>Gateway: Authenticate & Authorize + + Gateway->>Events: CreateEvent(gRPC) + Events->>DB: INSERT INTO events + DB-->>Events: Event created (id: uuid) + + Events->>Events: Generate tasks from event template + Note over Events: Tasks: Setup chairs, Prepare projector,
Organize coffee break, etc. + + loop For each generated task + Events->>Tasks: CreateTask(gRPC) + Tasks->>DB: INSERT INTO tasks + DB-->>Tasks: Task created + Tasks->>Kafka: Publish: tasks.created + Tasks-->>Events: Task ID + end + + Events->>DB: INSERT INTO event_tasks (event_id, task_id) + Events->>Kafka: Publish: events.created + Events->>Kafka: Publish: events.tasks_generated + + Events-->>Gateway: Event + generated tasks + Gateway-->>Web: 201 Created (event + tasks) + Web-->>User: Show created event with tasks + + par Notify departments + Kafka->>Notification: Consume: tasks.created + loop For each task + Notification->>Notification: Get assignee notification prefs + Notification->>Notification: Send notification (Telegram/Email) + end + end + + Note over User,Notification: All tasks created and departments notified +``` + +--- + +## Data Flow Diagram + +Показывает потоки данных в системе. + +```mermaid +flowchart TB + subgraph External["External Systems"] + PMS["PMS System"] + ERP["ERP System"] + Guest["Guest (Telegram)"] + end + + subgraph Adapters["Adapter Layer"] + PMS_A["PMS Adapter"] + ERP_A["ERP Adapter"] + TG_A["Telegram Adapter"] + end + + subgraph Kafka["Apache Kafka"] + T1["adapters.*"] + T2["tasks.*"] + T3["connections.*"] + T4["notifications.*"] + T5["audit.*"] + end + + subgraph Core["Core Services"] + Connections["Connections Service
(Routing Rules)"] + Tasks["Tasks Service
(Task Management)"] + Users["Users Service"] + Permissions["Permissions Service"] + end + + subgraph Support["Support Services"] + Notification["Notification Service"] + Audit["Audit Service"] + Scheduler["Scheduler Service"] + end + + subgraph Data["Data Layer"] + DB["PostgreSQL"] + Cache["Redis"] + end + + %% External to Adapters + PMS -->|"Webhook/API"| PMS_A + ERP -->|"API"| ERP_A + Guest -->|"Telegram Bot"| TG_A + + %% Adapters to Kafka + PMS_A -->|"Publish events"| T1 + ERP_A -->|"Publish events"| T1 + TG_A -->|"Publish events"| T1 + + %% Kafka to Connections + T1 -->|"Consume"| Connections + + %% Connections routing + Connections -->|"Route messages"| T3 + T3 -->|"Consume"| Tasks + + %% Tasks operations + Tasks -->|"Publish events"| T2 + Tasks -->|"Query permissions"| Permissions + Tasks -->|"Write/Read"| DB + Tasks -->|"Cache"| Cache + + %% Support services + T2 -->|"Consume"| Notification + T2 -->|"Consume"| Audit + Scheduler -->|"Trigger tasks"| T2 + + Notification -->|"Send notifications"| External + Audit -->|"Write logs"| DB + + %% Users and Permissions + Users -->|"Write/Read"| DB + Permissions -->|"Write/Read"| DB + Permissions -->|"Cache permissions"| Cache + + style Kafka fill:#ff9900 + style Core fill:#00cc66 + style Support fill:#3399ff + style Data fill:#cc66ff +``` + +--- + +## Notes + +### Как использовать эти диаграммы: + +1. **System Context (Level 1)**: Показывает TaskManager как черный ящик в контексте пользователей и внешних систем. Используйте для презентации бизнес-заказчикам. + +2. **Container Diagram (Level 2)**: Показывает основные приложения и хранилища данных. Используйте для обсуждения с архитекторами и DevOps командой. + +3. **Component Diagrams (Level 3)**: Показывают внутреннюю структуру отдельных сервисов. Используйте для разработчиков при проектировании конкретного сервиса. + +4. **Deployment Diagram**: Показывает как система развернута в Kubernetes. Используйте для DevOps и планирования инфраструктуры. + +5. **Sequence Diagrams**: Показывают взаимодействие компонентов во времени для конкретных сценариев. Используйте для понимания бизнес-процессов. diff --git a/Анализ требований.md b/Анализ требований.md index aedd76f..b4e2534 100644 --- a/Анализ требований.md +++ b/Анализ требований.md @@ -129,7 +129,7 @@ #### **Проблема 6: Неэффективное управление задачами для мероприятий** **AS-IS:** -- Менеджер по продажам вручную составляет "фанкшн" или "меморандум" (Word документ) +- Менеджер по продажам вручную составляет "функшн" или "меморандум" (Word документ) - В документе описываются все задачи для разных служб: - Застройка зала (расстановка столов, стульев) - Предоставление оборудования (проектор, экран) @@ -142,15 +142,15 @@ **TO-BE:** - Менеджер описывает мероприятие в системе один раз - Система автоматически создает задачи для каждой службы -- Автоматическая генерация "фанкшн" +- Автоматическая генерация "функшн" - Все задачи отслеживаются в единой системе - Массовое создание связанных задач для разных подразделений -**Требование:** +**Требование:** - Возможность массового создания задач (bulk task creation) - Описание мероприятия с автоматической декомпозицией на задачи - Привязка задач к конференц-залам и времени -- Формирование документа "фанкшн" на основе введенных данных +- Формирование документа "функшн" на основе введенных данных **Примечание:** Функционал, который можно продавать отдельным модулем. Пока не решен полноценно ни одной существующей системой. diff --git a/Архитектура системы TaskManager.md b/Архитектура системы TaskManager.md new file mode 100644 index 0000000..b821b61 --- /dev/null +++ b/Архитектура системы TaskManager.md @@ -0,0 +1,1053 @@ +[Назад](/README.md) + +# Архитектура системы TaskManager + +## Содержание +1. [Общее описание](#общее-описание) +2. [Архитектурные принципы](#архитектурные-принципы) +3. [Компоненты системы](#компоненты-системы) +4. [Микросервисы](#микросервисы) +5. [Модель данных](#модель-данных) +6. [Коммуникация между сервисами](#коммуникация-между-сервисами) +7. [Контракт сообщений](#контракт-сообщений) +8. [Безопасность и авторизация](#безопасность-и-авторизация) +9. [Масштабирование](#масштабирование) +10. [Технологический стек](#технологический-стек) + +--- + +## Общее описание + +TaskManager - это интеграционная платформа для управления задачами между различными подразделениями отеля и внешними системами. Система выступает в роли мастер-приложения, объединяющего разрозненные системы (PMS, ERP, системы управления уборкой и т.д.) через адаптеры. + +### Ключевые особенности: +- **Мультитенантность**: поддержка нескольких отелей одного владельца (1-50+ отелей) +- **Модульность**: возможность продажи отдельных модулей (базовый функционал, модуль мероприятий, модуль уборки и т.д.) +- **Расширяемость**: простота добавления новых адаптеров для интеграции с внешними системами +- **Гибкость**: настраиваемые роли, статусы задач, правила маршрутизации + +--- + +## Архитектурные принципы + +### 1. Микросервисная архитектура +Система построена на микросервисах, каждый из которых отвечает за свою domain-область. + +### 2. Event-Driven Architecture +Использование Apache Kafka для асинхронной коммуникации и обработки событий. + +### 3. Адаптер-паттерн для интеграций +Каждая внешняя система имеет свой адаптер, который: +- Преобразует данные внешней системы в стандартизированный формат +- Работает как по push, так и по pull модели (в зависимости от возможностей внешней системы) +- Изолирует логику интеграции от бизнес-логики + +### 4. API Gateway +Единая точка входа для клиентских приложений с поддержкой: +- REST API для простых запросов +- gRPC для высокопроизводительной коммуникации +- WebSocket для real-time обновлений + +### 5. CQRS (Command Query Responsibility Segregation) +Разделение операций чтения и записи для повышения производительности. + +--- + +## Компоненты системы + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Client Layer │ +├─────────────────────────────────────────────────────────────────┤ +│ Web Admin │ Mobile Apps │ External Systems │ Telegram Bot│ +└──────┬────────────┬─────────────────┬────────────────┬──────────┘ + │ │ │ │ + └────────────┴─────────────────┴────────────────┘ + │ +┌───────────────────────────┼───────────────────────────────────────┐ +│ API Gateway Layer │ +├───────────────────────────┴───────────────────────────────────────┤ +│ REST API │ gRPC │ WebSocket │ +└───────────────────────────┬───────────────────────────────────────┘ + │ +┌───────────────────────────┼───────────────────────────────────────┐ +│ Service Layer │ +├───────────────────────────┴───────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ Tasks │ │ Connections │ │ Users │ │ +│ │ Service │ │ Service │ │ Service │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │Notification │ │ File Storage │ │ Audit │ │ +│ │ Service │ │ Service │ │ Service │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ Scheduler │ │ Permissions │ │ Events │ │ +│ │ Service │ │ Service │ │ Service │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ │ +└───────────────────────────────────────────────────────────────────┘ + │ +┌───────────────────────────┼───────────────────────────────────────┐ +│ Message Broker (Kafka) │ +├───────────────────────────┴───────────────────────────────────────┤ +│ Topics: tasks.created, tasks.updated, tasks.completed, │ +│ notifications.send, audit.log, etc. │ +└───────────────────────────┬───────────────────────────────────────┘ + │ +┌───────────────────────────┼───────────────────────────────────────┐ +│ Adapter Layer │ +├───────────────────────────┴───────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │PMS Adapter │ │ERP Adapter │ │Housekeeping │ │ +│ │ (Opera/...) │ │ │ │ Adapter │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │Telegram Bot │ │ Custom │ │ Custom │ │ +│ │ Adapter │ │ Adapter #N │ │ Adapter #M │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ │ +└───────────────────────────┬───────────────────────────────────────┘ + │ +┌───────────────────────────┼───────────────────────────────────────┐ +│ Data Layer │ +├───────────────────────────┴───────────────────────────────────────┤ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ PostgreSQL │ │ Redis │ │ S3 │ │ +│ │ (Main DB) │ │ (Cache) │ │ (File Store) │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ │ +└───────────────────────────────────────────────────────────────────┘ +``` + +--- + +## Микросервисы + +### 1. API Gateway +**Назначение**: Единая точка входа для всех клиентских приложений + +**Ответственность**: +- Маршрутизация запросов к соответствующим микросервисам +- Аутентификация и авторизация +- Rate limiting +- Request/Response трансформация +- Логирование запросов + +**Технологии**: Kong / Envoy / NGINX + gRPC Gateway + +**Endpoints**: +- REST API: `/api/v1/*` +- gRPC: порт 50051 +- WebSocket: `/ws/*` + +--- + +### 2. Tasks Service +**Назначение**: Управление жизненным циклом задач + +**Ответственность**: +- CRUD операции с задачами +- Управление статусами задач (с поддержкой кастомных статусов) +- Управление зависимостями между задачами +- Назначение исполнителей +- Версионирование задач (история изменений) +- Прикрепление файлов к задачам + +**База данных**: PostgreSQL +- Таблицы: `tasks`, `task_statuses`, `task_dependencies`, `task_history`, `task_attachments` + +**Kafka Topics**: +- Публикует: `tasks.created`, `tasks.updated`, `tasks.status_changed`, `tasks.completed`, `tasks.cancelled` +- Подписан на: `scheduler.task_trigger`, `connections.route_task` + +**gRPC Service**: +```protobuf +service TaskService { + rpc CreateTask(CreateTaskRequest) returns (Task); + rpc UpdateTask(UpdateTaskRequest) returns (Task); + rpc GetTask(GetTaskRequest) returns (Task); + rpc ListTasks(ListTasksRequest) returns (ListTasksResponse); + rpc ChangeTaskStatus(ChangeStatusRequest) returns (Task); + rpc AddDependency(AddDependencyRequest) returns (TaskDependency); +} +``` + +--- + +### 3. Connections Service +**Назначение**: Управление связями между системами и правилами маршрутизации + +**Ответственность**: +- Хранение конфигурации связей между адаптерами +- Определение правил маршрутизации сообщений +- Динамическое обновление правил без перезапуска +- Валидация связей + +**База данных**: PostgreSQL +- Таблицы: `connections`, `routing_rules`, `adapters` + +**Модель связи** (направленная): +```json +{ + "id": "uuid", + "source_adapter_id": "uuid", + "target_adapter_id": "uuid", + "routing_rules": [ + { + "condition": "task_type == 'cleaning'", + "action": "route", + "enabled": true + } + ], + "enabled": true, + "created_at": "timestamp", + "updated_at": "timestamp" +} +``` + +**Kafka Topics**: +- Публикует: `connections.updated`, `connections.route_task` +- Подписан на: `tasks.created`, `adapters.message_received` + +**gRPC Service**: +```protobuf +service ConnectionService { + rpc CreateConnection(CreateConnectionRequest) returns (Connection); + rpc UpdateConnection(UpdateConnectionRequest) returns (Connection); + rpc DeleteConnection(DeleteConnectionRequest) returns (Empty); + rpc ListConnections(ListConnectionsRequest) returns (ListConnectionsResponse); + rpc RouteMessage(RouteMessageRequest) returns (RouteMessageResponse); +} +``` + +--- + +### 4. Users Service +**Назначение**: Управление пользователями и их профилями + +**Ответственность**: +- CRUD операции с пользователями +- Управление профилями +- Привязка к отелям и подразделениям +- Хранение настроек пользователя + +**База данных**: PostgreSQL +- Таблицы: `users`, `user_profiles`, `user_hotels`, `departments` + +**Kafka Topics**: +- Публикует: `users.created`, `users.updated`, `users.deleted` + +**gRPC Service**: +```protobuf +service UserService { + rpc CreateUser(CreateUserRequest) returns (User); + rpc GetUser(GetUserRequest) returns (User); + rpc UpdateUser(UpdateUserRequest) returns (User); + rpc ListUsers(ListUsersRequest) returns (ListUsersResponse); +} +``` + +--- + +### 5. Permissions Service +**Назначение**: Управление ролями и правами доступа (RBAC) + +**Ответственность**: +- Управление ролями (с поддержкой кастомных ролей на уровне отеля) +- Управление правами доступа +- Проверка прав доступа +- Привязка пользователей к ролям + +**База данных**: PostgreSQL +- Таблицы: `roles`, `permissions`, `role_permissions`, `user_roles` + +**Предопределенные роли**: +- `system_admin` - системный администратор +- `support` - техническая поддержка +- `hotel_admin` - администратор отеля +- `department_head` - руководитель подразделения +- `worker` - сотрудник + +**Кастомные роли**: отель может создавать свои роли на основе базовых + +**gRPC Service**: +```protobuf +service PermissionService { + rpc CreateRole(CreateRoleRequest) returns (Role); + rpc AssignRole(AssignRoleRequest) returns (UserRole); + rpc CheckPermission(CheckPermissionRequest) returns (PermissionResponse); + rpc ListRoles(ListRolesRequest) returns (ListRolesResponse); +} +``` + +--- + +### 6. Notification Service +**Назначение**: Отправка уведомлений через различные каналы + +**Ответственность**: +- Отправка Email +- Отправка Push-уведомлений +- Отправка SMS +- Отправка сообщений в Telegram +- Отправка сообщений в другие мессенджеры (WhatsApp, Viber и т.д.) +- Управление шаблонами уведомлений +- Управление подписками пользователей + +**База данных**: PostgreSQL +- Таблицы: `notification_templates`, `notification_subscriptions`, `notification_log` + +**Kafka Topics**: +- Подписан на: `notifications.send`, `tasks.created`, `tasks.assigned`, `tasks.completed` + +**Интеграции**: +- Email: SMTP +- Push: Firebase Cloud Messaging (FCM) / Apple Push Notification Service (APNS) +- SMS: Twilio / другие провайдеры +- Telegram: Telegram Bot API +- WhatsApp: WhatsApp Business API + +**gRPC Service**: +```protobuf +service NotificationService { + rpc SendNotification(SendNotificationRequest) returns (NotificationResponse); + rpc CreateTemplate(CreateTemplateRequest) returns (Template); + rpc Subscribe(SubscribeRequest) returns (Subscription); +} +``` + +--- + +### 7. File Storage Service +**Назначение**: Управление файлами и изображениями + +**Ответственность**: +- Загрузка файлов в S3 +- Генерация временных ссылок для скачивания +- Управление метаданными файлов +- Оптимизация изображений (resize, compress) +- Удаление файлов + +**Хранилище**: AWS S3 / MinIO (S3-compatible) + +**База данных**: PostgreSQL +- Таблицы: `files`, `file_metadata` + +**REST API**: +- `POST /api/v1/files/upload` - загрузка файла +- `GET /api/v1/files/{id}` - получение метаданных +- `GET /api/v1/files/{id}/download` - генерация signed URL +- `DELETE /api/v1/files/{id}` - удаление файла + +--- + +### 8. Audit Service +**Назначение**: Логирование всех действий в системе + +**Ответственность**: +- Запись всех изменений в системе +- Хранение истории действий пользователей +- Предоставление API для просмотра логов +- Ретеншн политика (удаление старых логов) + +**База данных**: PostgreSQL (с партиционированием по дате) +- Таблицы: `audit_log` (партиционирована) + +**Kafka Topics**: +- Подписан на: `*.created`, `*.updated`, `*.deleted`, `tasks.*`, `users.*` + +**Модель лога**: +```json +{ + "id": "uuid", + "timestamp": "2025-12-13T10:00:00Z", + "user_id": "uuid", + "hotel_id": "uuid", + "action": "task.created", + "entity_type": "task", + "entity_id": "uuid", + "changes": { + "before": {}, + "after": {} + }, + "ip_address": "192.168.1.1", + "user_agent": "Mozilla/5.0..." +} +``` + +**gRPC Service**: +```protobuf +service AuditService { + rpc LogAction(LogActionRequest) returns (Empty); + rpc GetAuditLog(GetAuditLogRequest) returns (GetAuditLogResponse); +} +``` + +--- + +### 9. Scheduler Service +**Назначение**: Управление периодическими задачами + +**Ответственность**: +- Создание расписаний для задач (cron-подобный синтаксис) +- Триггеринг создания задач по расписанию +- Управление повторяющимися задачами + +**База данных**: PostgreSQL +- Таблицы: `schedules`, `schedule_executions` + +**Модель расписания**: +```json +{ + "id": "uuid", + "name": "Уборка холла каждый день в 8:00", + "cron_expression": "0 8 * * *", + "task_template": { + "title": "Уборка холла", + "description": "Провести уборку холла", + "task_type": "cleaning", + "assignee_id": "uuid" + }, + "enabled": true, + "timezone": "Europe/Moscow", + "next_run": "2025-12-14T08:00:00Z" +} +``` + +**Kafka Topics**: +- Публикует: `scheduler.task_trigger` + +**gRPC Service**: +```protobuf +service SchedulerService { + rpc CreateSchedule(CreateScheduleRequest) returns (Schedule); + rpc UpdateSchedule(UpdateScheduleRequest) returns (Schedule); + rpc DeleteSchedule(DeleteScheduleRequest) returns (Empty); + rpc ListSchedules(ListSchedulesRequest) returns (ListSchedulesResponse); +} +``` + +--- + +### 10. Events Service +**Назначение**: Управление мероприятиями (модуль Events Management) + +**Ответственность**: +- Создание мероприятий +- Автоматическая генерация задач для разных подразделений +- Генерация документа "функшн" +- Управление конференц-залами и ресурсами + +**База данных**: PostgreSQL +- Таблицы: `events`, `event_tasks`, `conference_rooms`, `event_resources` + +**Модель мероприятия**: +```json +{ + "id": "uuid", + "title": "Конференция по маркетингу", + "description": "Описание мероприятия", + "start_time": "2025-12-20T10:00:00Z", + "end_time": "2025-12-20T18:00:00Z", + "conference_room_id": "uuid", + "tasks": [ + { + "department": "housemen", + "title": "Расставить 50 стульев и 10 столов", + "due_before": "2025-12-20T09:00:00Z" + }, + { + "department": "it", + "title": "Подготовить проектор и экран", + "due_before": "2025-12-20T09:30:00Z" + }, + { + "department": "restaurant", + "title": "Организовать кофе-брейк на 50 человек", + "due_before": "2025-12-20T12:00:00Z" + } + ] +} +``` + +**Kafka Topics**: +- Публикует: `events.created`, `events.task_generated` + +**gRPC Service**: +```protobuf +service EventService { + rpc CreateEvent(CreateEventRequest) returns (Event); + rpc UpdateEvent(UpdateEventRequest) returns (Event); + rpc GenerateTasks(GenerateTasksRequest) returns (GenerateTasksResponse); + rpc GenerateFunction(GenerateFunctionRequest) returns (FunctionDocument); +} +``` + +--- + +## Adapter Layer (Адаптеры) + +### Общая архитектура адаптера + +Каждый адаптер - это отдельный микросервис, который: +1. Подключается к внешней системе (API, БД, Webhook endpoint) +2. Слушает Kafka топики для исходящих сообщений +3. Преобразует данные в/из стандартизированного формата +4. Публикует входящие сообщения в Kafka + +**Базовая структура адаптера**: +``` +adapter/ +├── connector/ # Логика подключения к внешней системе +│ ├── api_client.py # REST/SOAP/GraphQL клиент +│ ├── db_client.py # Прямое подключение к БД (если требуется) +│ └── webhook.py # Обработчик входящих webhook'ов +├── transformer/ # Преобразование данных +│ ├── inbound.py # Внешний формат -> Стандартный формат +│ └── outbound.py # Стандартный формат -> Внешний формат +├── kafka/ +│ ├── consumer.py # Обработка исходящих сообщений +│ └── producer.py # Публикация входящих сообщений +└── config.py # Конфигурация адаптера +``` + +### Примеры адаптеров + +#### 1. PMS Adapter (например, Opera) +**Функции**: +- Получение информации о бронированиях +- Создание задач на основе заездов/выездов +- Обновление статуса номеров + +**Режим работы**: Pull (опрос API) + Push (Webhook для событий) + +**Kafka Topics**: +- Публикует: `adapters.pms.reservation_created`, `adapters.pms.checkout` +- Подписан на: `tasks.room_ready`, `tasks.room_cleaned` + +--- + +#### 2. Housekeeping Adapter +**Функции**: +- Получение задач на уборку номеров +- Отправка статусов выполнения уборки +- Загрузка фото до/после уборки + +**Режим работы**: Push (мобильное приложение отправляет данные) + +**Kafka Topics**: +- Публикует: `adapters.housekeeping.task_started`, `adapters.housekeeping.task_completed` +- Подписан на: `tasks.cleaning_assigned` + +--- + +#### 3. Telegram Bot Adapter +**Функции**: +- Прием запросов от гостей через Telegram +- Отправка уведомлений сотрудникам +- Интерактивные кнопки для частых запросов + +**Режим работы**: Push (Telegram Webhook) + +**Kafka Topics**: +- Публикует: `adapters.telegram.guest_request` +- Подписан на: `notifications.send`, `tasks.assigned` + +--- + +## Модель данных + +### Основные сущности + +#### 1. Hotels (Отели) +```sql +CREATE TABLE hotels ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(255) NOT NULL, + address TEXT, + owner_id UUID NOT NULL, -- один владелец, несколько отелей + settings JSONB DEFAULT '{}', + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); +``` + +#### 2. Departments (Подразделения) +```sql +CREATE TABLE departments ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + hotel_id UUID REFERENCES hotels(id), + name VARCHAR(255) NOT NULL, + type VARCHAR(50), -- housekeeping, technical, it, restaurant, etc. + created_at TIMESTAMP DEFAULT NOW() +); +``` + +#### 3. Users (Пользователи) +```sql +CREATE TABLE users ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + email VARCHAR(255) UNIQUE NOT NULL, + phone VARCHAR(50), + first_name VARCHAR(100), + last_name VARCHAR(100), + password_hash VARCHAR(255), + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +CREATE TABLE user_hotels ( + user_id UUID REFERENCES users(id), + hotel_id UUID REFERENCES hotels(id), + department_id UUID REFERENCES departments(id), + PRIMARY KEY (user_id, hotel_id) +); +``` + +#### 4. Tasks (Задачи) +```sql +CREATE TABLE tasks ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + hotel_id UUID REFERENCES hotels(id), + title VARCHAR(500) NOT NULL, + description TEXT, + task_type VARCHAR(100), -- cleaning, maintenance, delivery, etc. + priority VARCHAR(20) DEFAULT 'normal', -- low, normal, high, urgent + status_id UUID REFERENCES task_statuses(id), + + -- Исполнители + creator_id UUID REFERENCES users(id), + assignee_id UUID REFERENCES users(id), + department_id UUID REFERENCES departments(id), + + -- Временные метки + due_date TIMESTAMP, + started_at TIMESTAMP, + completed_at TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW(), + + -- Мета-информация + metadata JSONB DEFAULT '{}', -- для хранения дополнительных полей + source_system VARCHAR(100), -- откуда пришла задача + external_id VARCHAR(255) -- ID во внешней системе +); + +CREATE INDEX idx_tasks_assignee ON tasks(assignee_id); +CREATE INDEX idx_tasks_hotel ON tasks(hotel_id); +CREATE INDEX idx_tasks_status ON tasks(status_id); +CREATE INDEX idx_tasks_created ON tasks(created_at); +``` + +#### 5. Task Statuses (Статусы задач) +```sql +CREATE TABLE task_statuses ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + hotel_id UUID REFERENCES hotels(id), -- NULL для глобальных статусов + name VARCHAR(100) NOT NULL, + code VARCHAR(50) NOT NULL, -- created, assigned, in_progress, completed, cancelled, rejected + is_final BOOLEAN DEFAULT FALSE, -- финальный статус (completed, cancelled) + color VARCHAR(7), -- HEX цвет для UI + order_index INT, -- порядок отображения + is_custom BOOLEAN DEFAULT FALSE, -- кастомный статус отеля + created_at TIMESTAMP DEFAULT NOW() +); + +-- Предопределенные статусы +INSERT INTO task_statuses (name, code, is_final, order_index) VALUES + ('Создана', 'created', FALSE, 1), + ('Назначена', 'assigned', FALSE, 2), + ('В работе', 'in_progress', FALSE, 3), + ('Выполнена', 'completed', TRUE, 4), + ('Отменена', 'cancelled', TRUE, 5), + ('Отклонена', 'rejected', TRUE, 6); +``` + +#### 6. Task Dependencies (Зависимости задач) +```sql +CREATE TABLE task_dependencies ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + task_id UUID REFERENCES tasks(id) ON DELETE CASCADE, + depends_on_task_id UUID REFERENCES tasks(id) ON DELETE CASCADE, + dependency_type VARCHAR(50) DEFAULT 'finish_to_start', -- finish_to_start, start_to_start + created_at TIMESTAMP DEFAULT NOW(), + UNIQUE(task_id, depends_on_task_id) +); +``` + +#### 7. Task History (История изменений) +```sql +CREATE TABLE task_history ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + task_id UUID REFERENCES tasks(id) ON DELETE CASCADE, + user_id UUID REFERENCES users(id), + action VARCHAR(100), -- created, status_changed, assigned, updated + changes JSONB, -- {"field": "status", "old": "created", "new": "in_progress"} + created_at TIMESTAMP DEFAULT NOW() +); + +CREATE INDEX idx_task_history_task ON task_history(task_id, created_at DESC); +``` + +#### 8. Task Attachments (Вложения) +```sql +CREATE TABLE task_attachments ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + task_id UUID REFERENCES tasks(id) ON DELETE CASCADE, + file_id UUID REFERENCES files(id), + attachment_type VARCHAR(50), -- photo_before, photo_after, document, voice + uploaded_by UUID REFERENCES users(id), + created_at TIMESTAMP DEFAULT NOW() +); +``` + +#### 9. Files (Файлы) +```sql +CREATE TABLE files ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + filename VARCHAR(500), + file_size BIGINT, + mime_type VARCHAR(100), + s3_bucket VARCHAR(255), + s3_key VARCHAR(500), + uploaded_by UUID REFERENCES users(id), + created_at TIMESTAMP DEFAULT NOW() +); +``` + +#### 10. Connections (Связи между адаптерами) +```sql +CREATE TABLE adapters ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(255) NOT NULL, + type VARCHAR(100), -- pms, erp, housekeeping, telegram, custom + config JSONB, -- конфигурация подключения + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT NOW() +); + +CREATE TABLE connections ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + source_adapter_id UUID REFERENCES adapters(id), + target_adapter_id UUID REFERENCES adapters(id), + routing_rules JSONB DEFAULT '[]', -- массив правил маршрутизации + is_enabled BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); +``` + +#### 11. Schedules (Расписания) +```sql +CREATE TABLE schedules ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + hotel_id UUID REFERENCES hotels(id), + name VARCHAR(255), + cron_expression VARCHAR(100), -- "0 8 * * *" + task_template JSONB, -- шаблон задачи для создания + timezone VARCHAR(50) DEFAULT 'UTC', + is_enabled BOOLEAN DEFAULT TRUE, + next_run TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); + +CREATE TABLE schedule_executions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + schedule_id UUID REFERENCES schedules(id), + executed_at TIMESTAMP DEFAULT NOW(), + task_id UUID REFERENCES tasks(id), -- созданная задача + status VARCHAR(50) -- success, failed +); +``` + +#### 12. Roles & Permissions +```sql +CREATE TABLE roles ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + hotel_id UUID REFERENCES hotels(id), -- NULL для глобальных ролей + name VARCHAR(100) NOT NULL, + code VARCHAR(50), -- system_admin, support, hotel_admin, etc. + is_custom BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP DEFAULT NOW() +); + +CREATE TABLE permissions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + resource VARCHAR(100), -- tasks, users, connections, etc. + action VARCHAR(50), -- create, read, update, delete + description TEXT +); + +CREATE TABLE role_permissions ( + role_id UUID REFERENCES roles(id), + permission_id UUID REFERENCES permissions(id), + PRIMARY KEY (role_id, permission_id) +); + +CREATE TABLE user_roles ( + user_id UUID REFERENCES users(id), + role_id UUID REFERENCES roles(id), + hotel_id UUID REFERENCES hotels(id), -- в контексте какого отеля + PRIMARY KEY (user_id, role_id, hotel_id) +); +``` + +--- + +## Коммуникация между сервисами + +### 1. Синхронная коммуникация (gRPC) +Используется для: +- Запросы на чтение (GET операции) +- Операции требующие немедленного ответа +- Проверка прав доступа + +**Примеры**: +- API Gateway → Tasks Service: получение списка задач +- API Gateway → Users Service: получение профиля пользователя +- Tasks Service → Permissions Service: проверка прав доступа + +### 2. Асинхронная коммуникация (Kafka) +Используется для: +- Операции записи (создание, обновление) +- Распространение событий +- Интеграция между сервисами + +**Структура Kafka топиков**: +``` +tasks.created # Создана новая задача +tasks.updated # Задача обновлена +tasks.status_changed # Изменен статус задачи +tasks.assigned # Задача назначена +tasks.completed # Задача выполнена + +users.created +users.updated +users.deleted + +connections.updated # Обновлены связи +connections.route_task # Маршрутизация задачи + +notifications.send # Отправить уведомление + +scheduler.task_trigger # Триггер для создания задачи по расписанию + +adapters.*.message # Сообщения от адаптеров (подстановочные топики) + +audit.* # Все события для аудита +``` + +### 3. REST API (для внешних клиентов) +Используется для: +- Web админка +- Мобильные приложения (если gRPC не поддерживается) +- Webhook endpoints для адаптеров + +--- + +## Контракт сообщений + +### Стандартизированное сообщение (Kafka Event) + +```json +{ + "event_id": "uuid", + "event_type": "task.created", + "timestamp": "2025-12-13T10:00:00Z", + "version": "1.0", + + "source": { + "service": "tasks-service", + "adapter_id": "uuid", + "system": "pms_opera" + }, + + "target": { + "adapter_id": "uuid", + "system": "housekeeping_app" + }, + + "payload": { + "task": { + "id": "uuid", + "hotel_id": "uuid", + "title": "Уборка номера 305", + "description": "Гость выехал, требуется полная уборка", + "task_type": "cleaning", + "priority": "normal", + "status": "created", + + "assignee": { + "id": "uuid", + "name": "Иванова Мария", + "department": "housekeeping" + }, + + "metadata": { + "room_number": "305", + "checkout_time": "2025-12-13T12:00:00Z", + "guest_name": "Петров А.А.", + "special_requirements": [] + }, + + "due_date": "2025-12-13T14:00:00Z", + "created_at": "2025-12-13T10:00:00Z" + } + }, + + "correlation_id": "uuid", // для трейсинга + "causation_id": "uuid" // ID события, которое вызвало это событие +} +``` + +### Стандартизированное сообщение для адаптеров + +Адаптеры преобразуют внешний формат в этот контракт: + +```json +{ + "message_id": "uuid", + "message_type": "task_create_request", + "timestamp": "2025-12-13T10:00:00Z", + + "source_system": "pms_opera", + "target_system": "taskmanager", + + "hotel_id": "uuid", + + "data": { + "task": { + // стандартные поля задачи + } + }, + + "external_reference": { + "system": "pms_opera", + "id": "OPERA-12345", + "type": "reservation" + } +} +``` + +--- + +## Безопасность и авторизация + +### 1. Аутентификация +- **JWT токены** для API Gateway +- **mTLS** для межсервисной коммуникации (gRPC) +- **API Keys** для внешних систем (адаптеры) + +### 2. Авторизация (RBAC) +Проверка прав на уровне: +- API Gateway (базовая проверка роли) +- Каждый сервис (детальная проверка permissions) + +**Формат JWT токена**: +```json +{ + "sub": "user_id", + "email": "user@example.com", + "hotels": ["hotel_id_1", "hotel_id_2"], + "roles": [ + { + "hotel_id": "hotel_id_1", + "role": "hotel_admin" + } + ], + "exp": 1234567890 +} +``` + +### 3. Изоляция данных +- Все запросы фильтруются по `hotel_id` на уровне БД +- Row Level Security (RLS) в PostgreSQL +- Пользователи видят только данные отелей, к которым у них есть доступ + +--- + +## Масштабирование + +### 1. Horizontal Scaling +- Все сервисы stateless и могут масштабироваться горизонтально +- Kubernetes HPA (Horizontal Pod Autoscaler) на основе CPU/Memory + +### 2. Database Scaling +- **Read Replicas** для PostgreSQL +- **Connection Pooling** (PgBouncer) +- **Партиционирование** для больших таблиц (audit_log, task_history) + +### 3. Kafka Scaling +- Партиционирование топиков по `hotel_id` +- Consumer Groups для параллельной обработки + +### 4. Caching +- **Redis** для кэширования: + - Данных пользователей + - Разрешений (permissions) + - Конфигурации связей (connections) + - Списков задач + +--- + +## Технологический стек + +### Backend Services +- **Язык**: Python 3.11+ +- **Фреймворк**: FastAPI (для REST) + gRPC +- **ORM**: SQLAlchemy +- **Миграции БД**: Alembic + +### Message Broker +- **Apache Kafka** (одиночный инстанс для MVP) + +### Databases +- **PostgreSQL 15+** (основная БД) +- **Redis 7+** (кэш, сессии) + +### File Storage +- **AWS S3** / MinIO (S3-compatible) + +### API Gateway +- **Kong** / Envoy / NGINX + gRPC Gateway + +### Frontend +- **Framework**: React 18+ с TypeScript +- **UI Library**: Material-UI / Ant Design +- **State Management**: Redux Toolkit / Zustand +- **API Client**: gRPC-Web + +### Infrastructure +- **Containerization**: Docker +- **Orchestration**: Kubernetes +- **CI/CD**: GitLab CI / GitHub Actions +- **Monitoring**: Prometheus + Grafana +- **Logging**: ELK Stack (Elasticsearch, Logstash, Kibana) / Loki +- **Tracing**: Jaeger / OpenTelemetry + +### Development Tools +- **API Documentation**: OpenAPI (Swagger) для REST, Buf для gRPC +- **Code Quality**: Ruff, Black, MyPy +- **Testing**: Pytest, pytest-asyncio + +--- + +## Следующие шаги + +1. Детальная проработка gRPC контрактов (.proto файлы) +2. Проектирование REST API спецификации (OpenAPI) +3. Разработка примера адаптера (PMS Adapter) +4. Проектирование UI/UX для Web админки +5. Настройка CI/CD pipeline +6. Разработка Docker Compose для локальной разработки +7. Kubernetes манифесты для деплоя + diff --git a/Архитектурный скетч.drawio b/Архитектурный скетч.drawio new file mode 100644 index 0000000..5f01b2d --- /dev/null +++ b/Архитектурный скетч.drawio @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Вопросы по архитектуре.md b/Вопросы по архитектуре.md new file mode 100644 index 0000000..1b53dc6 --- /dev/null +++ b/Вопросы по архитектуре.md @@ -0,0 +1,117 @@ +[Назад](/README.md) + +# Вопросы по архитектуре TaskManager + +## 1. Стандартизация сообщений +- Какие ключевые поля должны быть в стандартизированном сообщении? + - Например: task_id, source_system, target_system, task_type, priority, description, attachments, due_date +- Нужна ли поддержка разных типов задач (одноразовые, периодические, срочные)? + +**Ответ:** +1. Пока не думал над контрактом, тк это уже детали. Наверно общие поля стоит указать, как минимум для примера. Но контракт точно поменяется, многое будет зависить от выбора решений. +2. Задачи точно будут одноразовые и периодические + + +--- + +## 2. Connections-service +- Связи между системами должны быть двусторонними или направленными? +- Нужна ли поддержка правил маршрутизации (например, "все задачи типа 'уборка' из System_1 идут в System_2")? +- Должны ли связи настраиваться динамически через админку без перезапуска сервисов? + +**Ответ:** +1. Сложно сказать, наверное правильнее строить односторонние связи. В случае если нам нужна двусторонняя связь, то мы сперва деляем связь от системы 1 к системе 2, а потом отдельную связь от системы 2 к системе 1. Думаю так будет гибче? +2. Какие то правила наверняка потребуются в будующем, стоит заложится под это, но пока не понятно как это будет +3. Да связи настраиваются динамически через админку и без перезапуска. + + +--- + +## 3. Адаптеры +- Какие типы внешних систем предполагаются для первых адаптеров? (PMS системы, ERP, существующие task-менеджеры?) +- Адаптеры должны работать по push-модели (отправляют сами) или pull-модели (периодически опрашивают внешние системы)? +- Нужна ли поддержка WebHooks для интеграций? + +**Ответ:** +1. PMS, ERP, управление уборщицами +2. Суть адаптера в том, что он адаптер. Системы бывают очень разные, гдето есть api, гдето надо будет самому пойти в базу, гдето нужно опрашивать систему, гдето система сама будет отправлять данные. Вообщем каждый адаптер будет уникален, но его задача адаптировать сообщения от\из системы в нашу +3. Пока сложно сказать, не понятно применение, какие у тебя идеи? + + +--- + +## 4. Tasks-service +- Какие основные статусы задачи? (created, assigned, in_progress, completed, cancelled, rejected?) +- Нужна ли поддержка подзадач и зависимостей между задачами? +- Нужно ли версионирование задач (история изменений)? + +**Ответ:** +1. Пока да, но вероятно пользователь захочет созхдавать "свои" статусы. Поэтому вероятно стоит заложится что в MVP делаем стандартынй набор, а потом чтобы смогли поддержать гибкость +2. Между задачами точно появятся зависимости. На счет подзадач не уверен, пока откажемся от них +3. История изменений нужна + + +--- + +## 5. Безопасность и разграничение доступа +- Будет ли мультитенантность (несколько отелей в одной инсталляции)? +- Какие основные роли предполагаются? (admin, manager, department_head, worker?) +- Нужна ли изоляция данных между отелями? + +**Ответ:** +1. Да отелей у компании может быть несколько +2. админ, техподдержка, и по ролям дальше, все виды сотрудников. скорее всего тоже надо делать настраиваемым, чтобы можно было для отелей настроить свои роли, вдруг там уникальные какието названия +3. Скорее всего нет, так как тот же маркетинг один на всю сетку отелей. Мы все же говорим что отелей несколько, но владелец один. Тоесть между отелями разных владельцев связей не будет! + + +--- + +## 6. Масштабирование +- Сколько отелей планируется поддерживать в одной инсталляции? +- Какой объем сообщений в день ожидается (примерно)? +- Kafka будет в одном экземпляре или нужен кластер? + +**Ответ:** +1. Может быть разное, от 1 до 50 (а может и больше) +2. Пока не известно, будем проводить НТ после разработки MVP +3. Пока предполагаем одну, кажется должно хватить. Разграничивать сообщения топиками. + + +--- + +## 7. Дополнительные сервисы +- **File Storage Service** - где хранить фото/файлы? (S3-compatible, локально, CDN?) +- **Notification Service** - нужны ли уведомления? (email, push, SMS?) +- **Audit Service** - нужен ли отдельный сервис для аудита действий? +- **Scheduler Service** - для периодических задач нужен отдельный сервис? + +**Ответ:** +1. s3 +2. уведомления нужны: email, push, sms, telegram, max и любые другие месенджеры +3. да аудит нужен +4. scheduler тоже нужен + + +--- + +## 8. Технологический стек +- Python сервисы - какой фреймворк? (FastAPI, Django, Flask?) +- Нужна ли REST API Gateway перед всеми сервисами? +- GraphQL рассматривается или только REST? +- Контейнеризация (Docker) + оркестрация (Kubernetes, Docker Compose)? + +**Ответ:** +1. Пока не понятно. Это уже детали +2. Rest api нужно для веба. Но еще для веба нужно получать информацию и от сервера. Поэтому вероятно лучше сразу смотреть в grpc + может rest оставить для каких то отдельных запросов +3. Да, скорее всего кластер кубер. Сервисы по контейнерам с возможностью масштабирования подов + + +--- + +## 9. Дополнительные вопросы от вас +Есть ли у вас дополнительные требования или ограничения, которые нужно учесть при проектировании? + +**Ответ:** +1. Пока наверно все рассказал + + diff --git a/Скетч.svg b/Скетч.svg new file mode 100644 index 0000000..b6d5425 --- /dev/null +++ b/Скетч.svg @@ -0,0 +1 @@ +
Message Broker
[Container: Apache Kafka]

Очередь сообщений
Message Broker...
connections-service
[Service: Python]

Управляет связями между адаптерами системы
connections-service...
PMS Adapter
[Service: Python]

Адаптер передачи сообщений в систему
PMS Adapter...
Custom Adapter #N
[Service: Python]

Адаптер передачи сообщений в систему
Custom Adapter #N...
PMS
[Software System]

PMS...
ERP
[Software System]

ERP...
Housekeeper
[Software System]

Housekeeper...
tasks-service
[Service: Python]

Управляет всеми тасками системы
tasks-service...
БД
[Container: Postgress]

Сохранение данных системы
БД[Container: Postgress]...
Web admin
[Container: Typescript, React]

Админка для управления системой
Web admin...
Service Layer
[Layer]
Service Layer...
Mobile Apps
[Container: ?]

Отдельные приложения для работников
Mobile Apps...
Telegram Bot
[Container: ?]

Бот для управления системой
Telegram Bot...
Client Layer
[Layer]
Client Layer...
API Gateway Layer
[Layer]
API Gateway Layer...
REST API
[Service: ?]

REST API...
gRPC
[Service: ?]

gRPC...
WebSocket
[Service: ?]

WebSocket...
users-service
[Service: Python]

Управление пользователями системы
users-service...
notification-service
[Service: Python]

Управление уведомлениями системы
notification-service...
file-storage-service
[Service: Python]

Управление файлами системы
file-storage-service...
audit-service
[Service: Python]

Аудит действий пользователя
audit-service...
scheduler-service
[Service: Python]

Создание задач по расписанию
scheduler-service...
permissions-service
[Service: Python]

Управление правами доступа пользователей
permissions-service...
events-service
[Service: Python]

Управление мероприятиями
events-service...
Adapter Layer
[Layer]
Adapter Layer...
ERP Adapter
[Service: Python]

Адаптер передачи сообщений в систему
ERP Adapter...
Housekeeping Adapter
[Service: Python]

Адаптер передачи сообщений в систему
Housekeeping Adapter...
Telegram Bot Adapter
[Service: Python]

Адаптер передачи сообщений в систему
Telegram Bot Adapter...
Data Layer
[Layer]
Data Layer...
Cache
[Container: Redis]

Сохранение данных системы
Cache...
File Storage
[Container: S3]

Сохранение данных системы
File Storage...
External System
[Layer]
External System...
e.g. Makes API calls
[e.g. JSON/HTTP]
e.g. Makes API calls...
Text is not SVG - cannot display
\ No newline at end of file