TradingAgents/docs/architecture/application-boundary.md

8.1 KiB

TradingAgents architecture convergence draft: application boundary

Status: draft Audience: backend/dashboard/orchestrator maintainers Scope: define the boundary between HTTP/WebSocket delivery, application service orchestration, and the quant+LLM merge kernel

Current status snapshot (2026-04)

This document is still the target boundary document, but several convergence pieces are already landed on the mainline:

  • web_dashboard/backend/services/job_service.py now owns public task/job projection logic;
  • web_dashboard/backend/services/result_store.py persists result contracts under results/<task_id>/result.v1alpha1.json;
  • web_dashboard/backend/services/analysis_service.py and api/portfolio.py already expose contract-first result payloads by default;
  • task lifecycle query/command routing for status/list/cancel now sits behind backend task services instead of route-local orchestration in main.py;
  • /ws/analysis/{task_id} and /ws/orchestrator already carry contract_version = "v1alpha1" and include result/degradation/data-quality metadata.

What is not fully finished yet:

  • web_dashboard/backend/main.py still contains too much orchestration glue and transport-local logic outside the task lifecycle slice;
  • route handlers are thinner than before, but the application layer has not fully absorbed reports/export and every remaining lifecycle branch;
  • migration flags/modes still coexist with legacy compatibility paths.

1. Why this document exists

The current backend mixes three concerns inside web_dashboard/backend/main.py:

  1. transport concerns: FastAPI routes, headers, WebSocket sessions, task persistence;
  2. application orchestration: task lifecycle, stage progress, subprocess wiring, result projection;
  3. domain execution: TradingOrchestrator, LiveMode, quant+LLM signal merge.

For architecture convergence, these concerns should be separated so that:

  • the application service remains a no-strategy orchestration and contract layer;
  • orchestrator/ remains the quant+LLM merge kernel;
  • transport adapters can migrate without re-embedding business rules.

2. Current evidence in repo

2.1 Merge kernel already exists

  • orchestrator/orchestrator.py owns quant runner + LLM runner composition.
  • orchestrator/signals.py owns Signal, FinalSignal, and merge math.
  • orchestrator/live_mode.py owns batch live execution against the orchestrator.

This is the correct place for quant/LLM merge semantics.

2.2 Backend currently crosses the boundary

web_dashboard/backend/main.py currently also owns:

  • analysis subprocess template creation;
  • stage-to-progress mapping;
  • conversion from FinalSignal to UI-oriented fields such as decision, quant_signal, llm_signal, confidence;
  • report materialization into results/<ticker>/<date>/complete_report.md.

This makes the transport layer hard to replace and makes result contracts implicit.

At the same time, current mainline no longer matches the oldest “all logic sits in routes” description exactly. The codebase now sits in a mid-migration state:

  • merge semantics remain in orchestrator/;
  • public payload shaping has started moving into backend services;
  • task lifecycle query/command paths now route through backend task services;
  • legacy compatibility fields still exist for UI safety.

3. Target boundary

3.1 Layer model

Transport adapters

Examples:

  • FastAPI REST routes
  • FastAPI WebSocket endpoints
  • future CLI/Tauri/worker adapters

Responsibilities:

  • request parsing and auth
  • response serialization
  • websocket connection management
  • mapping application errors to HTTP/WebSocket status

Non-responsibilities:

  • no strategy logic
  • no quant/LLM weighting logic
  • no task-stage business rules beyond rendering application events

Application service

Suggested responsibility set:

  • accept typed command/query inputs from transport
  • orchestrate analysis execution lifecycle
  • map domain results into stable result contracts
  • own task ids, progress events, persistence coordination, and rollback-safe migration switches
  • decide which backend implementation to call during migration

Non-responsibilities:

  • no rating-to-signal research logic
  • no quant/LLM merge math
  • no provider-specific data acquisition details

Domain kernel

Examples:

  • TradingOrchestrator
  • SignalMerger
  • QuantRunner
  • LLMRunner
  • TradingAgentsGraph

Responsibilities:

  • produce quant signal, LLM signal, merged signal
  • expose domain-native dataclasses and metadata
  • degrade gracefully when one lane fails

3.2 Canonical dependency direction

transport adapter -> application service -> domain kernel
transport adapter -> application service -> persistence adapter
application service -> result contract mapper

Forbidden direction:

transport adapter -> domain kernel + ad hoc mapping + ad hoc persistence

4. Proposed application-service interface

The application service should expose typed use cases instead of letting routes assemble logic inline.

4.1 Commands / queries

Suggested surface:

  • start_analysis(request) -> AnalysisTaskAccepted
  • get_analysis_status(task_id) -> AnalysisTaskStatus
  • cancel_analysis(task_id) -> AnalysisTaskStatus
  • run_live_signals(request) -> LiveSignalBatch
  • list_analysis_tasks() -> AnalysisTaskList
  • get_report(ticker, date) -> HistoricalReport

4.2 Domain input boundary

Inputs from transport should already be normalized into application DTOs:

  • ticker
  • trade date
  • auth context
  • provider/config selection
  • execution mode

The application service may choose subprocess/backend/orchestrator execution strategy, but it must not redefine domain semantics.

5. Boundary rules for convergence work

Rule A: result mapping happens once

Current code maps FinalSignal to dashboard fields inside the analysis subprocess template. That mapping should move behind a single application mapper so REST, WebSocket, export, and persisted task status share one contract.

Rule B: stage model belongs to application layer

Stage names such as analysts, research, trading, risk, portfolio are delivery/progress concepts, not merge-kernel concepts. Keep them outside orchestrator/.

Rule C: orchestrator stays contract-light

orchestrator/ should continue returning Signal / FinalSignal and domain metadata. It should not learn about HTTP status, WebSocket payloads, pagination, or UI labels beyond domain rating semantics already present.

Rule D: transport only renders contracts

Routes should call the application service and return the already-shaped DTO/contract. They should not reconstruct decision, quant_signal, llm_signal, or progress math themselves.

6. Suggested module split

One viable split:

web_dashboard/backend/
  application/
    analysis_service.py
    live_signal_service.py
    report_service.py
    contracts.py
    mappers.py
  infra/
    task_store.py
    subprocess_runner.py
    report_store.py
  api/
    fastapi_routes remain thin

This keeps convergence local to backend/application without moving merge logic out of orchestrator/.

7. Non-goals

  • Do not move signal merge math into the application service.
  • Do not turn the application service into a strategy engine.
  • Do not require frontend-specific field naming inside orchestrator/.
  • Do not block migration on a full rewrite of existing routes.

8. Review checklist

A change respects this boundary if all are true:

  • route handlers mainly validate/auth/call service/return contract;
  • application service owns task lifecycle and contract mapping;
  • orchestrator/ remains the only owner of merge semantics;
  • domain dataclasses can still be tested without FastAPI or WebSocket context.

9. Current maintainer guidance

When touching backend convergence code, treat these files as the current application-facing boundary:

  • web_dashboard/backend/services/job_service.py
  • web_dashboard/backend/services/result_store.py
  • web_dashboard/backend/services/analysis_service.py
  • web_dashboard/backend/api/portfolio.py

If a change adds or removes externally visible fields, update docs/contracts/result-contract-v1alpha1.md in the same change set.