Статус: Draft
Дата: 2026-04-29
Область: компонентная архитектура проекта platform-core, микросервисы, модули, коммуникация и инфраструктурные технологии.
1. Термины проектирования
В этой архитектуре используются такие уровни:
- Проект - большой домен, объединяющий несколько микросервисов. Пример:
platform-core,auth-platform. - Микросервис - bounded context со своей БД и своими контрактами. Микросервисы не ходят в одну общую БД.
- Модуль микросервиса - отдельный runtime/process/pod внутри микросервиса, который разделяет БД и доменную модель этого микросервиса. Пример:
api,worker,scheduler,runner.
Пример из текущего стиля проекта:
1 2 3 4 | |
Для нового platform-core это означает: мы не пытаемся превратить все в один микросервис, но и не выносим сильно связанные части в отдельные сервисы, если это приведет к постоянным синхронным запросам и дублированию данных.
2. Главный принцип разбиения
Микросервисная граница оправдана, если:
- сервис владеет отдельной моделью данных;
- другой сервис не должен читать его данные на каждом шаге обычного сценария;
- данные можно синхронизировать событиями или projections;
- workload, scaling, risk profile или security boundary существенно отличаются.
Микросервисная граница вредна, если:
- два компонента постоянно участвуют в одном transaction-like use case;
- один компонент без другого почти не может отвечать на свои основные запросы;
- приходится хранить почти полную копию данных соседнего сервиса;
- большинство запросов превращаются в цепочку синхронных вызовов.
На этом основании scenario-registry не выносится в отдельный микросервис: он слишком тесно связан с control-plane-service.
3. Итоговая структура проекта
1 2 3 4 | |
Подробная документация по каждому core-сервису находится в services/index.ru.md.
Опциональные внешние/соседние домены:
1 2 3 | |
ai-ops-agent-service не является владельцем операций. Он анализирует, предлагает patches и reviews. iam-platform принимает решения о доступе и ролях. audit-service хранит независимый audit trail.
4. Общая схема компонентов
catalog DB)] pgControl[(PostgreSQL
control DB)] pgExecution[(PostgreSQL
execution DB)] kafka[(Kafka)] temporal[(Temporal)] vault[(Vault)] objectStorage[(S3 / MinIO)] observability[OpenTelemetry
Prometheus Loki Tempo] ui --> catalog ui --> control catalog --> pgCatalog control --> pgControl execution --> pgExecution control --> temporal control --> execution control --> catalog control --> ai control --> iam execution --> vault execution --> objectStorage catalog --> kafka control --> kafka execution --> kafka kafka --> audit catalog --> observability control --> observability execution --> observability
5. resource-catalog-service
Смысл: что у нас есть.
resource-catalog-service владеет каноническим каталогом ресурсов и их provided capabilities.
Владеет данными
- Organizations / tenant context для platform resources.
- Topology: regions, datacenters, layers, segments.
- Compute nodes.
- Clusters.
- Internal/external services.
- Deployments.
- Managed resources: postgres, redis, tempo, minio, vault, kafka и другие.
- Resource relationships.
- Resource desired state и observed state.
- Resource health summary.
- Capability definitions.
- Provided capabilities.
Модули
1 2 3 4 5 | |
| Модуль | Назначение |
|---|---|
| api | UI/API queries и команды каталога |
| worker | Обработка событий от control-plane и execution-plane |
| observer | Получение observed state из Kubernetes, agents, cloud APIs |
| projector | Read models: topology graph, resource graph, capability graph |
БД
Одна PostgreSQL БД сервиса.
1 2 3 4 5 6 7 8 9 10 | |
Важная граница
resource-catalog-service не исполняет долгие операции. Он может принять команду уровня "зарегистрировать ресурс" или "обновить metadata", но создание реального Kubernetes-кластера, установка Tempo или ремонт ноды идут через control-plane-service.
6. control-plane-service
Смысл: что хотим сделать, какой план построен и как идет операция.
control-plane-service владеет намерениями, сценариями, планами, операциями, gates, patches и timeline.
Владеет данными
- Intent.
- ScenarioPack.
- Scenario.
- PlanTemplate.
- JobTemplate / JobContract.
- CapabilityRequirement.
- ResourceBinding.
- Plan.
- PlanNode.
- Operation.
- OperationNodeRun.
- Gate.
- Intervention.
- PlanPatch.
- Artifact metadata.
- Evidence metadata.
- Operation timeline.
- Local catalog/capability projections.
Модули
1 2 3 4 5 6 7 8 9 10 | |
| Модуль | Назначение |
|---|---|
| api | Intent, plan preview, operation start, approve gate, retry, pause, patch review |
| scenario-registry | Executable marketplace scenarios, versions, plan templates, job contracts |
| planner | Построение DAG из intent + scenario |
| capability-resolver | Resolution required capabilities в providers или sub-operations |
| operation-manager | Lifecycle операций, node states, gates, retries, interventions |
| workflow-worker | Durable orchestration через Temporal |
| policy-adapter | IAM/policy checks, approvals, risk decisions |
| ai-adapter | Контекст для AI и прием Diagnosis/PlanPatch/RiskReview |
| event-worker | Kafka events, projections, integration events |
Почему scenario-registry здесь
scenario-registry не выносится в отдельный микросервис, потому что:
- planner постоянно читает scenario templates;
- resolver читает capability requirements сценариев;
- plan preview требует scenario, job contracts и policy metadata;
- operation validation зависит от версии scenario;
- PlanPatch может ссылаться на templates/jobs из registry.
Если вынести registry отдельно, control-plane-service будет либо постоянно дергать его синхронно, либо держать почти полную копию. Поэтому это модуль внутри control-plane-service, но с четкой внутренней границей, чтобы позже его можно было вынести только при реальной необходимости.
БД
Одна PostgreSQL БД сервиса.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Projections вместо постоянных вызовов
control-plane-service держит локальные projections каталога:
- resources;
- topology;
- provided capabilities;
- capability definitions;
- active/last operation references при необходимости.
Это нужно, чтобы plan preview и capability resolution не ходили в resource-catalog-service на каждый read.
Синхронные вызовы в catalog нужны только на command boundary:
- перед стартом operation;
- перед destructive action;
- перед финальным bind/update resource;
- если projection устарела и нужна сильная проверка.
Product API и история операций
control-plane-service обязан иметь собственный API для интерфейса. UI не должен зависеть от Temporal UI, Argo UI или UI другого workflow engine. Workflow engine может быть внутренним runtime, но продуктовый опыт строится поверх нашей доменной модели.
Интерфейс должен уметь показывать operation history примерно как GitLab pipelines:
- список операций по ресурсу, пользователю, организации, сценарию и статусу;
- текущую active operation для ресурса;
- историю completed/failed/cancelled operations;
- DAG плана;
- stages и jobs;
- статусы каждого узла;
- attempts, retries, duration, started/finished timestamps;
- logs и raw outputs;
- artifacts;
- evidence;
- gates и approvals;
- manual interventions;
- AI diagnosis и PlanPatch history.
Пример продуктовых API:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
Для этого нужны read models внутри control-plane-service:
1 2 3 4 5 6 7 8 | |
Правило: operation history должна быть audit-friendly и immutable на уровне фактов. Если plan меняется во время выполнения, история не переписывается; добавляется PlanPatch, approval и новая версия effective plan.
7. execution-plane-service
Смысл: как реально выполнить job.
execution-plane-service не владеет business operation model. Он владеет execution attempts, runners, raw logs, leases и technical execution records.
Владеет данными
- ExecutionAttempt.
- RunnerLease.
- RunnerStatus.
- Raw execution logs metadata.
- Runner output metadata.
- Temporary job state.
Модули
1 2 3 4 5 6 7 8 9 | |
| Модуль | Назначение |
|---|---|
| dispatcher | Выбор runner по job type и запуск execution attempt |
| ansible-runner | Bare metal, WireGuard, kubeadm, prepare nodes |
| kubernetes-runner | Kubernetes API operations |
| helm-runner | Helm install/upgrade/status |
| opentofu-runner | Cloud/IaaS provisioning |
| ssh-runner | Только ограниченные approved commands, не общий root shell |
| verification-runner | Checks и evidence: kubectl, HTTP, systemd, metrics |
| artifact-worker | Logs, manifests, reports, diagnostics bundles в S3/MinIO |
БД
Одна PostgreSQL БД сервиса.
1 2 3 4 5 6 | |
Важная граница
Source of truth по operation остается в control-plane-service. execution-plane-service возвращает execution result, artifacts и evidence, но не решает, завершена ли business operation.
8. AI agent boundary
AI не должен быть execution-plane.
AI-слой может жить как отдельный домен:
1 2 3 4 | |
Роли:
- Analyzer - анализ failed operation, logs, artifacts, evidence.
- Planner - предлагает PlanPatch.
- Reviewer - проверяет plan до запуска, объясняет risk и blast radius.
AI возвращает structured output:
1 2 3 4 | |
control-plane-service применяет результат только через policy, gates и approvals.
9. Инфраструктурные технологии
| Технология | Роль |
|---|---|
| PostgreSQL per service | Source of truth каждого микросервиса |
| Kafka | Domain events и projections между сервисами, не workflow engine |
| Temporal | Primary durable runtime для long-running операций, не source of truth |
| Vault | Secrets, kubeconfigs, SSH refs, cloud credentials |
| S3/MinIO | Artifacts, logs, diagnostics bundles, rendered manifests |
| Redis | Cache/ephemeral locks только где действительно нужен |
| OpenTelemetry | Traces на operation/job/resource уровне |
| Prometheus | Metrics |
| Loki | Logs |
| Tempo | Distributed traces |
| Ansible Runner | Host automation |
| Kubernetes API client | K8s operations без shell там, где возможно |
| Helm runner | Helm install/upgrade/status |
| OpenTofu runner | Cloud/IaaS provisioning |
| Policy adapter | IAM/OPA/Cedar-like слой за стабильным интерфейсом |
Temporal не является доменной моделью. Доменная модель живет в PostgreSQL control-plane-service. Temporal отвечает за durability: retries, timers, signals, pause/resume, human gates и long-running workflows.
10. Выбор workflow/orchestration engine
Под workflow engine здесь понимается runtime для long-running operations: retries, timers, cancellation, resume, human gates, signals, parallel branches и crash recovery.
Важно: какой бы движок ни был выбран, source of truth для продукта остается в control-plane-service. Движок не должен диктовать доменную модель Intent, Plan, Operation, PlanPatch, Evidence.
Решение для текущей архитектуры: Temporal является primary durable runtime за workflow-engine adapter. Остальные варианты остаются кандидатами для будущих specialized adapters или замены runtime, но не являются базовым выбором.
Критерии выбора
| Критерий | Почему важно |
|---|---|
| Durable execution | Операции могут идти минуты, часы или дни |
| Dynamic DAG / PlanPatch | План может меняться после failed stage |
| Human-in-the-loop | Gates, approvals, manual fix |
| External workers | Ansible, Helm, Kubernetes, OpenTofu, SSH |
| История выполнения | Нужна GitLab-like история jobs/stages |
| Self-hosted | Платформа должна работать в нашей инфраструктуре |
| Observability hooks | Traces, metrics, logs, events |
| Простая интеграция с Python/Go | Под текущий стек проекта |
Кандидаты
| Вариант | Когда подходит | Риски / ограничения |
|---|---|---|
| Temporal | Durable execution, long-running operations, signals, retries, strong SDK model | Dynamic DAG и PlanPatch надо реализовывать как интерпретацию нашего Plan, а не как "workflow code = plan" |
| Cadence | Похожая модель на Temporal, open-source predecessor | Меньше momentum и ecosystem по сравнению с Temporal |
| Netflix/Conductor OSS | Task/workflow orchestration, JSON/YAML-like definitions, хороший fit для microservice orchestration | Менее естественная модель для code-level durable execution; нужно проверить fit для сложных PlanPatch и capability contracts |
| Argo Workflows | Kubernetes-native container jobs, понятный DAG, хороший UI для pod-based workflows | Сильно Kubernetes-centric; хуже для SSH/bare metal/human gates/product domain model |
| Camunda 8 / Zeebe | BPMN, human processes, approvals, бизнес-процессы | BPMN может быть слишком тяжелым; лицензирование и source-available модель требуют отдельной проверки |
| Kestra | Declarative orchestration, UI, plugins, infra/data workflows | Нужно проверить, насколько удобно моделировать наш Operation/PlanPatch protocol |
| Prefect / Dagster / Airflow | Data/batch workflows, Python ecosystem, scheduling | Обычно хуже подходят как core control-plane runtime для интерактивных infra operations |
| Restate / DBOS | Durable execution ближе к application code, Postgres-backed модели возможны | Более новый класс решений; нужно оценить зрелость для нашего уровня operation history и gates |
| Собственный DAG engine | Максимальный контроль над PlanPatch, capability contracts, UI/history | Дорого: retries, timers, leases, cancellation, crash recovery, idempotency, backpressure, versioning придется писать самим |
Решение
Архитектурно нужно ввести внутренний workflow-engine adapter, чтобы не зашить Temporal или любой другой движок в доменную модель:
1 2 3 4 5 6 | |
Primary runtime фиксируется как Temporal, потому что он лучше всего закрывает durable execution, signals, retries и long-running operations. Но продуктовые API, operation history, plan graph, gates и patches должны жить в control-plane-service.
Если позже выяснится, что dynamic DAG/PlanPatch слишком сильно конфликтует с Temporal model, можно рассмотреть специализированные adapters:
- Conductor/Kestra как более declarative workflow runtime;
- Argo Workflows для Kubernetes-only execution subgraphs;
- собственный DAG engine поверх PostgreSQL для operation runtime;
- гибрид: control-plane own DAG engine + Temporal только для durable activities/timers.
11. Коммуникация
Синхронная коммуникация
Синхронные вызовы должны быть на границах команд, а не на каждом read.
Асинхронная коммуникация
Kafka события:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
События используются для projections, audit, read models и loosely-coupled integrations. Они не заменяют source of truth.
12. Source of truth matrix
| Данные | Source of truth |
|---|---|
| Resource metadata | resource-catalog-service |
| Resource desired/observed state | resource-catalog-service |
| Provided capabilities | resource-catalog-service |
| Capability definitions | resource-catalog-service |
| Scenario packs/templates | control-plane-service |
| Intents/plans/operations | control-plane-service |
| Gates/interventions/patches | control-plane-service |
| Artifact/evidence metadata | control-plane-service |
| Raw logs/artifact blobs | S3/MinIO |
| Secrets/kubeconfigs/credentials | Vault |
| Execution attempts/runner leases | execution-plane-service |
| Audit trail | audit-service |
13. Типовой flow: установка Tempo
14. Почему не один микросервис
Один большой platform-core-service быстро смешает:
- catalog state;
- operation state;
- execution attempts;
- runner security;
- scenario registry;
- observed state.
У этих частей разные риски и разные нагрузки. Особенно execution-plane должен быть изолирован: он работает с SSH, Ansible, Helm, Kubernetes API, secrets и потенциально опасными side effects.
15. Почему не больше микросервисов
Не стоит выносить отдельно:
- organizations внутри platform-core;
- scenario-registry;
- planner;
- capability-resolver;
- operation-manager.
Причина: они постоянно участвуют в одном planning/operation use case и ходят в одни и те же данные. Разделение создаст лишние синхронные вызовы и сложные projections без реального выигрыша.
16. Финальная граница ответственности
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
17. Открытые вопросы
- Должны ли
CapabilityDefinitionжить в resource-catalog или control-plane? Текущая рекомендация: resource-catalog, потому что это онтология ресурсов. - Какой транспорт выбрать для internal API execution-plane: gRPC, HTTP, Temporal activities wrapper или гибрид?
- Какой уровень isolation нужен runners: process, container, Kubernetes Job, Firecracker-like sandbox?
- Какой формат scenario packs выбрать: YAML directory, OCI artifact, Git repository, database records?
- Как синхронизировать versions scenario packs с уже запущенными operations?
- Какую policy technology выбрать под adapter: OPA/Rego, Cedar, custom rules или гибрид?
- Какие ограничения заложить в
workflow-engine adapter, чтобы Temporal оставался runtime, а не доменной моделью? - Какой минимальный read model нужен для GitLab-like отображения operation history без тяжелых runtime queries?