Architecture Overview
Ampra employs a layered clean architecture with clear dependency boundaries. The system is composed of seven modules that communicate through well-defined interfaces, enabling independent development, testing, and deployment of each component.
High-Level System Diagram
Module Dependency Graph
The dependency flow follows the Dependency Inversion Principle — higher-level modules never depend on lower-level implementation details:
| Dependency Rule | Description |
|---|---|
Ampra.Core | Zero dependencies. Pure domain layer containing entities, constants, enums, and configuration POCOs. |
Ampra.Application | Depends only on Ampra.Core. Defines service interfaces and DTOs without implementation knowledge. |
Ampra.Infrastructure | Depends on Ampra.Core and Ampra.Application. Implements all service interfaces with concrete database and API integrations. |
Ampra.Web | Depends on all .NET modules. Serves as the composition root — wires DI, configures middleware, exposes controllers. |
Ampra.MQTT | Depends on Ampra.Core only. Operates as an independent worker with its own MongoDB and PostgreSQL connections. |
Data Flow Patterns
1. Real-Time Telemetry Ingestion (MQTT)
2. Real-Time Telemetry Ingestion (Webhook)
3. ML Prediction Pipeline
4. Scheduled Background Jobs
Authentication & Authorization Model
Ampra uses cookie-based authentication with ASP.NET Identity, supporting three hierarchical roles:
| Role | Permissions |
|---|---|
| Overseer | Full platform control. Can manage all users, assign any role, access debug tools. Cannot be deleted by other roles. |
| Admin | User management (non-Overseer), debug data generation, process simulation. Cannot assign Overseer role. |
| User | Standard access to own resources — sun sources, predictions, ROI, exports, sharing. |
Authorization Policies
| Policy | Grants Access To |
|---|---|
Omni | All authenticated users (Overseer, Admin, User) |
Roles: Admin, Overseer | Administrative endpoints only |
[Authorize] | Any authenticated user |
[AllowAnonymous] | Public sharing endpoints |
Ownership Enforcement
Every data-accessing operation validates that the requesting user owns the target resource through IOwnershipService. This multi-tenant isolation ensures users can never access, modify, or delete another user's data.
Inter-Service Communication
| Source | Target | Protocol | Purpose |
|---|---|---|---|
| Ampra.UI | Ampra.Web | HTTPS (REST) | All user interactions, data queries, CRUD operations |
| IoT Devices | EMQX | MQTT v5 | Real-time telemetry publishing |
| EMQX | Ampra.Web | HTTP (internal) | Authentication and ACL validation callbacks |
| Ampra.MQTT | EMQX | MQTT v5 | Telemetry subscription and consumption |
| Ampra.MQTT | MongoDB | Native driver | Normalized data storage |
| Ampra.MQTT | PostgreSQL | Npgsql | Source ownership validation |
| Ampra.Web | Ampra.ML | HTTP (internal) | Training and prediction job dispatch |
| Ampra.ML | MongoDB | PyMongo | Training data retrieval, prediction storage |
| Ampra.ML | Redis | redis-py | Job status tracking |
| Ampra.ML | MinIO | S3 API | Model artifact storage and retrieval |
| Ampra.Web | Open-Meteo | HTTPS | Weather forecast retrieval |
| Ampra.Web | SendGrid | HTTPS | Transactional email delivery |
Design Decisions
Why Dual Databases (PostgreSQL + MongoDB)?
| Concern | PostgreSQL | MongoDB |
|---|---|---|
| Data Model | Relational entities with strict schemas | Flexible time-series documents with embedded arrays |
| Use Case | Users, sources, pricing, weather, settings | Telemetry (30+ metrics per point), predictions, model metadata |
| Query Pattern | JOINs, transactions, referential integrity | Aggregation pipelines, time-range scans, $group/$project |
| Volume | Thousands of records | Millions of time-series data points |
Why EMQX over Mosquitto?
EMQX provides enterprise-grade features: HTTP authentication callbacks (integrated with the Ampra API), native clustering support, a built-in management dashboard, and superior throughput for high-frequency IoT telemetry.
Why XGBoost over Deep Learning?
The prediction engine uses gradient-boosted trees (XGBoost) because they perform well on tabular/time-series data without requiring GPU infrastructure; they train in seconds rather than hours; and they provide feature importance metrics for model explainability. The physics-aware blending approach (60% ML + 40% physics model) further ensures predictions remain physically plausible even with limited training data.