본문으로 건너뛰기

아키텍처 개요

cMind는 .NET 10 / C# 14를 기반으로 한 cTrader용 멀티테넌트 Blazor Server + Minimal API 플랫폼으로, EF Core + PostgreSQL, .NET Aspire, MCP 서버 및 AI 코어를 함께 사용합니다. 엄격한 도메인 주도 설계(DDD) 를 따릅니다: 비즈니스 규칙은 순수 Core의 애그리게이트 및 값 객체에 있고, 다른 모든 것은 오케스트레이션합니다.

이 페이지가 맵입니다. 특정 선택이 왜 이루어졌는지 자세히 알려면, 아키텍처 결정 기록을 참조하세요.

모듈​

프로젝트책임
src/Core순수 도메인 — 엔티티, 애그리게이트, 값 객체, 강한 ID, 도메인 이벤트, Core 측 인터페이스. 0개의 인프라 의존성(EF/HttpClient/Docker/ASP.NET 없음).
src/InfrastructureEF Core + PostgreSQL, DataProtection 암호화, GHCR 클라이언트, Anthropic AI 클라이언트, 관찰성.
src/Nodes교차 노드 오케스트레이션 — 스케줄링, 디스패치, 폴러, 백그라운드 서비스.
src/CtraderCliNode원격 호스트의 독립형 HTTP 노드 에이전트(JWT 인증, 셸 없음). cTrader CLI를 docker 컨테이너 내에서 구동하여 cBot을 실행 및 백테스트하고, cTrader CLI가 지원하면 최적화도 할 예정.
src/CopyEngine복사 거래 호스트: 소스 계정에서 대상으로 거래를 미러링.
src/CTraderOpenApicTrader Open API 클라이언트(protobuf over TCP/SSL) — 인증, 거래 세션, 자산.
src/WebBlazor Server SSR + Minimal API + SignalR + MudBlazor UI.
src/McpAI 클라이언트에 도구를 노출하는 MCP HTTP+SSE 서버.
src/AppHost.NET Aspire 오케스트레이터(Postgres, Web, MCP, pgAdmin).

전체 그림​

요청 흐름​

빌드 & 백테스트​

  1. 사용자가 cBot 소스 프로젝트를 제출합니다. CBotBuilder는 웹 호스트에서 실행되며(Docker 소켓이 필요합니다) /work가 bind-mounted된 일회용 SDK 컨테이너 내에서 실행되고 app-nuget-cache 볼륨을 공유하므로 신뢰할 수 없는 MSBuild는 호스트 파일 시스템이나 네트워크에 접근할 수 없습니다.
  2. 실행/백테스트 컨테이너는 NodeScheduler가 선택한 노드에서 실행되며, ContainerDispatcherFactory를 통해 디스패치됩니다 → Http(원격 CtraderCliNode 에이전트) 또는 Local(웹 호스트의 자체 노드).
  3. 컨테이너는 ghcr.io/spotware/ctrader-console에서 --exit-on-stop과 함께 실행됩니다. 폴러(RunCompletionPoller, BacktestCompletionPoller)가 자체 종료 컨테이너를 조정합니다: exit 0/null ⇒ Stopped, 0 이외 ⇒ Failed.

인스턴스 상태는 TPH이며, 전환이 엔티티를 교체합니다(판별식은 변경될 수 없음), 따라서 인스턴스 id는 변경됩니다 starting → running → terminal. 컨테이너 id는 안정적이며 이월됩니다; HTTP 에이전트는 상태/보고서/중지/로그에 대해 컨테이너 id로 키가 지정됩니다.

cTrader CLI 노드​

cTrader CLI 노드는 SSH나 셸을 갖지 않습니다. 메인 앱은 각 에이전트와 HTTP를 통해 통신합니다; 모든 요청은 해당 노드의 비밀로 서명된 단기 HS256 JWT(5분, iss=app-main / aud=app-node)를 전달합니다. 에이전트는 AllowedImagePrefix와 일치하는 이미지만 실행하며, ArgumentList를 통해 docker을 실행하고(셸 없음), 상태 비저장입니다(app.instance 레이블로 컨테이너를 찾습니다). 에이전트는 자체 등록되고 POST /api/nodes/register로 하트비트합니다; 메인 앱은 CtraderCliNode를 이름으로 upserts하므로 IP 변경 시에도 유지됩니다.

복사 거래​

CopyEngineSupervisor(하나의 BackgroundService)는 실행 중인 복사 프로필을 실시간 CopyEngineHost 인스턴스와 조정합니다 — 원자적 DB 리스를 통해 프로필을 청구합니다(두 노드가 중복 복사하지 않음), 리스를 갱신하고, 죽은 호스트를 재시작합니다. 각 CopyEngineHost는 cTrader Open API에 연결되고, 순수 CopyDecisionEngine(방향/레이턴시/슬리피지 필터 + 사이징)을 통해 소스 실행을 대상으로 미러링하며, resync + partial-fill true-up을 통해 자체 치유됩니다.

AI​

AI는 AppOptions.Ai.ApiKey에 대해 완전히 게이트됩니다 — 미설정 ⇒ 모든 기능이 AiResult.Fail을 반환하고 앱은 변경되지 않습니다(빌드/테스트/E2E에는 키가 필요하지 않음). IAiClient는 raw HTTP(typed HttpClient)를 통해 Anthropic을 호출하며, 의도적으로 SDK를 사용하지 않습니다. AiFeatureService는 Web 엔드포인트, MCP AiTools, AiRiskGuard를 통해 공유되는 단일 오케스트레이터입니다.

교차 절단 규칙​

  • 하나의 SaveChanges는 하나의 애그리게이트를 변경합니다. 교차 애그리게이트 흐름은 EF 인터셉터에 의해 디스패치된 도메인 이벤트를 사용합니다.
  • 애그리게이트는 강한 ID로 서로를 참조합니다, 네비게이션 프로퍼티가 아닙니다.
  • 앰비언트 클록이 없습니다. 코드는 TimeProvider를 주입합니다; 도메인 메서드는 DateTimeOffset now를 취합니다.
  • 비밀은 ISecretProtector(EncryptionPurposes)를 통해 암호화됩니다; 문자열은 Core/Constants/에 있습니다; 로그는 소스 생성 LogMessages를 통해 진행됩니다.

이들은 CI에서 시행됩니다: 분석기 스윕, zero-warning 빌드, ArchitectureGuardTests(앰비언트 클록 읽기, Core 인프라 의존성, 또는 직접 ILogger.Log* 호출 시 빌드 실패).