Skip to main content

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 RuleDescription
Ampra.CoreZero dependencies. Pure domain layer containing entities, constants, enums, and configuration POCOs.
Ampra.ApplicationDepends only on Ampra.Core. Defines service interfaces and DTOs without implementation knowledge.
Ampra.InfrastructureDepends on Ampra.Core and Ampra.Application. Implements all service interfaces with concrete database and API integrations.
Ampra.WebDepends on all .NET modules. Serves as the composition root — wires DI, configures middleware, exposes controllers.
Ampra.MQTTDepends 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:

RolePermissions
OverseerFull platform control. Can manage all users, assign any role, access debug tools. Cannot be deleted by other roles.
AdminUser management (non-Overseer), debug data generation, process simulation. Cannot assign Overseer role.
UserStandard access to own resources — sun sources, predictions, ROI, exports, sharing.

Authorization Policies

PolicyGrants Access To
OmniAll authenticated users (Overseer, Admin, User)
Roles: Admin, OverseerAdministrative 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

SourceTargetProtocolPurpose
Ampra.UIAmpra.WebHTTPS (REST)All user interactions, data queries, CRUD operations
IoT DevicesEMQXMQTT v5Real-time telemetry publishing
EMQXAmpra.WebHTTP (internal)Authentication and ACL validation callbacks
Ampra.MQTTEMQXMQTT v5Telemetry subscription and consumption
Ampra.MQTTMongoDBNative driverNormalized data storage
Ampra.MQTTPostgreSQLNpgsqlSource ownership validation
Ampra.WebAmpra.MLHTTP (internal)Training and prediction job dispatch
Ampra.MLMongoDBPyMongoTraining data retrieval, prediction storage
Ampra.MLRedisredis-pyJob status tracking
Ampra.MLMinIOS3 APIModel artifact storage and retrieval
Ampra.WebOpen-MeteoHTTPSWeather forecast retrieval
Ampra.WebSendGridHTTPSTransactional email delivery

Design Decisions

Why Dual Databases (PostgreSQL + MongoDB)?

ConcernPostgreSQLMongoDB
Data ModelRelational entities with strict schemasFlexible time-series documents with embedded arrays
Use CaseUsers, sources, pricing, weather, settingsTelemetry (30+ metrics per point), predictions, model metadata
Query PatternJOINs, transactions, referential integrityAggregation pipelines, time-range scans, $group/$project
VolumeThousands of recordsMillions 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.