Skip to content

Architecture

albertoodev edited this page Jul 21, 2026 · 8 revisions

Architecture

SPM follows Clean Architecture with independent feature modules under lib/features/. Each feature has the same internal layout: data/domain/presentation/.

Top-level layout

lib/
├── core/
│   ├── constants/
│   ├── errors/          # failures.dart: 20 typed Failure subclasses + CompoundFailure
│   ├── injection/       # static service locators (AnalysisDI, IsolationDI, InjectionDI, ValidationDI…)
│   ├── loggor/          # SpmLogger (use instead of print)
│   └── types.dart       # shared type aliases (Result<T>, AsyncResult<T>, JsonRecord, …)
│
└── features/
    ├── analysis/        # static AST analysis  → analyze
    ├── injection/       # code transformation + app execution → inject / run
    ├── isolation/       # rebuild-scope extraction → isolate
    ├── profiler/        # runtime-side API imported by the instrumented app
    └── validation/      # structural-mutation gate → validate

Each feature follows the same three-layer layout:

<feature>/
├── data/
│   ├── data_sources/
│   ├── models/
│   └── repositories/
├── domain/
│   ├── entities/
│   ├── repositories/
│   └── use_cases/
└── presentation/        # one Command subclass, wiring CLI args → use cases via the feature's DI

Features

Feature Command Responsibility
analysis analyze Walks Dart files, finds every State subclass, extracts build-tree metrics via feature sets → specialized extractors and visitors. Streams AnalysisResultEntity records as JSONL.
isolation isolate Finds rebuild scopes (State, ConsumerWidget/HookConsumerWidget, builder patterns like BlocBuilder/Consumer/Selector/Obx/GetX/Observer) and transplants each into a self-contained .dart file, grouped by type. DI class: IsolationDI.
injection inject, run Reads the analysis JSONL and rewrites each target State class to extends SpmState<T> (adding an instanceId getter and the SpmState import); modes inject / remove. run then launches the app and connects to its Dart VM Service over WebSocket to collect ext.spm.profiler events.
profiler (imported by the app) Runtime-side API. SpmState.setState picks SpmProfiler.monitor() under --profile or SpmProfiler.monitorDataFlow() under --debug, measuring frame timings and rebuild counts and emitting events back to SPM over the VM Service.
validation validate Static gate deciding whether a mutation is a clean structural variant of a base file. DI class: ValidationDI. See AST Internals.

Core layer (lib/core/)

  • types.dart holds all shared type aliases. The key ones are Result<T> = Either<Failure, T>, AsyncResult<T>, StreamResult<T>, AsyncVoidResult = AsyncResult<void>, AsyncVoid = Future<void> (data layer, may throw), and JsonRecord = Map<String, dynamic>. Always use these aliases; never spell out the full types inline.
  • errors/failures.dart defines 20 typed Failure subclasses (e.g. InjectionFailure, RunAppFailure, FlutterAnalyzeFailure); CompoundFailure aggregates multiple failures.
  • injection/ holds static service locators built on lazy singleton getters (??=), with no external DI framework. Each DI class exposes a reset() to clear all singletons; call it in setUp/tearDown when testing through the DI layer.
  • loggor/logger.dart provides SpmLogger for stdout/stderr. Use it instead of print.

Entry points

  • bin/spm.dartlib/runner.dart (SpmRunner, a CommandRunner with the feature Command subclasses registered).
  • Each feature's presentation/ contains one Command subclass wiring CLI args to use cases via the feature's DI singleton.

Error handling

Either<Failure, T> is used throughout the data and domain layers (Left = error, Right = success). Data sources may throw domain-specific exceptions; repositories translate those into typed failures. Presentation commands fold results and return a non-zero status on failure, and bin/spm.dart terminates the process with that returned status.

Key dependencies

Package Role
analyzer Dart AST parsing and semantic resolution.
dartz Either type for functional error handling.
vm_service Connect to the Dart VM to collect profiler events.
web_socket_channel WebSocket transport to the VM Service.
args CLI argument parsing.
path Cross-platform path utilities.

Clone this wiki locally