graph TB
subgraph Users
MU["Mobile User - Flutter App"]
AP["Admin/Manager - Web Panel"]
end
subgraph StagePlus["Stage+ Platform"]
GW["Ingress / API Gateway"]
CORE["Core API - backend"]
IAM["IAM Service"]
CHAT["Chat Service"]
CALC["Calculator Service"]
SPECS["Car Specs Service"]
end
subgraph External["External Systems"]
AJES["AJES Auction"]
DROM["Drom.ru - specs parser"]
CBR["CBR / Exchange Rates"]
FCM["Firebase Cloud Messaging"]
SMS["SMS Gateway - OTP Bot"]
end
subgraph Data["Data Layer"]
PG[("PostgreSQL")]
REDIS[("Redis")]
MINIO[("MinIO - S3 Storage")]
end
MU -->|"REST + WebSocket"| GW
AP -->|"REST"| GW
GW --> CORE
GW --> IAM
GW --> CHAT
GW --> CALC
GW --> SPECS
CORE --> PG
CORE --> REDIS
CORE --> MINIO
IAM --> PG
IAM --> REDIS
CHAT --> PG
CHAT --> REDIS
SPECS --> PG
CALC -->|"lookup HP"| SPECS
CHAT -->|"validate token"| IAM
CHAT -->|"car info"| CORE
CHAT -->|"push"| IAM
CORE -->|"parse"| AJES
SPECS -->|"parse"| DROM
CALC -->|"rates"| CBR
IAM -->|"push"| FCM
IAM -->|"OTP"| SMS
| Service |
Стек |
Порт |
БД |
Функции |
| Core API (backend) |
FastAPI + SQLAlchemy + asyncpg |
8000 |
PostgreSQL + Redis + MinIO |
Каталог авто, заказы, файлы, курсы, вебхуки, AJES sync |
| IAM Service |
FastAPI + SQLAlchemy + asyncpg |
— |
PostgreSQL + Redis |
Auth (phone + OTP), users, push-уведомления, версии приложения |
| Chat Service |
FastAPI + WebSocket + Redis PubSub |
— |
PostgreSQL + Redis |
Чаты, сообщения, файлы, real-time через WS |
| Calculator Service |
FastAPI (stateless) |
— |
— (in-memory) |
Расчёт стоимости авто (таможня, утиль, доставка) |
| Car Specs Service |
FastAPI + SQLAlchemy + asyncpg |
— |
PostgreSQL |
Справочник характеристик авто (парсинг Drom.ru) |
sequenceDiagram
participant U as Flutter App
participant IAM as IAM Service
participant CORE as Core API
participant CALC as Calculator
participant SPECS as Car Specs
participant CHAT as Chat Service
U->>IAM: POST /auth/send-code
IAM-->>U: OTP sent via SMS
U->>IAM: POST /auth/verify
IAM-->>U: JWT token
U->>CORE: GET /cars
CORE-->>U: Car list
U->>CALC: POST /calculate
CALC->>SPECS: GET /specs
SPECS-->>CALC: horsepower
CALC-->>U: Full cost breakdown
U->>CHAT: WS /ws with JWT
U->>CHAT: Send message
CHAT->>IAM: Push notification
IAM->>FCM: Firebase push
graph TB
subgraph FlutterApp["Flutter App"]
subgraph CoreLayer["Core Layer"]
DI["service_locator - GetIt DI"]
API["ApiClient - Dio + JWT"]
WS["WsClient - WebSocket"]
SEC["SecureStorage"]
PUSH["PushNotificationService"]
end
subgraph Features["Features"]
AUTH["auth/"]
CAT["catalog/"]
ORDERS["orders/"]
CHATF["chat/"]
PROFILE["profile/"]
HOME["home/"]
AUCTIONS["auctions/"]
end
end
DI --> API
DI --> WS
DI --> SEC
DI --> PUSH
AUTH --> API
AUTH --> SEC
CAT --> API
ORDERS --> API
CHATF --> API
CHATF --> WS
Паттерны:
- DI: GetIt (service locator), все зависимости — lazy singleton
- State Management: Riverpod (pubspec) + GetIt (фактически). ⚠️ Смешение подходов
- Routing: GoRouter
- Network: Dio (REST) + web_socket_channel (WS)
- Auth storage: flutter_secure_storage (JWT в Keychain/Keystore)
- Offline: MessageQueueService (очередь сообщений при потере связи)
graph LR
subgraph CoreAPI["Core API"]
ROUTES["api"]
MODELS["models"]
SCHEMAS["schemas"]
SERVICES["services"]
end
ROUTES -->|"uses"| SERVICES
ROUTES -->|"validates"| SCHEMAS
SERVICES -->|"ORM"| MODELS
subgraph ApiRoutes["API Endpoints"]
R1["cars"]
R2["orders"]
R3["files"]
R4["rates"]
R5["webhooks"]
end
subgraph Svc["Service Layer"]
S1["ajes_parser"]
S2["ajes_sync"]
S3["auth"]
S4["cache"]
S5["car_calculator"]
S6["minio_client"]
end
graph LR
CALC -->|"HTTP GET /specs"| SPECS
CHAT -->|"HTTP validate"| IAM["IAM - token validation"]
CHAT -->|"HTTP"| CORE["Core API - car info"]
CHAT -->|"HTTP push"| IAM2["IAM - push_client"]
subgraph Async["Async Channel"]
CHAT_WS["Chat WS"] -->|"Redis PubSub"| CHAT_SUB["Chat Subscribers"]
end
Протоколы:
- Sync: HTTP (service-to-service, внутренний)
- Async: Redis PubSub (только chat real-time)
- Client ↔ Chat: WebSocket с reconnect + exponential backoff
erDiagram
USER ||--o{ ORDER : places
USER ||--o{ DEVICE_TOKEN : has
USER ||--o{ CHAT_MEMBER : participates
CAR ||--o{ ORDER : "ordered as"
CHAT ||--|{ CHAT_MEMBER : contains
CHAT ||--o{ MESSAGE : has
MESSAGE ||--o{ FILE : attaches
CAR_SPEC ||--|| CAR : "specs for"
USER {
int id PK
string phone
string name
string role
}
CAR {
int id PK
string make
string model
int year
int engine_volume
int price_jpy
}
ORDER {
int id PK
int user_id FK
int car_id FK
string status
}
CHAT {
int id PK
string type
}
MESSAGE {
int id PK
int chat_id FK
int sender_id FK
string content
string type
}
CAR_SPEC {
int id PK
string make
string model
int engine_volume
int horsepower
}
- Декомпозиция на сервисы — логичное разделение по bounded contexts (IAM, Chat, Calculator, Specs)
- Единый стек бэкенда — FastAPI + SQLAlchemy + asyncpg везде → низкий когнитивный overhead
- Prometheus метрики — instrumentator подключен во всех сервисах
- Health checks — есть в каждом сервисе (DB + Redis проверки)
- Offline support — MessageQueueService в Flutter для chat
- WebSocket reconnect — exponential backoff в ws_client.dart
| # |
Проблема |
Рекомендация |
| 1 |
Нет API Gateway — Flutter ходит напрямую в каждый сервис (разные base URL в api_constants.dart) |
Внедрить API Gateway (nginx/Traefik/Kong) или единый ingress с path-based routing: /api/v1/iam/*, /api/v1/chat/*, etc. |
| 2 |
JWT валидация дублируется — каждый сервис имеет свой services/auth.py |
Вынести в shared library или валидировать на уровне gateway. JWT verification должен быть в одном месте. |
| 3 |
Нет service discovery / registry — URL сервисов захардкожены в env |
Для K8s — использовать DNS (уже есть), но стоит добавить retry + circuit breaker (httpx не имеет из коробки) |
| # |
Проблема |
Рекомендация |
| 4 |
Смешение DI подходов во Flutter — GetIt + Riverpod одновременно |
Выбрать один: Riverpod (предпочтительнее для Flutter) или GetIt. Смешение усложняет тестирование. |
| 5 |
Calculator CORS: allow_origins=["*"] |
Ограничить до конкретных origins |
| 6 |
Redis — single point of failure — один инстанс для cache + pubsub + sessions |
Как минимум Redis Sentinel, идеально — Redis Cluster. Разделить cache и pubsub. |
| 7 |
Нет rate limiting |
Добавить на уровне gateway (особенно auth/send-code — SMS flood) |
| 8 |
Celery в requirements но не используется (backend) |
Убрать или внедрить для тяжёлых задач (AJES sync?) |
| # |
Проблема |
Рекомендация |
| 9 |
Нет OpenAPI contract testing между сервисами |
Добавить schema validation (Pact или просто shared schemas) |
| 10 |
Chat — нет message persistence guarantee |
При WS disconnect сообщения могут потеряться между PubSub и клиентом. Нужен cursor/offset для catch-up. |
| 11 |
Нет structured logging (json) |
python-json-logger в requirements, но logging.basicConfig перезаписывает. Использовать json formatter. |
| 12 |
Feature-first без domain layer в некоторых features |
auth и chat имеют domain/, но catalog и orders — нет. Унифицировать. |
graph TB
subgraph Clients
APP["Flutter App"]
WEB["Web Panel"]
end
subgraph Edge
INGRESS["Ingress Nginx - TLS + Rate Limit"]
end
subgraph Gateway["API Gateway Layer"]
GW["API Gateway - routing, JWT, rate limit"]
end
subgraph Services
CORE["Core API"]
IAM["IAM Service"]
CHAT["Chat Service"]
CALC["Calculator"]
SPECS["Car Specs"]
end
subgraph DataLayer["Data"]
PG[("PostgreSQL - per-service DB")]
REDIS_CACHE[("Redis Cache")]
REDIS_PUBSUB[("Redis PubSub")]
MINIO[("MinIO")]
end
subgraph Observability
PROM["VictoriaMetrics"]
GRAF["Grafana"]
LOGS["Loki - structured logs"]
end
APP --> INGRESS
WEB --> INGRESS
INGRESS --> GW
GW --> CORE
GW --> IAM
GW --> CHAT
GW --> CALC
GW --> SPECS
CORE --> PG
IAM --> PG
CHAT --> PG
SPECS --> PG
CORE --> REDIS_CACHE
IAM --> REDIS_CACHE
CHAT --> REDIS_PUBSUB
CORE --> MINIO
CHAT --> MINIO
CORE --> PROM
IAM --> PROM
CHAT --> PROM
CALC --> PROM
SPECS --> PROM
- API Gateway — единая точка входа, JWT validation, rate limiting
- Database per service — изоляция данных (сейчас, вероятно, shared DB)
- Redis split — cache отдельно от pubsub
- Structured logging → Loki — централизованные логи
- Circuit breaker — для inter-service calls