Navigazione docs

Architettura

cloudrift segue un’architettura DDD (Domain-Driven Design) con il pattern Ports & Adapters (Hexagonal Architecture). Le dipendenze puntano sempre verso l’interno, dalla CLI attraverso il layer applicativo fino al domain.

Struttura del monorepo

cloudrift/
├── apps/
│   └── cli/                        # Entry point CLI (Commander.js)
├── libs/
│   └── cloud-cost/
│       ├── domain/                 # Entità, ports, policies
│       ├── application/            # Use cases (AnalyzeCloudWasteUseCase)
│       └── infrastructure/
│           └── aws-adapter/        # Implementazione scanner AWS SDK v3
├── packages/                       # Shared kernel, utilities
├── docs/                           # Documentazione tecnica (EN + IT)
├── policy/                         # Policy OPA di esempio
├── nx.json
├── pnpm-workspace.yaml
└── tsconfig.base.json

Layer

Domain (libs/cloud-cost/domain)

Contiene la logica di business pura, senza dipendenze esterne:

  • Entità: WastedResource e le sue specializzazioni per ogni tipo di risorsa
  • Ports (interfacce): WasteScannerPort, PricingPort
  • Policies: waste policies con grace period e tag di esclusione (base class riusabile)
  • Value Objects: ResourceKind union type (il compilatore guida quando aggiungi un nuovo tipo)

Application (libs/cloud-cost/application)

  • AnalyzeCloudWasteUseCase — il coordinatore. Generico sugli scanner registrati: itera i WasteScannerPort, aggrega i risultati, applica le policy.

Infrastructure (libs/cloud-cost/infrastructure/aws-adapter)

  • Scanner: un’implementazione di WasteScannerPort per ogni tipo di risorsa (EBS, EC2, RDS, ecc.)
  • Usa AWS SDK v3 per le chiamate API e CloudWatch per le metriche di attività

CLI (apps/cli)

  • Parsing argomenti con Commander.js
  • Formatters: table, JSON, markdown, PDF
  • Composition root: registra gli scanner e istanzia il use case
  • Exit codes: 0 (ok), 2 (budget superato)

Perché questa architettura

  • Testabilità: il domain non dipende da AWS SDK, si testa con mock
  • Estensibilità: nuovi servizi = nuovi scanner, zero modifiche al use case
  • Multi-cloud path: per supportare GCP/Azure, basta aggiungere nuovi adapter che implementano WasteScannerPort
  • Separation of concerns: ogni layer ha responsabilità chiare e ben definite

Bounded context

Il progetto ha quattro bounded context, ciascuno con la stessa struttura libs/<context>/{domain,application,infrastructure}:

Contesto Comando Cosa trova
cloud-cost analyze Risorse sprecate con stima dei costi (44 scanner)
cost-analytics cost, trend Confronto e trend di spesa via Cost Explorer
dead-resources dead-resources Risorse morte/inutilizzate a costo $0 (18 check)
resource-security resource-security Configurazioni di sicurezza a rischio (29 check)

Condividono solo shared/kernel. L’isolamento dei layer (domain → application → infrastructure) è imposto da @nx/enforce-module-boundaries — non solo per convenzione.

Server MCP

cloudrift mcp è il secondo input adapter accanto a Commander. Espone gli stessi use case come sette tool JSON-RPC su stdio, così un agente AI (Claude Code, Kiro, VS Code Copilot Chat) può chiamare direttamente analyze_cloudrift o le versioni per-dominio più mirate (analyze_cloud_waste, analyze_dead_resources, analyze_resource_security, get_cost_trend), oltre a get_resource_types e get_required_iam_permissions. Nessun nuovo concetto di dominio — puramente un adapter di protocollo che riusa le composition root esistenti (resolveMcpScope() estrae il preambolo config/regione/account condiviso da tutti e quattro i tool di analisi).

Trend store locale

Ogni esecuzione di analyze, dead-resources e resource-security salva uno snapshot completo in un file SQLite per-account (~/.cloudrift/trends/<account-id>.db). Il comando history lo rilegge. Lo store vive in un package condiviso (shared-trend-store) che non conosce i DTO specifici di dominio — memorizza solo { domain, generatedAt, payload }. La scrittura è best-effort: un fallimento non blocca mai la scansione stessa.