Skip to content

Architecture

Jim Daley edited this page Jul 2, 2026 · 1 revision

Architecture

Forsetti follows a multi-module single-application architecture. The production app owns one visible SwiftUI module and activates service/feature lifecycle modules behind the Forsetti runtime boundary.

Architectural Pattern

The app uses Pattern B: one application target with multiple runtime modules.

flowchart TB
    subgraph AppTarget["Forsetti app target"]
        Entry["ForsettiApp"]
        Root["ForsettiProductionRootView"]
        Bootstrap["ForsettiRuntimeBootstrap"]
        AppServices["ForsettiApplicationServices"]
        UI["Retail UI module"]
        Dashboard["DashboardView and workspace shell"]
        FeatureViews["Feature SwiftUI workspaces"]
    end

    subgraph Runtime["Forsetti public framework boundary"]
        Core["ForsettiCore"]
        Platform["ForsettiPlatform"]
        Manifests["ModuleManifest loader"]
        Registry["ModuleRegistry factories"]
        Manager["Runtime module manager"]
        Services["ForsettiServiceContainer"]
    end

    subgraph External["External systems"]
        Jamf["Jamf Pro API"]
        Keychain["System Keychain"]
        Files["App container files"]
        Logs["Unified log"]
    end

    Entry --> Root --> Bootstrap
    Bootstrap --> AppServices
    Bootstrap --> Core
    Bootstrap --> Platform
    Bootstrap --> Manifests --> Registry --> Manager
    Bootstrap --> Services
    Manager --> UI --> Dashboard --> FeatureViews
    AppServices --> Jamf
    AppServices --> Keychain
    AppServices --> Files
    AppServices --> Logs
Loading

Dependency Direction

Forsetti is structured so dependencies flow from the app entry down into runtime, services, UI composition, and feature view models.

flowchart TD
    AppEntry["App entry"] --> Bootstrap["Runtime bootstrap"]
    Bootstrap --> PublicFramework["Forsetti public products"]
    Bootstrap --> ServiceAggregate["App service aggregate"]
    Bootstrap --> FactoryRegistry["Runtime module factories"]
    FactoryRegistry --> UIModule["Single UI module"]
    UIModule --> WorkspaceBuilders["Feature workspace builders"]
    WorkspaceBuilders --> ViewModels["Feature view models"]
    ViewModels --> ServiceFacades["App-owned service facades"]
    FactoryRegistry --> LifecycleModules["Service and feature lifecycle modules"]
    LifecycleModules --> ServiceFacades

    Bad1["Feature -> app entry"] -. disallowed .-> AppEntry
    Bad2["Feature -> unrelated feature"] -. disallowed .-> WorkspaceBuilders
    Bad3["Service module -> primary UI"] -. disallowed .-> UIModule
    Bad4["App -> framework internals"] -. disallowed .-> PublicFramework
Loading

Source Layout

Path Responsibility
ForsettiApp/App App identity, SwiftUI app entry, production root view, runtime bootstrap, app service aggregate, and framework container.
ForsettiApp/DesignSystem Theme, colors, typography, Metal-backed backgrounds, command activity bar, hardware visualization, and shared surface styles.
ForsettiApp/Framework/Core App-local dashboard module contracts, module registry, sharing support, and common record rendering helpers.
ForsettiApp/Framework/Networking Jamf API gateway, authentication service, request scope service, RSQL helpers, and URLSession tuning.
ForsettiApp/Framework/Security Keychain-backed secure store and Jamf credential persistence.
ForsettiApp/Framework/Diagnostics In-memory and persistent diagnostics model and export support.
ForsettiApp/Framework/UI Dashboard shell, settings, diagnostics view, scanner input, platform compatibility, and support views.
ForsettiApp/Modules App-owned feature workflows and feature-specific models, services, views, rendering, persistence, and tests.
ForsettiApp/Resources/ForsettiManifests Runtime manifests loaded by the Forsetti public framework.
ModulePackageTemplates App-owned package/template metadata for dashboard/import flows. These are not runtime manifests.
ForsettiTests Unit, integration, model, parser, layout, runtime, diagnostics, security, and feature tests.

Runtime Boundary

The production runtime boundary is defined by these facts:

  • The app links only the public ForsettiCore and ForsettiPlatform package products.
  • Runtime manifests use the com.forsetti.jamfpro.* namespace.
  • The app has exactly one production UI module: com.forsetti.jamfpro.retail.ui.
  • Service and feature modules are lifecycle modules; they activate behind the UI and expose readiness, validation, diagnostics, and capability declarations.
  • Feature SwiftUI views are still app-owned workspaces. The runtime module layer is the production activation and capability boundary.

Data Ownership

flowchart LR
    subgraph PrivateStores["Private module stores"]
        UIS["forsetti-retail-ui"]
        CS["forsetti-computer-search"]
        MS["forsetti-mobile-device-search"]
        ST["forsetti-support-technician"]
        PS["forsetti-prestage-director"]
        RP["forsetti-reports"]
        DT["forsetti-deployment-tracker"]
        PM["forsetti-permissions-matrix"]
    end

    subgraph SharedServices["Shared app services"]
        Credentials["JamfCredentialsStore"]
        Gateway["JamfAPIGateway"]
        Diagnostics["DiagnosticsCenter"]
        Packages["ModulePackageManager"]
    end

    PrivateStores --> SharedServices
    Credentials --> Keychain["Keychain service com.ravenforge.forsetti"]
    Gateway --> Jamf["Jamf Pro API"]
    Diagnostics --> NDJSON["ForsettiDiagnostics/forsetti-diagnostics.ndjson"]
    Diagnostics --> Unified["Unified log subsystem"]
    Packages --> Disk["App container package store"]
Loading

UI Composition

The visible UI starts only after runtime boot succeeds. DashboardView receives ForsettiFrameworkContainer from ForsettiApplicationServices, renders the command center, and navigates into dashboard-facing feature modules through ModuleContext.

flowchart TD
    Root["ForsettiProductionRootView"] -->|booting| Progress["ForsettiBootProgressView"]
    Root -->|ready| Dashboard["DashboardView"]
    Root -->|failed| Failure["ForsettiBootFailureView"]
    Dashboard --> Rail["Navigation rail"]
    Dashboard --> Header["Command center header"]
    Dashboard --> Cards["Module cards"]
    Dashboard --> Inspector["Tenant state and module mix"]
    Dashboard --> Drawer["Diagnostics drawer"]
    Cards --> ModuleRoot["module.makeRootView(context:)"]
    ModuleRoot --> ViewModel["Feature view model"]
    ViewModel --> Context["ModuleContext"]
    Context --> API["JamfAPIGateway"]
    Context --> Credentials["JamfCredentialsStore"]
    Context --> Diagnostics["DiagnosticsReporting"]
Loading

Platform Targets

Target Current setting
macOS deployment target 14.0
iOS deployment target 26.0
Bundle family com.ravenforge.forsetti
Code signing in CI Disabled for build/test commands with CODE_SIGNING_ALLOWED=NO

Known Boundary Note

Some app UI files live under framework-named source folders, especially under ForsettiApp/Framework/UI. The current architecture treats these as app-owned UI composition files, not framework internals. The runtime boundary remains enforced through package linking, manifest loading, public framework APIs, and the production bootstrap.

Clone this wiki locally