суббота, 19 сентября 2026 г.

Aspire, Projects, Gateway, http, Deepseek

Aspire, Projects, Gateway, http, Deepseek

https://chat.deepseek.com/share/xlyuiib6qysuf40kd6

Итоги сегодняшней работы: Aspire Trading Gateway

1. Контекст и отправная точка (Что было)

Бизнес-проблема:
Поиск «Золотого Руна» — способа связывать проекты внутри Aspire без выхода во внешние брокеры (RabbitMQ, Kafka, MassTransit). Индустрия навязывает очереди даже для задач уровня «сказать Привет», что ведёт к over-engineering.

Существующее решение (Status Quo):

  • Микросервисы на .NET 10, оркестрация через .NET Aspire 13.4.6.

  • Стандартный стек: ASP.NET Core, Blazor Server, YARP.

  • Отсутствие понимания, как правильно организовать взаимодействие между AddProject-ресурсами.

Критические недостатки старого подхода:

  • Спагетти-архитектура N×N: каждый сервис знает каждого.

  • Нет единой точки входа → сложно с CORS, аутентификацией, rate limiting.

  • Нет декларативного манифеста топологии → архитектура живёт в головах разработчиков, а не в коде.

  • Прямые HTTP-вызовы без Gateway → невозможно отследить сквозные трейсы, диагностика превращается в археологию.

Ограничения среды:

  • Windows Server 2025, Visual Studio, .NET 10.

  • Обучение и понимание — важнее скорости на этом этапе.

2. Концепция и цели (Что было задумано нового)

Формулировка гипотезы:
«Если внутри Aspire использовать центральный Gateway на HTTP + YARP, то можно получить прозрачную, отлаживаемую и наблюдаемую топологию без внешних брокеров».

Ключевые метрики успеха (KPI):

  • Работающая цепочка Web → Gateway → Orders/MarketData с HTTP-вызовами. ✅

  • Сквозные трейсы в Dashboard с единым TraceId. ✅ (проверено в трейсах)

  • Zero Trust через WithReference: у Web нет адресов Orders и MarketData. ✅

  • Dashboard показывает все 4 ресурса с эндпоинтами. ✅

Нереализованные альтернативы:

  • RabbitMQ / MassTransit — отброшены как over-engineering на этом этапе.

  • Wolverine — отложен на Этап 2-3, когда появится реальная боль.

  • gRPC — отложен, HTTP проще для старта и отладки.

  • SignalR как транспорт между сервисами — отброшен как антипаттерн.

Границы проекта (что решили НЕ делать):

  • Не подключать Outbox / Wolverine / брокеры на этом этапе.

  • Не делать синхронизацию состояния между вкладками.

  • Не добавлять аутентификацию / авторизацию.


3. Инновационность и новизна (Почему это Новое — является Новым)

Относительно рынка:
Стандартная практика — либо Full Mesh HTTP (хаос), либо сразу RabbitMQ/Kafka (over-engineering). Промежуточный паттерн «Центральный Gateway + HTTP + Aspire Service Discovery» — недооценён и почти не описан в литературе.

Относительно старой системы:
Закрывается технический долг:

  • Было: разрозненные HttpClient с захардкоженными адресами.

  • Стало: декларативный AppHost/Program.cs как исполняемая конституция топологии.

Интеллектуальная составляющая:

  • Комбинация: Aspire Service Discovery + YARP + WithReference + Blazor Server.

  • Идея: HTTP — это не «просто транспорт», а протокол прикладной адресации, который идеально ложится на философию Aspire.

  • Формулировка принципов:

    • Zero Trust через физическое отсутствие путей.

    • «Код как Конституция» — AppHost = исполняемый манифест.

    • HTTP с большой буквы как «Золотое Руно».

Патентный ландшафт:
Пропускаю (не исследовался).

4. Реализация и результат (Что получилось)

Фактическая архитектура (то, что реально работает):

text
[Browser]
    │
    ▼
[Trading.Web] ──── http://gateway ────▶ [Trading.Gateway]
   (Blazor)                                 (YARP)
                                              │
                              ┌───────────────┴───────────────┐
                              ▼                               ▼
                     [orders-api]                   [market-data-api]
                  /api/orders                    /api/marketdata/{symbol}

Достигнутые показатели:

ПоказательПланФакт
Все 4 ресурса видны в DashboardДа✅ Да
Web → Gateway → Orders работаетДа✅ Да
Web → Gateway → MarketData работаетДа✅ Да
Сквозные трейсыДа✅ Есть
Service Discovery резолвит gatewayДа✅ Да
Zero Trust (нет прямых адресов)Да✅ Да

Положительные побочные эффекты:

  • Поняли, почему appsettings.json не копировался в bin: Microsoft.NET.Sdk vs Microsoft.NET.Sdk.Web. Это классическая ошибка, теперь знаем.

  • Разобрались с @rendermode InteractiveServer — «немая» ошибка Blazor Web App, при которой страница выглядит рабочей, но не является.

  • Научились диагностировать YARP через /debug/routes — быстрый способ увидеть, что реально загружено в IProxyConfigProvider.

Выжившие части старого:
Шаблон Trading.ApiService не используется, но остался в решении. Можно удалить.

Обратная связь:
Ваша цитата: «Да, сейчас кнопки работают. И высвечивают цифры. Помог @rendermode InteractiveServer».

5. Отклонения и неудачи (Что не получилось)

Несовпадение прогноза и реальности:

  • Ожидали, что AddProject + WithReference заработает «из коробки» — на деле вылезло 7 разных ошибок, каждая требовала отдельного разбирательства.

  • YARP требует явного AddServiceDiscoveryDestinationResolver() из отдельного пакета — не очевидно.

Архитектурные компромиссы (Технический долг v2.0):

  • Gateway работает в Production, а не Development — health checks /health и /alive не работают. Не критично, но надо исправить.

  • Gateway слушает только HTTP (порт 5000), без HTTPS. Позже нужно добавить HTTPS для production-сценариев.

  • Trading.Web не имеет явного WithExternalHttpEndpoints(). Пока работает, но лучше указать явно.

Выпавшие функции:

  • Sync-состояние между вкладками (не делали сознательно).

  • Аутентификация / авторизация (не делали).

  • Polly-политики на TradingApiClient (есть через ServiceDefaults, но не настроены параметры).

  • Idempotency-Key для POST /api/orders (не делали).

Неудачные эксперименты:

  • https+http://gateway не работает, если у Gateway нет HTTPS-эндпоинта. Заменили на http://gateway.

  • WeatherApiClient остался от шаблона, но apiservice не зарегистрирован в AppHost — страница Weather падает. Не критично, надо удалить.

Извлечённые уроки:

  1. В Aspire всегда проверяйте Sdk="Microsoft.NET.Sdk.Web" для веб-проектов. Обычный SDK молча не копирует appsettings.json.

  2. YARP маршрут /api/orders/{**catch-all} не матчит /api/orders без слэша. Нужны явные маршруты для базовых путей.

  3. Blazor Web App без @rendermode InteractiveServer выглядит рабочим, но не реагирует на события.

  4. UseAntiforgery() — обязателен для Blazor Server с интерактивным рендерингом.

  5. Диагностика через /debug/routes экономит часы отладки.

6. Экономика и эксплуатация

Пропускаю (не исследовалось, учебный проект).

7. Взгляд в будущее (Куда идём дальше)

Roadmap следующих версий:

Этап 1 — Укрепление HTTP-фундамента:

  • Polly-политики: retry, circuit breaker, timeout на TradingApiClient.

  • Idempotency-Key для POST /api/orders.

  • ProblemDetails (RFC 7807) для ошибок.

  • Версионирование URL: /api/v1/orders.

  • WithEnvironment("ASPNETCORE_ENVIRONMENT", "Development") для Gateway.

Этап 2 — События:

  • Outbox в Gateway (таблица OutboxMessages в PostgreSQL).

  • Фоновый OutboxDispatcher.

  • POST /api/orders возвращает 202 Accepted мгновенно.

Этап 3 — Wolverine (Росомаха):

  • Замена самописного Outbox на Wolverine.

  • Готовые durability, idempotency, correlation, distributed tracing.

Этап 4 — Микропроцессорное ядро:

  • Отдельный Trading.Matching.Engine без HTTP-эндпоинтов.

  • Работа в одном потоке с CPU affinity.

  • LMAX Disruptor-подобные секвенсоры.

Масштабируемость:

  • Текущая архитектура выдержит рост за счёт добавления новых Gateway («Вселенных») и новых сервисов.

  • Множественные Gateway для изоляции bounded contexts — обсуждали, технически возможно.

План вывода из эксплуатации:
Пропускаю (слишком рано).

8. Риски и неопределённости

Уязвимости:

  • Нет аутентификации на Gateway — любой может обратиться к /api/orders.

  • Нет rate limiting — легко положить сервис.

  • HTTPS не настроен на Gateway.

Зависимости:

  • Aspire 13.4.6 — относительно новая версия, API может меняться.

  • YARP 2.3.0 + Microsoft.Extensions.ServiceDiscovery.Yarp — зависимость на конкретную версию.

  • Blazor Server требует WebSocket-соединения, что может быть проблемой за прокси.

Внешние угрозы:

  • Изменения в лицензировании NuGet-пакетов.

  • Изменения в API Aspire между версиями (например, AddServiceDiscoveryDestinationResolver может переехать).

🎯 Ключевые вешки на будущее

  1. Золотое Руно найдено: HTTP с большой буквы — фундамент взаимодействия.

  2. Три опоры: Микропроцессорная архитектура с events + правильные взаимодействия в Aspire + декларативность.

  3. Работающая топология: Web → Gateway → Orders/MarketData.

  4. Проверено: Zero Trust, Service Discovery, YARP-маршрутизация, сквозные трейсы, Blazor Server.

  5. Следующий шаг: Этап 1 — Polly + Idempotency + ProblemDetails + версионирование.


Резюме дня:
Мы не просто запустили пример — мы построили работающий скелет «Управляемой Звезды» и прошли путь от теории (дискуссия об архитектуре) до практики (реально работающие HTTP-вызовы через Gateway с Service Discovery). Все ошибки, которые вылезли, — это классические грабли Aspire, и теперь у вас есть опыт их распознавания.

Фундамент заложен. Можно строить дальше.


Aspire, Gateway, Projects, http, Declarative Federation

Aspire, Gateway, Projects, http,  Declarative Federation

Архитектура системы: Декларативная федерация (Declarative Federation)
Код как конституция (Закон AppHost)
В современной среде .NET Aspire архитектура системы перестает быть просто абстрактной схемой в Visio; она становится исполняемым физическим законом. Проект AppHost берет на себя роль управляющего слоя (Control Plane), который жестко диктует правила взаимодействия в системе.
  • Zero Trust по умолчанию (Нулевое доверие): Микросервисы изначально изолированы. Они физически неспособны обнаружить друг друга в сети или вызвать соседний API, пока им не будет выдано явное разрешение в коде через метод конфигурации .WithReference().
  • Буфер гравитации (The Gravity Buffer): Вся сложность сетевых взаимодействий замыкается на центральном Маршрутизаторе/Шлюзе (например, реализованном на базе библиотеки Wolverine). Он принудительно внедряет топологию «Звезда» (Hub-and-Spoke), где шлюз — это центр, а сервисы — лучи. Это предотвращает превращение системы в «Спагетти-архитектуру» (Spaghetti Mesh), где каждый сервис связан с каждым, порождая неуправляемую паутину из \(N \times N\) соединений.

Синхронная агрегация и асинхронные мутации (Sync Aggregation & Async Mutation)
Центральный маршрутизатор выполняет роль гибридного BFF (Backend-for-Frontend), разделяя потоки данных по принципу CQRS (разделение чтения и записи):
  • Чтение / Запросы (Отображение UI-дашбордов): Для получения данных используется паттерн API Composition (Композиция API). Маршрутизатор выполняет веерную рассылку (fan-out) запросов к внутренним микросервисам через быстрый бинарный протокол gRPC. Данные собираются воедино без лишних затрат ресурсов на сериализацию и десериализацию (в отличие от классического текстового JSON).
  • Запись / Команды (Размещение ордеров): Обработка транзакций построена на базе Wolverine и паттерна Transactional Outbox (Внешний ящик отправки). Пользовательский интерфейс (UI) мгновенно получает ответ 202 Accepted («Принято в обработку»), что обеспечивает молниеносный отклик. В это время маршрутизатор берет на себя гарантированную асинхронную доставку команды во внутренние сервисы (выступая как Федерация сервисов), даже если в этот момент они временно отключены или перегружены.

Структуры данных (Data Structures)
Уровни цен (Price Levels) — Красно-черное дерево (Red-Black Tree)
Биржевой стакан (Order book) требует экстремально быстрых операций: добавления ордеров, их удаления и постоянного доступа к «верхушке стакана» — лучшим ценам покупки и продажи (best bid/ask).
Для этого уровни цен организуются в виде самобалансирующегося бинарного дерева поиска (чаще всего красно-черного), где ключом является Цена, а значением — Очередь ордеров (Price -> Queue).
  • Это дает гарантированную скорость выполнения операций \(O(\log N)\) при поиске или создании новых ценовых уровней.
  • Доступ к самой лучшей цене (минимальной/максимальной в дереве) занимает фиксированное время \(O(1)\).
Очереди ордеров (Order Queues) — Двусвязный список (Doubly Linked List)
Внутри каждого конкретного уровня цены все ордера выстраиваются в двусвязный список. В связке с хэш-таблицей (которая хранит прямые ссылки на узлы списка по ID ордера) это дает феноменальную скорость:
  • Вставка нового ордера в конец очереди занимает \(O(1)\).
  • Удаление ордера из любой части очереди (при отмене пользователем или полном исполнении) занимает \(O(1)\), так как двусвязный список позволяет мгновенно «перебросить» указатели соседних элементов без перебора всей структуры.

Алгоритмы мэтчинга / сведения ордеров (Matching Algorithms)
FIFO (First In, First Out — Приоритет цены и времени)
Это классический стандарт для спотовых (предметных) бирж. Если несколько ордеров находятся на одном ценовом уровне, они будут исполнены строго в том хронологическом порядке, в котором они поступили в систему. Такой подход поощряет маркетмейкеров, которые предоставляют ликвидность на ранних этапах.
  • Контекст: На уровне цены $50 стоят два лимитных ордера: [Ордер А: объем 10] \(\rightarrow \) [Ордер B: объем 5].
  • Входящий запрос: Рыночный ордер на покупку объема 12 по цене $50.
  • Результат сведения: Ордер А исполняется полностью (забирает 10), ордер B исполняется частично (забирает оставшиеся 2), в очереди ордера B остается объем 3.
Pro-Rata (Пропорциональное распределение)
Данный алгоритм широко применяется на рынках деривативов (фьючерсов и опционов). Он полностью игнорирует время поступления заявки. Входящий объем распределяется между всеми ордерами на уровне пропорционально их размеру. Это стимулирует крупных игроков выставлять глубокую ликвидность (ордера больших объемов).
  • Контекст: На уровне цены $50 стоят два ордера разного объема: [Ордер А: объем 100] (составляет 80% от общего объема уровня) и [Ордер B: объем 25] (составляет 20%).
  • Входящий запрос: Рыночный ордер на покупку объема 10 по цене $50.
  • Результат сведения: Время выставления не важно. Объемы распределяются согласно долям: Ордер А исполняется на 8 (80% от 10), а Ордер B исполняется на 2 (20% от 10).

Очереди сообщений и конкурентность (Message Queues & Concurrency)
Высокопроизводительное ядро мэтчинга (matching engine) в продакшене работает строго в один поток (Single Thread). Это позволяет полностью избавиться от накладных расходов на межпоточные блокировки (locks, mutexes) и исключает возникновение состояний гонки (race conditions). При этом данный поток жестко привязывается к выделенному изолированному ядру процессора операционной системы (CPU affinity), чтобы исключить задержки на переключение контекста.
  • Входной поток (Ingress): API-шлюзы не отправляют ордера напрямую в движок. Они сбрасывают их в ультра-низколатентный последовательный конвейер — секвенсор (например, LMAX Disruptor или Aeron). Задача секвенсора — присвоить каждому входящему пакету строгий, детерминированный порядковый номер перед тем, как отдать его на обработку однопоточному движку.
  • Выходной поток (Egress): Как только движок совершил сведение (мэтчинг), он не тратит время на запись в базу или отправку клиентам. События сделок и обновления стаканов асинхронно выбрасываются в сеть через UDP Multicast (или брокер Kafka). Эту информацию подхватывают дистрибьюторы рыночных данных (Market Data) и клиринговые сервисы (расчетные палаты), а критически важное ядро мэтчинга мгновенно освобождается для обработки следующего ордера.