Skip to content

Architecture

Chrison Simtian edited this page May 17, 2026 · 1 revision

Architecture

Onion + CQRS

The app follows an onion architecture with CQRS dispatched via Wolverine in-process. Dependencies point inward — Domain has no references; Application talks to ports; Infrastructure provides adapters.

See ADR-0004 and ADR-0006.

┌─────────────────────────────────────────────────────────┐
│                    ApiService / Web                     │   composition root
│      (Aspire-orchestrated, MudBlazor frontend)          │
├─────────────────────────────────────────────────────────┤
│                  ERP.Infrastructure                     │   adapters, DI
│     OR-Tools planner · EF Core · TickerQ · Save        │
├─────────────────────────────────────────────────────────┤
│                   ERP.Application                       │   ports, CQRS handlers
│   IRecipePlanner · IFactoryStateProvider · queries     │
├─────────────────────────────────────────────────────────┤
│                     ERP.Domain                          │   pure entities
│      ProductionTarget · ResourceAvailability · …       │
└─────────────────────────────────────────────────────────┘

Bounded contexts

Two bounded contexts live under src/:

  • ERP/ — the planner. Game-agnostic.
    • Domain/ — pure entities (ProductionPlan, ProductionTarget, ResourceAvailability, ProductionStep, …)
    • Application/ — ports + CQRS handlers, including IRecipePlanner
    • Infrastructure/ — adapters (the OR-Tools planner lives here)
    • Infrastructure/Persistence/ — EF Core + dual-provider plumbing
  • Satisfactory/ — game-specific adapters.
    • Catalog/ — parses Docs.json into game-agnostic recipes/items
    • Save/ — wraps the SatisfactorySaveNet fork and projects it into ERP.Domain entities

Both contexts publish ports into ERP.Application and provide adapters in ERP.Infrastructure. Wolverine wires everything in AddErpInfrastructure.

Composition root

src/AppHost/         # Aspire orchestrator — `dotnet run` entry point
src/ApiService/      # Minimal-API backend; PlanDto and friends live here
src/Web/             # Blazor Server UI (MudBlazor, FICSIT-themed)
src/ServiceDefaults/ # Aspire defaults

ADRs

All architecturally significant decisions live in docs/adr/. Headline ones:

ID Title
0001 Use .NET 10 as the target framework
0002 Use Blazor for the UI
0003 Use .NET Aspire for orchestration
0004 Use Onion Architecture
0005 Use CQRS in the application layer
0006 Wolverine as the in-process mediator
0008 Playwright for UI tests
0010 Game-agnostic catalogue contract
0014 Pure-C# .sav ingestion via fork
0017 MudBlazor as the UI framework
0018 Dual-provider persistence stack
0019 TickerQ background scheduler

ADRs are immutable once accepted — if the decision changes, write a new ADR that supersedes the old one (see ADR-0012 → ADR-0014 for the canonical example).

Clone this wiki locally