public class Architecture : CleanArchitecture

Not just a CRUD App.
An Engineering Standard.

A production-grade, modular ERP for PCI-DSS governance. Built on .NET 8, Blazor WebAssembly, and Domain-Driven Design. Engineered for high-performance analytics and complex state orchestration.

.NET 8 Blazor WASM FastEndpoints MediatR (CQRS) EF Core Redis RabbitMQ MongoDB GridFS Docker

The Solution Topology

Deliberately split to enforce separation of concerns and enable code sharing between Web (Blazor) and Mobile (MAUI).

PCIShield.Domain

The heart of the system. Contains rich DDD Aggregates (Merchant, Assessment) that enforce invariants. No framework dependencies allowed here.

Merchant.cs
1public class Merchant : AggregateRoot
2{
3 public void FinalizeAssessment(Assessment a)
4 {
5 Guard.Against.MissingEvidence(a);
6 Guard.Against.OpenFindings(a);
7
8 this.ComplianceStatus = Status.Compliant;
9 this.AddDomainEvent(new AssessmentFinalizedEvent(this.Id));
10 }
11}

The Data Engine:
Specification Pattern

We don't write raw LINQ in controllers. We use highly optimized EF Core Specifications that project data into flat DTOs directly in the SQL Engine.

  • Split Queries: Prevents Cartesian explosion on complex graphs.
  • In-Database BI: Calculates compliance scores and risk metrics in the projection, not in memory.
  • Reusable: The same spec is used for EF Core fetching and Redis cache keys.
MerchantAdvancedGraphSpecV7.cs
public MerchantAdvancedGraphSpecV7(Guid id)
{
Query.Where(m => m.Id == id);
// Server-Side Projection (Runs in SQL)
Query.Select(m => new MerchantDto {
RiskScore = m.Assessments.Average(a => a.RiskLevel),
GapCount = m.Assessments.SelectMany(a => a.Findings).Count(),
IsHubNode = m.NetworkSegments.Count() > 10
});
Query.AsSplitQuery().AsNoTracking();
}
MerchantMasterOrchestrator.cs
public class MerchantOrchestrator
{
// Reactive State Management
public MerchantDto State { get; private set; }
public IObservable<Unit> RefreshRequired => _subject;
public async Task SaveAsync()
{
// Call Typed Client
var res = await _client.UpdateAsync(State);
// Notify SignalR Group
await _hub.NotifyMerchantUpdated(State.Id);
}
}

Presentation Layer:
The Orchestrator Pattern

Our Blazor UI is not spaghetti code. We strictly separate layout (.razor) from logic.

  • Smart Orchestrators: C# classes that hold state, manage API calls, and handle SignalR events.
  • Dumb Views: Razor files only bind to the Orchestrator's state.
  • Shared DTOs: The API and Blazor Client share the exact same PCIShield.Shared.dll, ensuring 100% type safety.

Business Intelligence & Analytics

It's not just a CRUD app. It's a Data Warehouse. We use Background Jobs (Hangfire) to pre-calculate complex risk matrices, serving millisecond-latency dashboards via Redis.

98.5%
Compliance Rate
1,240
Active Assessments
12
Critical Findings
A+
Avg Risk Score