Files
crank/docs/module-decomposition.md
T
2026-03-25 17:19:54 +03:00

709 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Декомпозиция модулей
## 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
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` останутся тонкими входными слоями;
- добавление нового протокола не потребует переписывать половину проекта.
Это и есть целевая архитектурная дисциплина проекта: отдельные слои, отдельные модели, минимально необходимая публичность и отсутствие "универсальных" структур, в которые со временем начинает стекаться вся система.