Skip to content

Architecture

Valkerran edited this page Sep 2, 2026 · 2 revisions

Architecture

PCEdit is a .NET 10 solution (PCEdit.slnx) in three layers plus two test projects. The rule that shapes everything: the save-file library knows nothing about the app, and the app layer knows nothing about the UI framework.

Project TFM Role
PCEdit.SaveFileHandler net10.0 Dependency-free library: parse, edit, re-serialize save files. The real work.
PCEdit.App.Core net10.0 Portable app layer: ViewModels, services, item catalog, localization, platform interfaces. No UI-framework dependency.
PCEdit.Desktop net10.0 The UI head — Avalonia, on Linux / Windows / macOS.
PCEdit.SaveFileHandler.Tests net10.0 xUnit — serializer and round-trip.
PCEdit.App.Core.Tests net10.0 xUnit — ViewModels, services, localization parity.
flowchart TD
    D["PCEdit.Desktop<br/>Avalonia views, converters,<br/>platform implementations"]
    C["PCEdit.App.Core<br/>ViewModels · Services · Catalogs · Localization<br/>+ platform interfaces"]
    S["PCEdit.SaveFileHandler<br/>Models · Serializer · Store"]
    F[("Save file on disk")]

    D --> C --> S --> F
Loading

Dependencies point one way only. PCEdit.App.Core has no reference to Avalonia — the UI-specific concerns (file pickers, dialogs, navigation, screen-reader announcements, settings storage) sit behind small interfaces the head implements. That boundary is a legacy of a second, MAUI mobile head that has since been removed; it is kept because it is what makes the app layer testable without a UI.

Where the work happens

Concern Lives in Page
Framing, sections, byte-exact round-trip PlanetCrafterSaveFileSerializer, JsonRecordSerializer, PlanetCrafterSaveFileStore Save File Format, Save File Library
The loaded save, and every mutation to it SaveFileWorkspace App Core
Inventory rules (move, group, logistics) InventoryEditor App Core
Which planet something is on PlanetIndex + PlanetHash Worlds & Planets
Item names, icons, logistics groups ItemCatalog, LogisticsGroupCatalog Item Catalog
UI strings in 15 languages Localizer + Strings.resx Localization
Views, theme, navigation shell PCEdit.Desktop Desktop UI

Composition root

PCEdit.Desktop/App.axaml.cs builds the IServiceProvider: Core services as singletons, page ViewModels as singletons, and the Avalonia implementations of every platform interface. It applies the stored culture, then shows MainWindow.

Interface (in PCEdit.App.Core) Avalonia implementation
IFilePickerService AvaloniaFilePickerService
INavigationService AvaloniaNavigationService
IDialogService AvaloniaDialogService
IScreenReaderAnnouncer AvaloniaScreenReaderAnnouncer
IAppVersionInfo AvaloniaAppVersionInfo
ILanguageStore, IDisclaimerGate JsonSettingsStore
ISaveBackupService LocalFileSaveBackupService

Two invariants worth internalising

  1. An unedited load → save is byte-identical. BOM behaviour, decimal formatting, key order and unknown keys are all reproduced. A diff after an edit shows the edit and nothing else. This is asserted by the test suite, not merely intended.
  2. All mutation goes through ISaveFileWorkspace. ViewModels never construct a modified save themselves. Every model is a sealed record with init-only properties, edited with a with expression — hand-copying properties is how fields once got silently dropped on save.

Build and test

dotnet build PCEdit.slnx                                              # everything
dotnet build PCEdit.SaveFileHandler/PCEdit.SaveFileHandler.csproj     # fast inner loop
dotnet test  PCEdit.SaveFileHandler.Tests/PCEdit.SaveFileHandler.Tests.csproj
dotnet test  PCEdit.App.Core.Tests/PCEdit.App.Core.Tests.csproj
dotnet run --project PCEdit.Desktop/PCEdit.Desktop.csproj

global.json pins the SDK feature band (10.0.4xx). See Testing and Building & Packaging.

Clone this wiki locally