Initialize project scaffold and domain model

This commit is contained in:
a.tolmachev
2026-03-25 12:20:42 +03:00
commit fb302b2a2c
51 changed files with 6815 additions and 0 deletions
+709
View File
@@ -0,0 +1,709 @@
# Декомпозиция модулей
## 1. Цель документа
Этот документ фиксирует детальную структуру проекта до начала активной разработки. Его задача - заранее ограничить ответственность каждого компонента, избежать разрастания `mcpaas-core`, не допустить появления "универсальных" структур на все случаи жизни и сохранить понятные границы между доменной логикой, runtime, адаптерами, API и UI.
Основной принцип: каждый crate отвечает за один слой системы. Внутри crate модули должны быть маленькими, тематическими и с минимальным количеством публичных сущностей.
## 2. Общие архитектурные правила
### 2.1. Что считается правильной декомпозицией
- `core` содержит только базовую доменную модель, идентификаторы, типы ошибок и общие контракты.
- `registry` отвечает только за хранение и загрузку конфигурации операций.
- `runtime` исполняет операции, но не знает о способе их хранения.
- адаптеры знают только свой протокол и общий контракт runtime.
- `admin-api` оркестрирует use case для UI, но не содержит протокольной логики.
- `mcp-server` публикует tools и вызывает runtime, но не содержит бизнес-логики конфигурирования.
- `ui` не знает внутреннюю реализацию runtime и работает только через HTTP API.
### 2.2. Что запрещено
- помещать SQL, HTTP-клиенты или gRPC-клиенты в `mcpaas-core`;
- хранить в `core` "общие утилиты", не относящиеся к доменной модели;
- делать `runtime`, который напрямую читает БД;
- писать mapping-логику внутри REST, GraphQL или gRPC адаптеров;
- дублировать доменные типы в `admin-api`, `mcp-server` и адаптерах;
- создавать большие структуры вида `AppState`, в которые складывается все подряд;
- создавать большие enum или config-объекты, содержащие поля всех протоколов одновременно без выделенных вложенных типов.
### 2.3. Предпочтительный стиль
- узкие интерфейсы;
- маленькие DTO;
- отдельные типы для draft, published и runtime-view сущностей;
- отдельные модули для чтения, записи, валидации и исполнения;
- композиция из небольших сервисов вместо одного глобального сервиса.
## 3. Workspace-структура
Рекомендуемая структура:
```text
mcpaas/
apps/
admin-api/
mcp-server/
ui/
crates/
mcpaas-core/
mcpaas-registry/
mcpaas-runtime/
mcpaas-adapter-rest/
mcpaas-adapter-graphql/
mcpaas-adapter-grpc/
mcpaas-mapping/
mcpaas-schema/
mcpaas-proto/
```
Дополнительные crates `mcpaas-mapping`, `mcpaas-schema` и `mcpaas-proto` нужны затем, чтобы не перегружать `mcpaas-core`.
## 4. Детальная декомпозиция по crate
### 4.1. `mcpaas-core`
Назначение:
- базовые доменные типы;
- идентификаторы;
- метаданные операций;
- общие контракты и ошибки верхнего уровня.
Что должно лежать в crate:
```text
mcpaas-core/
src/
lib.rs
ids.rs
protocol.rs
operation/
mod.rs
model.rs
status.rs
target.rs
metadata.rs
auth/
mod.rs
profile.rs
secret_ref.rs
errors/
mod.rs
domain.rs
validation.rs
runtime.rs
```
Описание модулей:
- `ids.rs` - типы `OperationId`, `DescriptorId`, `ToolId` и другие идентификаторы.
- `protocol.rs` - enum протоколов и общие protocol capability flags.
- `operation/model.rs` - основная доменная модель операции без технических деталей хранения.
- `operation/status.rs` - типы состояний операции.
- `operation/target.rs` - базовые protocol-specific target structs.
- `operation/metadata.rs` - описание tool, display name, version, tags.
- `auth/profile.rs` - типы auth-профилей без привязки к конкретному клиенту.
- `auth/secret_ref.rs` - ссылки на секреты, а не сами секреты.
- `errors/*` - типизированные ошибки доменного слоя.
Что не должно лежать в crate:
- JSON Schema реализация;
- mapping engine;
- SQL-модели;
- HTTP DTO;
- protobuf parsing;
- `reqwest`, `sqlx`, `tonic`, `axum`.
Причина:
`mcpaas-core` должен быть максимально стабильным и независимым. Если положить туда все подряд, он станет точкой связности всей системы.
### 4.2. `mcpaas-schema`
Назначение:
- внутренняя модель схем;
- нормализация входа и выхода;
- представление полей для UI и runtime;
- преобразование схем из разных источников в единый вид.
Структура:
```text
mcpaas-schema/
src/
lib.rs
schema/
mod.rs
model.rs
field.rs
scalar.rs
object.rs
collection.rs
oneof.rs
enums.rs
normalize/
mod.rs
json.rs
graphql.rs
protobuf.rs
validate/
mod.rs
input.rs
output.rs
```
Описание:
- `schema/model.rs` - корневая структура схемы.
- `field.rs` - описание поля, nullable, required, description.
- `scalar.rs` - базовые scalar types.
- `object.rs` - вложенные объекты.
- `collection.rs` - массивы и map-подобные структуры.
- `oneof.rs` - представление protobuf `oneof`.
- `enums.rs` - enum-значения и метаданные.
- `normalize/*` - преобразование GraphQL и protobuf моделей в единую схему.
- `normalize/json.rs` - нормализация загруженных JSON-примеров во внутреннюю schema model.
- `validate/*` - проверка JSON относительно внутренней схемы.
Почему отдельный crate:
Схемы будут использоваться почти везде, но это не повод тащить их в `core`. Иначе `core` станет тяжелым и начнет менять версию при каждом изменении схемной логики.
### 4.3. `mcpaas-mapping`
Назначение:
- описание mapping DSL;
- компиляция mappings в runtime-представление;
- применение mappings к входу и выходу;
- автогенерация чернового mapping по загруженным примерам;
- трассировка ошибок маппинга.
Структура:
```text
mcpaas-mapping/
src/
lib.rs
model/
mod.rs
mapping.rs
source.rs
target.rs
transform.rs
parser/
mod.rs
jsonpath.rs
compile/
mod.rs
plan.rs
infer/
mod.rs
from_samples.rs
from_schema.rs
execute/
mod.rs
input.rs
output.rs
errors.rs
```
Описание:
- `model/mapping.rs` - описание одного правила mapping.
- `model/source.rs` - откуда берем данные: `mcp`, `response`, `constant`.
- `model/target.rs` - куда кладем данные: `request.path`, `request.query`, `request.body`, `output`.
- `model/transform.rs` - ограниченный набор допустимых преобразований.
- `parser/jsonpath.rs` - единый parser и validator для `JSONPath` выражений.
- `compile/plan.rs` - предварительно скомпилированный план маппинга.
- `infer/from_samples.rs` - генерация чернового mapping по загруженным примерам JSON.
- `infer/from_schema.rs` - генерация чернового mapping по нормализованной схеме.
- `execute/input.rs` - применение mappings к запросу.
- `execute/output.rs` - применение mappings к ответу.
Антипаттерн, которого нужно избежать:
не помещать mapping-правила в строковые поля, которые потом интерпретируются каждым адаптером по-своему. Mapping должен быть единым движком.
### 4.4. `mcpaas-proto`
Назначение:
- работа с `.proto` и descriptor set;
- извлечение services, methods и message schemas;
- преобразование protobuf metadata во внутренние типы.
Структура:
```text
mcpaas-proto/
src/
lib.rs
descriptor/
mod.rs
loader.rs
source.rs
registry.rs
reflect/
mod.rs
client.rs
model/
mod.rs
service.rs
method.rs
message.rs
field.rs
convert/
mod.rs
to_schema.rs
to_json.rs
from_json.rs
errors.rs
```
Описание:
- `descriptor/loader.rs` - загрузка descriptor set.
- `descriptor/source.rs` - типы источников: upload, file, reflection.
- `descriptor/registry.rs` - индексирование описаний для поиска services/methods.
- `reflect/client.rs` - клиент server reflection, если будет добавлен.
- `model/*` - protobuf-ориентированная промежуточная модель.
- `convert/to_schema.rs` - перевод protobuf message в `mcpaas-schema`.
- `convert/to_json.rs` и `from_json.rs` - преобразование runtime payload.
Почему отдельный crate:
protobuf-логика объемная и быстро начнет загрязнять gRPC adapter, если не отделить ее сразу.
### 4.5. `mcpaas-registry`
Назначение:
- хранение операций, схем, descriptor links и статусов;
- выдача draft/published представлений;
- поиск активных операций для runtime и MCP server.
Структура:
```text
mcpaas-registry/
src/
lib.rs
model/
mod.rs
record.rs
draft.rs
published.rs
repo/
mod.rs
operation_repo.rs
descriptor_repo.rs
service/
mod.rs
create_operation.rs
update_operation.rs
publish_operation.rs
list_operations.rs
get_runtime_view.rs
storage/
mod.rs
sqlite.rs
postgres.rs
cache/
mod.rs
runtime_cache.rs
errors.rs
```
Описание:
- `model/record.rs` - DB-aligned record model.
- `model/draft.rs` - модель черновика.
- `model/published.rs` - модель опубликованной операции.
- `repo/*` - контракты репозиториев.
- `service/*` - use case операции над реестром.
- `storage/*` - реализации репозиториев на `sqlx`.
- `cache/runtime_cache.rs` - кэш активных операций.
Правило:
`registry` не выполняет операции и не знает о `reqwest`/`tonic`. Он только хранит и отдает согласованные представления.
### 4.6. `mcpaas-runtime`
Назначение:
- исполнение операций;
- orchestration между схемой, mapping и адаптерами;
- выдача нормализованного результата.
Структура:
```text
mcpaas-runtime/
src/
lib.rs
executor/
mod.rs
operation_executor.rs
input_prepare.rs
output_finalize.rs
adapter/
mod.rs
traits.rs
dispatch.rs
context/
mod.rs
execution_context.rs
model/
mod.rs
runtime_operation.rs
prepared_request.rs
adapter_response.rs
errors.rs
```
Описание:
- `executor/operation_executor.rs` - основной orchestration use case.
- `executor/input_prepare.rs` - валидация входа и применение input mapping.
- `executor/output_finalize.rs` - обработка adapter response и output mapping.
- `adapter/traits.rs` - общий контракт для протокольных адаптеров.
- `adapter/dispatch.rs` - выбор адаптера по протоколу.
- `context/execution_context.rs` - correlation id, deadlines, tracing data.
- `model/runtime_operation.rs` - runtime-ready представление операции.
Правило:
`runtime` не должен знать, где хранится операция. Он получает уже готовую `runtime_operation`.
### 4.7. `mcpaas-adapter-rest`
Назначение:
- построение и выполнение REST-вызовов.
Структура:
```text
mcpaas-adapter-rest/
src/
lib.rs
client.rs
request/
mod.rs
build.rs
path.rs
query.rs
headers.rs
body.rs
response/
mod.rs
decode.rs
normalize.rs
errors.rs
```
Правило:
REST adapter не валидирует MCP input и не знает о registry. Он получает уже подготовленный request contract.
### 4.8. `mcpaas-adapter-graphql`
Назначение:
- построение и выполнение GraphQL-вызовов.
Структура:
```text
mcpaas-adapter-graphql/
src/
lib.rs
client.rs
request/
mod.rs
build.rs
variables.rs
response/
mod.rs
decode.rs
extract.rs
errors.rs
```
Правило:
GraphQL adapter не занимается introspection по умолчанию и не содержит редактор схем. Он только исполняет подготовленный operation template.
Дополнительное ограничение:
один GraphQL tool соответствует одному заранее определенному `query` или `mutation`. Адаптер не должен принимать от LLM произвольный GraphQL-документ, потому что в MCP-модели операция должна оставаться узкой, предсказуемой и валидируемой по фиксированной схеме.
### 4.9. `mcpaas-adapter-grpc`
Назначение:
- выполнение unary gRPC-вызовов на основе уже выбранного метода и descriptor metadata.
Структура:
```text
mcpaas-adapter-grpc/
src/
lib.rs
channel.rs
invoke/
mod.rs
unary.rs
request/
mod.rs
build.rs
response/
mod.rs
decode.rs
errors.rs
```
Правило:
gRPC adapter не должен сам парсить `.proto`. Этим занимается `mcpaas-proto`. Иначе в адаптере смешаются discovery и execution.
Дополнительное ограничение:
adapter поддерживает только unary RPC. Streaming-вызовы не реализуются, потому что целевая модель MCP tool в проекте соответствует сценарию `запрос -> ответ`, а не долгоживущей сессии обмена сообщениями.
### 4.10. `admin-api`
Назначение:
- HTTP API для UI;
- координация use case создания, редактирования, тестирования и публикации.
Структура:
```text
apps/admin-api/
src/
main.rs
app.rs
state.rs
router.rs
http/
mod.rs
dto/
mod.rs
operation.rs
mapping.rs
test_run.rs
handlers/
mod.rs
create_operation.rs
update_operation.rs
publish_operation.rs
list_operations.rs
test_operation.rs
export_operation_yaml.rs
import_operation_yaml.rs
upload_json_samples.rs
upload_proto.rs
list_grpc_services.rs
response.rs
services/
mod.rs
operation_service.rs
descriptor_service.rs
config_portability_service.rs
test_service.rs
errors.rs
```
Правила:
- HTTP DTO не должны протекать в доменный слой;
- handlers должны быть тонкими;
- orchestration должна жить в `services/*`;
- `state.rs` не должен разрастаться в огромную структуру. Лучше использовать вложенные state-компоненты или отдельные service bundles.
- import/export конфигурации в `YAML` должен быть отдельным use case, а не побочным эффектом обычного CRUD.
### 4.11. `mcp-server`
Назначение:
- публикация MCP tools;
- вызов runtime по имени tool;
- обновление активного списка tools.
Структура:
```text
apps/mcp-server/
src/
main.rs
app.rs
state.rs
tools/
mod.rs
list.rs
call.rs
cache.rs
translate/
mod.rs
to_mcp_tool.rs
from_mcp_input.rs
to_mcp_output.rs
errors.rs
```
Правило:
`mcp-server` не должен реализовывать business rules публикации. Он читает уже опубликованные операции и транслирует их в MCP.
### 4.12. `ui`
Назначение:
- интерфейс оператора.
Предлагаемая frontend-структура:
```text
apps/ui/
src/
main.tsx
app/
router.tsx
providers.tsx
pages/
operation-list/
operation-create/
operation-edit/
operation-test/
grpc-browser/
features/
operation-form/
mapping-editor/
sample-upload/
grpc-method-picker/
schema-viewer/
publish-operation/
entities/
operation/
descriptor/
shared/
api/
lib/
ui/
config/
```
Правило:
UI должен декомпозироваться по пользовательским сценариям, а не по типам файлов уровня "все компоненты в одной папке".
## 5. Правила зависимостей между crate
Целевой граф зависимостей:
```text
mcpaas-core
mcpaas-schema -> mcpaas-core
mcpaas-mapping -> mcpaas-core
mcpaas-proto -> mcpaas-core, mcpaas-schema
mcpaas-registry -> mcpaas-core, mcpaas-schema, mcpaas-mapping
mcpaas-adapter-rest -> mcpaas-core
mcpaas-adapter-graphql -> mcpaas-core
mcpaas-adapter-grpc -> mcpaas-core, mcpaas-proto
mcpaas-runtime -> mcpaas-core, mcpaas-schema, mcpaas-mapping, adapters
admin-api -> mcpaas-core, mcpaas-schema, mcpaas-mapping, mcpaas-proto, mcpaas-registry, mcpaas-runtime
mcp-server -> mcpaas-core, mcpaas-registry, mcpaas-runtime
```
Критические ограничения:
- `mcpaas-core` ни от кого не зависит;
- адаптеры не зависят от `registry`;
- `runtime` не зависит от `admin-api` и `mcp-server`;
- `registry` не зависит от адаптеров;
- `ui` зависит только от HTTP API.
## 6. Границы публичных API модулей
Чтобы структура не разъехалась, нужно заранее ограничить публичность.
Рекомендуемое правило:
- наружу экспортируются только корневые доменные типы, service-интерфейсы и ошибки;
- внутренние DTO, record-модели и промежуточные builder-структуры остаются `pub(crate)`;
- не реэкспортировать целые деревья модулей без необходимости;
- не делать `mod utils`, если можно назвать модуль по смыслу.
Пример плохого решения:
- `pub mod common;`
- `pub mod helpers;`
- `pub struct AppContext { ... 25 полей ... }`
Пример правильного решения:
- `pub struct OperationExecutor`
- `pub trait OperationRepository`
- `pub struct RuntimeOperation`
## 7. Какие большие структуры точно не нужны
Ниже список сущностей, которые легко превращаются в антипаттерн:
- одна гигантская `Operation`, содержащая сразу все REST, GraphQL и gRPC поля;
- один `MappingConfig`, содержащий и input, и output, и transforms, и validation rules без разделения;
- единый `AppState` со всеми репозиториями, клиентами, кэшами и конфигами;
- один `ProtocolAdapter` с ветвлением `match protocol` внутри на сотни строк;
- один `SchemaField` без выделения object/array/enum/oneof вариантов.
Правильный подход:
- отдельные target-типы по протоколам;
- отдельные input/output mapping модели;
- отдельные bounded state-наборы для каждого приложения;
- отдельные adapter crates;
- выделенная иерархия schema types.
## 8. Порядок реализации без архитектурного долга
Рекомендуемый порядок разработки:
1. `mcpaas-core`
2. `mcpaas-schema`
3. `mcpaas-mapping`
4. `mcpaas-registry`
5. `mcpaas-adapter-rest`
6. `mcpaas-runtime`
7. `admin-api`
8. `ui`
9. `mcpaas-proto`
10. `mcpaas-adapter-grpc`
11. `mcpaas-adapter-graphql`
12. `mcp-server`
Причина такого порядка:
- сначала фиксируется доменная модель;
- затем схема и mapping как самые чувствительные части;
- затем реестр и базовое выполнение REST;
- после этого можно собирать UI и только потом наращивать сложные протоколы.
## 9. Практический итог
Если придерживаться этой декомпозиции, то:
- `mcpaas-core` останется маленьким и стабильным;
- schema и mapping не смешаются с transport-логикой;
- protobuf discovery не загрязнит gRPC runtime;
- `admin-api` и `mcp-server` останутся тонкими входными слоями;
- добавление нового протокола не потребует переписывать половину проекта.
Это и есть целевая архитектурная дисциплина проекта: отдельные слои, отдельные модели, минимально необходимая публичность и отсутствие "универсальных" структур, в которые со временем начинает стекаться вся система.