GridLab.Gmss
GridLab.GMSS
Important
Personal Research & Access Notice
This project is a personal research and technical capability development initiative conducted independently by the author.
- Independent Initiative: This solution has been developed entirely during personal time. All development overhead, infrastructure costs, and computational resources are personally provided and funded by the author, without utilizing any Siemens assets, hardware, or corporate budget.
- Exclusive Benefit: While this is a private endeavor, public access is restricted to ensure that the strategic advantages of this research primarily benefit Siemens and its internal community, preventing unauthorized commercial exploitation by third-party entities.
- Siemens Employees: You can access the full source code and contribute via the internal mirror at code.siemens.com.
- Inquiries: If you are a partner or an external entity seeking collaboration, please provide your contact information and business case to raif.durmaz@siemens.com.
Microservices Solution
This is a distributed microservices application built on Domain Driven Design (DDD) practices, orchestrated by .NET Aspire.
Pre-requirements
- Visual Studio 2026 (v18.2+) or another suitable IDE
- .NET 10.0+ SDK
- Node v18 or v20
- Docker Desktop v4.0+
All infrastructure dependencies (PostgreSQL, Valkey, RabbitMQ, Elasticsearch, Prometheus, Grafana, Jaeger, etc.) are containerized and orchestrated via .NET Aspire—no external installation required.
Quick Start
TL;DR — Start Everything
cd aspire\GridLab.Gmss.AppHost
dotnet runThat's it! The Aspire AppHost will:
✅ Start all infrastructure containers (PostgreSQL, Valkey, RabbitMQ, Elasticsearch, etc.)✅ Start observability stack (Prometheus, Grafana, Jaeger, Kibana)✅ Start microservices in the correct dependency order✅ Automatically run database migrations and seed initial data✅ Wait for each service's health check before starting dependents✅ Open the Aspire Dashboard
Default Credentials
After startup, log in with:
- Username:
platon - Password:
1q2w3E*
Architecture Overview
System Diagram
Solution Structure
GridLab.Gmss/
├── apps/ # Web Applications
│ ├── auth-server/ # OAuth 2.0 Authentication Server
│ ├── web/ # Main Web Application (MVC/Razor Pages)
│ └── public/ # Public Website / Landing Page
│
├── gateways/ # API Gateways
│ ├── web/ # Web Gateway (internal services)
│ └── public/ # Public Gateway (external APIs)
│
├── services/ # Microservices
│ ├── identity/ # Users, Roles, OpenIddict
│ ├── administration/ # Permissions, Features, Settings
│ ├── audit-logging/ # Audit Logs
│ ├── tenant-management/ # Tenant definitions and management
│ ├── background-jobs/ # Hangfire Background Jobs
│ ├── data-management/ # Data Import/Export
│ └── .../ # Business-specific services (e.g. CIM Service)
│
├── aspire/ # .NET Aspire Orchestration
│ └── GridLab.Gmss.AppHost/ # Aspire AppHost (startup orchestrator)
│
├── etc/ # Infrastructure Configuration Files
│ ├── elastic/ # Elasticsearch configuration
│ ├── grafana/ # Grafana dashboards & config
│ ├── otel/ # OpenTelemetry Collector config
│ ├── prometheus/ # Prometheus configuration
│ ├── rabbitmq/ # RabbitMQ configuration
│ └── valkey/ # Valkey (Redis) configurationInfrastructure Components
Core Infrastructure
| Component | Image | Port(s) | Purpose |
|---|---|---|---|
| PostgreSQL | postgres:18 |
5432 | Primary relational database for all microservices |
| Valkey | valkey/valkey:8 |
6379 | Redis-compatible distributed cache (sessions, caching) |
| RabbitMQ | rabbitmq:4-management |
5672, 15672 | Message broker for async events and distributed messaging |
| Elasticsearch | elasticsearch:8.11.3 |
9200, 9300 | Centralized logging, full-text search, log analytics |
Observability Stack
| Component | Image | Port(s) | Purpose |
|---|---|---|---|
| Prometheus | prom/prometheus:v3 |
9090 | Metrics collection, alerting, and time-series database |
| Grafana | grafana/grafana:12.0 |
3001 | Metrics visualization, dashboards, and alerting UI |
| Jaeger | jaegertracing/jaeger:2.17.0 |
16686, 4317, 4318 | Distributed tracing for request flow visualization |
| Kibana | kibana:8.11.3 |
5601 | Elasticsearch UI for log exploration and visualization |
| OTel Collector | OpenTelemetry Collector | 4317, 4318 | Unified telemetry collection and export pipeline |
Configuration files are located in the etc/ directory:
etc/
├── elastic/ # Elasticsearch configuration
├── grafana/
│ ├── config/ # Grafana settings
│ └── dashboards/ # Pre-configured dashboards
├── otel/ # OpenTelemetry Collector configuration
│ └── otel-collector-config.yaml
├── prometheus/ # Prometheus configuration
│ └── prometheus.yml
├── rabbitmq/ # RabbitMQ configuration
│ ├── rabbitmq.conf
│ └── enabled_plugins
├── valkey/ # Valkey (Redis) configuration
│ └── valkey.conf
└── secrets/ # Secret managementDatabase Schema
Each microservice owns its own database following the Database-per-Service pattern:
| Service | Database | Description |
|---|---|---|
| Identity Service | Gmss_Identity |
Users, Roles, Claims, OpenIddict clients/scopes |
| Administration Service | Gmss_Administration |
Permissions, Features, Settings definitions |
| Administration Service | Gmss_AbpBlobStoring |
Binary Large Object (BLOB) storage |
| Audit Logging Service | Gmss_AuditLogging |
Security audit logs, entity change logs |
| Tenant Management Service | Gmss_TenantManagement |
Tenant definitions, tenant-specific features |
| Data Management Service | Gmss_DataManagement |
Data import/export and processing |
| Background Jobs Service | Gmss_BackgroundJobs |
Hangfire job storage and scheduling |
AuthServer Role
Important: The AuthServer does NOT create any databases. It only:
- Reads from databases created by the services above
- Handles authentication/authorization
- Issues tokens using OpenIddict
How Automatic Migration Works
Each service has a RuntimeDatabaseMigrator that runs during OnPreApplicationInitializationAsync:
// Example from IdentityService
public override async Task OnPreApplicationInitializationAsync(ApplicationInitializationContext context)
{
using var scope = context.ServiceProvider.CreateScope();
await scope.ServiceProvider
.GetRequiredService<IdentityServiceRuntimeDatabaseMigrator>()
.CheckAndApplyDatabaseMigrationsAsync();
}The migrator:
✅ Acquires a distributed lock (prevents concurrent migrations)✅ Checks if database exists (creates if not)✅ Applies EF Core migrations (creates/updates tables)✅ Runs data seeders (creates initial data)✅ Releases lock
Running with .NET Aspire (Recommended)
.NET Aspire is the recommended way to run the solution locally. It orchestrates all services, databases, and infrastructure dependencies with a single launch.
Pre-requirements (Aspire)
- Docker Desktop must be running (Aspire uses containers for infrastructure)
- .NET 10+ SDK installed
Authenticate with Docker Registry
Before running, authenticate with the container registry:
docker login
docker pull postgres:18
docker pull valkey/valkey:8
docker pull rabbitmq:4-management
docker pull docker.elastic.co/elasticsearch/elasticsearch:8.11.3
docker pull docker.elastic.co/kibana/kibana:8.11.3
docker pull prom/prometheus:v3
docker pull grafana/grafana:12.0
docker pull jaegertracing/jaeger:2.17.0
docker pull ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector-contrib:0.123.0Running with Aspire
-
Ensure Docker Desktop is running
-
Set
GridLab.Gmss.AppHostas the startup project in Visual Studio -
Press
F5(orCtrl+F5), or from the command line:dotnet run --project aspire/GridLab.Gmss.AppHost -
The Aspire Dashboard opens automatically at
http://localhost:15888, providing:- Resources — all running projects and containers with endpoints and status
- Console Logs — aggregated, searchable logs from every service
- Structured Logs — filterable structured/semantic log entries
- Traces — distributed traces showing request flow across services
- Metrics — runtime and application-level metrics
Viewing Logs
- Aspire Dashboard: http://localhost:15888 → Structured Logs
- Elasticsearch/Kibana: http://localhost:5601
- Container logs:
docker logs <container-id> - Service logs: Check output in terminal or Aspire Console Logs
Service Startup Order
Aspire enforces the following startup order to ensure proper database migrations:
This order is enforced in aspire/GridLab.Gmss.AppHost/Program.cs using .WaitFor() dependencies.
Configure User Secrets (Optional)
Override default credentials using .NET User Secrets:
cd aspire/GridLab.Gmss.AppHost
# PostgreSQL credentials
dotnet user-secrets set "Parameters:postgres-user" "postgres"
dotnet user-secrets set "Parameters:postgres-password" "your-secure-password"
# RabbitMQ credentials
dotnet user-secrets set "Parameters:rabbitMQ-user" "user"
dotnet user-secrets set "Parameters:rabbitMQ-user-password" "your-secure-password"Application Endpoints
Web Applications
| Application | URL | Description |
|---|---|---|
| Aspire Dashboard | http://localhost:15888 |
Orchestration dashboard |
| Auth Server | http://localhost:44346 |
OAuth 2.0 / OpenIddict server |
| Web Application | http://localhost:44304 |
Main Web UI |
| Public Website | http://localhost:44354 |
Public landing page |
API Gateways
| Gateway | URL | Description |
|---|---|---|
| Web Gateway | http://localhost:44310 |
Internal API gateway |
| Public Gateway | http://localhost:44369 |
External API gateway |
Microservices
| Service | URL | Description |
|---|---|---|
| Identity Service | http://localhost:44321 |
User & role management |
| Administration Service | http://localhost:44355 |
Permissions, features, settings |
| Tenant Management Service | http://localhost:44348 |
Tenant management |
| Audit Logging Service | http://localhost:44328 |
Audit logging |
| Data Management Service | http://localhost:44378 |
Data management |
| Background Jobs Service | http://localhost:44393 |
Hangfire background jobs |
Observability
| Tool | URL | Description |
|---|---|---|
| Grafana | http://localhost:3001 |
Metrics dashboards |
| Prometheus | http://localhost:9090 |
Metrics & alerting |
| Jaeger | http://localhost:16686 |
Distributed tracing |
| Kibana | http://localhost:5601 |
Log exploration |
| RabbitMQ Management | http://localhost:15672 |
Message broker UI |
Building the Solution
This workspace uses the .slnx solution format (supported in .NET SDK 9.0.200+ and .NET 10).
Build with Graph
dotnet build GridLab.Gmss.slnx /graphThe /graph switch constructs a Directed Acyclic Graph (DAG) of all project dependencies:
- Improves parallel scheduling for faster builds
- Avoids double-building projects referenced by multiple entry points
- Detects circular or missing dependency issues upfront
Useful Commands
| Task | Command |
|---|---|
| Build with graph | dotnet build GridLab.Gmss.slnx /graph |
| Build with graph + isolation | dotnet build GridLab.Gmss.slnx /graph /isolate |
| List projects in solution | dotnet sln GridLab.Gmss.slnx list |
| Restore packages | dotnet restore GridLab.Gmss.slnx |
Configuration
Configuration Check
Review these configurations before running:
appsettings.jsonfiles in each application/service project- Connection strings for PostgreSQL
- Redis/Valkey connection settings
- RabbitMQ credentials
- Elasticsearch URLs
Key Configuration Files
| File | Purpose |
|---|---|
aspire/GridLab.Gmss.AppHost/Program.cs |
Service orchestration and dependencies |
aspire/GridLab.Gmss.AppHost/InfrastructureExtensions.cs |
Infrastructure container definitions |
etc/*/ |
Infrastructure configuration files |
appsettings.json (per service) |
Service-specific configuration |
services/*/Data/*RuntimeDatabaseMigrator.cs |
Migration logic |
services/*/Data/*DataSeeder.cs |
Data seeding logic |
Generating a Signing Certificate
For production environments:
dotnet dev-certs https -v -ep openiddict.pfx -p 17bd45f0-e7c8-4350-a745-66c4d501ed8bClient-Side Dependencies Setup
If client-side packages are missing:
# Install ABP CLI
dotnet tool install --global Volo.Abp.Studio.CLI --version X.Y.Z
# Or restore from local tool manifest
dotnet tool restore
# Install client-side libraries
abp install-libsHealth Checks
Each service exposes health checks at /health-status:
http://localhost:{port}/health-statusUse these to verify services are fully initialized before making requests.
Observability Guide
Metrics (Prometheus + Grafana)
-
Access Grafana at http://localhost:3001
-
Default credentials: admin / admin
-
Pre-configured dashboards available in
etc/grafana/dashboards/ -
Prometheus data source auto-configured
Adding a New Service to Prometheus
To enable metrics scraping for a new microservice, add a job entry to etc/prometheus/prometheus.yml:
scrape_configs:
# ... existing jobs ...
- job_name: 'your-new-service'
scheme: http
metrics_path: 'metrics'
static_configs:
- targets: ['host.docker.internal:<PORT>']Example: Adding a new reporting service on port 44399:
- job_name: 'reporting'
scheme: http
metrics_path: 'metrics'
static_configs:
- targets: ['host.docker.internal:44399']Note: Use
host.docker.internalto access services running on the host from within Docker containers.
After updating, restart the Aspire AppHost to apply changes.
Current configured services:
| Job Name | Port | Service |
|---|---|---|
identity |
44321 | Identity Service |
administration |
44355 | Administration Service |
tenantmanagement |
44348 | Tenant Management Service |
auditlogging |
44328 | Audit Logging Service |
datamanagement |
44378 | Data Management Service |
authserver |
44346 | Auth Server |
web |
44304 | Web Application |
Tracing (Jaeger)
-
Access Jaeger at http://localhost:16686
-
Select a service from the dropdown
-
View distributed traces across microservices
-
Analyze request latency and bottlenecks
Logging (Elasticsearch + Kibana)
-
Access Kibana at http://localhost:5601
-
Create index pattern for
logs-* -
Explore logs in Discover tab
-
Build visualizations and dashboards
OpenTelemetry Collector
The OTel Collector receives telemetry from all services and exports to:
- Metrics → Prometheus
- Traces → Jaeger
- Logs → Elasticsearch
Configuration: etc/otel/otel-collector-config.yaml
Troubleshooting
| Problem | Solution |
|---|---|
Cannot connect to PostgreSQL |
Ensure Docker Desktop is running: docker ps |
Port already in use |
Stop other services using same ports |
OpenIddict client not found |
IdentityService didn't seed — check logs |
| Infrastructure containers not starting | Run docker system prune and retry |
Manual Database Reset (Development Only)
# Connect to PostgreSQL
docker exec -it <postgres-container-id> psql -U postgres
# Drop databases (they will be recreated on service startup)
DROP DATABASE "Gmss_Identity";
DROP DATABASE "Gmss_Administration";
DROP DATABASE "Gmss_AuditLogging";
DROP DATABASE "Gmss_TenantManagement";
DROP DATABASE "Gmss_BackgroundJobs";
DROP DATABASE "Gmss_AbpBlobStoring";
DROP DATABASE "Gmss_Cim";
\q
# Restart Aspire AppHost
cd aspire\GridLab.Gmss.AppHost
dotnet runHelpful Commands
# Check running containers
docker ps
# View container logs
docker logs <container-id>
# Reset all databases (development only)
docker exec -it <postgres-container> psql -U postgres -c "DROP DATABASE \"Gmss_Identity\";"
# ... repeat for other databases
# Restart the entire stack
cd aspire\GridLab.Gmss.AppHost
dotnet runProduction Deployment
For production environments:
- Pre-create databases on your PostgreSQL server
- Run migrations manually using CI/CD pipeline
- Configure connection strings via environment variables or secrets
- Disable auto-migration in production configuration
- Use proper SSL certificates (not dev-certs)
- Configure external observability (cloud-based monitoring)
Example migration command:
dotnet ef database update --project services/identity/GridLab.Gmss.IdentityService
dotnet ef database update --project services/administration/GridLab.Gmss.AdministrationService
# ... repeat for other servicesSummary
| Use Aspire AppHost for development | Start AuthServer before other services |
| Wait for health checks before testing | Manually create database tables |
| Check service logs if migrations fail | Run multiple instances during first startup |
Use docker ps to verify containers |
Modify infrastructure configs without testing |
Need Help? Check the logs in each service's output or the Aspire Dashboard for detailed migration progress and errors





