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

24 KiB
Raw Blame History

Декомпозиция модулей

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-структура

Рекомендуемая структура:

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:

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;
  • преобразование схем из разных источников в единый вид.

Структура:

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 по загруженным примерам;
  • трассировка ошибок маппинга.

Структура:

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 во внутренние типы.

Структура:

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.

Структура:

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 и адаптерами;
  • выдача нормализованного результата.

Структура:

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-вызовов.

Структура:

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-вызовов.

Структура:

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.

Структура:

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 создания, редактирования, тестирования и публикации.

Структура:

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.

Структура:

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-структура:

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

Целевой граф зависимостей:

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 останутся тонкими входными слоями;
  • добавление нового протокола не потребует переписывать половину проекта.

Это и есть целевая архитектурная дисциплина проекта: отдельные слои, отдельные модели, минимально необходимая публичность и отсутствие "универсальных" структур, в которые со временем начинает стекаться вся система.