Architecture
Introduction
This document describes the layered architecture of Ravenxcope.Backend, the dependency direction between layers, the runtime initialization flow, and important design decisions that shape the codebase. It also highlights the fresh-deployment sensor architecture where a physical Sensor enrolls once through a host agent and each VirtualSensor is deployed independently onto that host.
Layering Approach
The backend is organized into distinct layers, each with a clear responsibility:
| Layer | Directory | Responsibility |
|---|---|---|
| API | API/Controllers/ | Thin HTTP/gRPC endpoint handlers, request routing, auth annotations |
| Application | Application/DTOs/ | Request/response payload definitions, API envelope |
| Domain | Domain/Entities/ | Core business entities persisted in PostgreSQL, including physical sensors, virtual sensors, and structured sensor interface inventory |
| Infrastructure | Infrastructure/ | Services, repositories, DbContext, external integrations |
| Common | Common/Helpers/ | Shared utilities used across all layers |
| Extensions | Extensions/ | Startup composition (DI, middleware, migration, validation) |
Dependency Direction
Current dependency rules:
- API depends on Application (DTOs), and Infrastructure (service interfaces). Controllers should delegate to services and avoid direct persistence access.
- Infrastructure depends on Domain (entities) and external SDKs (EF Core, Redis, InfluxDB, OpenSearch)
- Domain has no outward dependencies — pure entity definitions
- Common can be used by all layers — shared helpers
- Program.cs and Extensions orchestrate startup only
Note: All feature controllers now delegate to dedicated service interfaces (e.g.,
IAuthService,ISensorsService,IAssetService). All entities have corresponding repository abstractions underInfrastructure/Repositories/.
Runtime Architecture
The application progresses through three distinct phases during startup:
Phase 1: Builder (Service Registration)
- Configure Logging: Initialize Serilog logging from appsettings
- Validate Configuration: Ensure all required configuration keys are present
- Security Check: Emit JWT secret length warning if
< 32 characters - Log Settings: Log auto-migration configurations
- Bind Typed Options: Map configuration sections to strongly-typed classes
- Register Controllers: Configure API controllers with case-insensitive JSON
- Register API Docs: Add EndpointsApiExplorer and Swagger
- Register gRPC: Add gRPC services
- Rate Limiting: Set
auth-login(5/min) andauth-register(3/min) - Database: Configure PostgreSQL DbContext with Npgsql
- Caching: Connect to Redis (eager connection)
- Singleton Services: Register
JwtService,InfluxDbService,OpenSearchService,RedisService,ResilientHttpService - Scoped Repositories: Register all entity repositories (e.g.,
UserRepository,SensorRepository) - Scoped Services: Register all business logic services (e.g.,
AuthService,SensorsService) - HTTP Client: Register generic HttpClient factory
- Authentication: Configure JWT Bearer auth with token blacklist check
- Authorization: Configure role/permission-based authorization policies
- CORS: Register CORS policy
Phase 2: Health Checks & Migrations
- Check PostgreSQL: Run connectivity check with retry mechanism
- Check Redis: Run PING check with retry mechanism
- Check InfluxDB: Run
/healthendpoint check with retry mechanism - Check OpenSearch: Run root endpoint check with retry mechanism
- Fail-Fast: Halt startup immediately if any check fails
- Apply Migrations: Execute EF Core database migrations (if
AutoMigrate = true)
Phase 3: Middleware Pipeline
- Request Logging: Serilog HTTP request logging
- Exception Handling: Global exception handler (
ApiExceptionMapper) - Swagger UI: Serve API documentation
- CORS: Apply Cross-Origin Resource Sharing
- HTTPS Redirection: Force secure connections
- Rate Limiting: Apply endpoint limits
- Authentication: Validate JWT tokens
- Authorization: Enforce access policies
- Map Controllers: Route HTTP requests to endpoints
- Map gRPC: Route
SensorHealthcheckService
Extension Method Architecture
Each startup responsibility is isolated in a dedicated extension method:
| Extension Class | Method | Purpose |
|---|---|---|
ServiceCollectionExtensions | AddBackendServicesAsync() | All DI registrations (services, repos, DB, auth, rate limiting) |
ConfigurationValidationExtensions | ValidateRequiredConfiguration() | Startup config key validation |
StartupDependencyHealthChecksExtensions | RunStartupDependencyHealthChecksAsync() | PostgreSQL, Redis, InfluxDB, OpenSearch checks |
DatabaseMigrationExtensions | ApplyDatabaseMigrationsAsync() | Auto-migration with retry logic |
WebApplicationExtensions | UseBackendPipeline() | Middleware pipeline composition |
GlobalExceptionHandlingExtensions | UseGlobalExceptionHandling() | Global exception handler registration |
CorsExtensions | AddCorsPolicy() | CORS policy registration |
This gives Program.cs a clean, declarative bootstrap sequence:
var builder = WebApplication.CreateBuilder(args);
// ... logging, validation, warnings ...
await builder.Services.AddBackendServicesAsync(builder.Configuration);
var app = builder.Build();
await app.RunStartupDependencyHealthChecksAsync();
await app.ApplyDatabaseMigrationsAsync();
app.UseBackendPipeline();
await app.RunAsync();
Important Design Decisions
-
Service-first controller design — All controllers delegate business logic to dedicated service interfaces (e.g.,
IAuthService,ISensorsService,IAssetService). Controllers contain no business logic, only HTTP transport concerns. -
Repository pattern for all entities — Every entity has a corresponding repository abstraction (
IRepository<T>base + feature-specific interfaces). Services depend on repository interfaces, notApplicationDbContextdirectly. -
Singleton infrastructure services —
JwtService,InfluxDbService,OpenSearchService,RedisService, andResilientHttpServiceare registered as singletons because they manage their own connection state and are thread-safe. -
Eager Redis connection — Redis
ConnectionMultiplexeris connected during service registration (AddBackendServicesAsync), not lazily. This means a Redis failure blocks DI setup. -
Fail-fast startup philosophy — The service deliberately crashes on startup for any configuration or dependency failure, rather than starting in a degraded state.
-
Typed options over ad-hoc reads — All configuration sections are bound to strongly-typed option classes (defined in
BackendConfiguration.cs), avoiding scatteredconfiguration["key"]calls. -
Composite runtime options —
SensorRuntimeOptionsaggregates settings from multiple configuration sections into a single options class for convenience in sensor-related operations. -
Standardized API envelope — All endpoints return
ApiResponse<T>viaApiEnvelope.Success()andApiEnvelope.Error(), providing a consistent{ success, data, errors, traceId }response contract. -
Global exception handling —
GlobalExceptionHandlingExtensionscatches unhandled exceptions and maps them viaApiExceptionMapperto appropriate HTTP status codes, eliminating manual try-catch patterns. -
Resilient external calls —
ResilientHttpServicewraps outbound HTTP calls with retry (3 attempts with backoff) and circuit breaker (opens after 3 failures, 30s cooldown) patterns. -
Rate limiting on auth endpoints — Login (5 req/min) and register (3 req/min) are protected by ASP.NET Core fixed-window rate limiters.
-
Host-agent-first sensor model — A physical
Sensoris enrolled once through a one-time install token and a durable agent token. That host agent reports heartbeat, host metrics, and discovered network interfaces, whileVirtualSensorrecords only workload-level deployment state and assigned capture interfaces. -
Structured interface inventory — The backend persists sensor interfaces as first-class rows (
SensorNetworkInterface) rather than runtime JSON blobs. This allows backend-side eligibility validation and exclusive interface assignment to virtual sensors. -
Separated telemetry measurements — InfluxDB stores host presence in
sensor_statusand host metrics/interface counters insensor_metrics, so online state is derived from heartbeat data rather than inferred from CPU series.
Known Architecture Debt
| Area | Issue | Status | Impact |
|---|---|---|---|
| Test coverage breadth | Coverage is improving, but many services/controllers still lack deeper integration and contract tests | Open | Regression risk remains for less-tested flows |
| Analytics graceful fallback | Graceful fallback is now applied across analytics endpoints with stale-cache fallback patterns; response metadata contracts may still vary by endpoint shape | Partial | Consumers should still handle endpoint-specific fallback payload nuances |
| Analytics precompute/warming | Background cache warming exists for key dashboard queries, but coverage and schedule tuning are still basic | Partial | Remaining cold-start spikes possible on non-warmed query shapes |
Resolved debt items (previously open):
- Audit logging in
RoleService,UserService, andPermissionService. - Analytics response caching for high-frequency endpoints.
- Hosted analytics cache warming service for key dashboard paths.