TDD & Architecture Philosophy
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
Dotorixel follows a strict architectural philosophy centered on high cohesion, low coupling, and a Rust-first core logic strategy. The project prioritizes self-contained core modules that are independent of UI frameworks (Svelte or SwiftUI), ensuring that critical domain logic—such as coordinate transformations, pixel manipulation, and history management—is portable across web and native Apple shells. This approach facilitates a multi-platform strategy where a shared Rust core serves as the single source of truth for both WebAssembly (WASM) and native Swift bindings.
The development workflow is driven by a Test-Driven Development (TDD) mindset where tests serve as behavioral specifications. By prioritizing "deep modules" over shallow abstractions, the architecture minimizes "config noise" and cognitive load, favoring a single atomic source of truth for features like the workspace system, tool handlers, and undo/redo logic.
Sources: CLAUDE.md:144-154, issues/013-apple-native-catchup.md:11-20
Core Architectural Principles
The project adheres to several key structural rules designed to ensure long-term maintainability and performance.
Logic Separation and Portability
A fundamental rule is that core logic is self-contained. Coordinate transforms, tool algorithms (pencil, eraser, flood fill), and undo/redo history must have no framework dependencies (DOM, Svelte, or SwiftUI) and no hidden side effects like global state or I/O. This enables:
- Testability: Pure functions and stateless transformations are easily verified with Vitest or Rust unit tests.
- Portability: Logic can be compiled to WASM for the web or linked via UniFFI for Apple native apps.
- Performance: Compute-heavy operations can be migrated to Rust without redesigning the UI layer.
Sources: CLAUDE.md:162-168, issues/013-apple-native-catchup.md:20-25
Deep vs. Shallow Modules
Dotorixel favors "right-sized modules" where a reader can understand a feature by looking at one place. The architecture avoids splitting code so finely that understanding a single feature requires navigating many files. Code is grouped by what changes together—for instance, a tool's logic, its state, and its cursor behavior belong in the same module rather than being scattered across "utils/" or "types/" directories.
Sources: CLAUDE.md:158-161, CLAUDE.md:180-182
Dependency Management
Modules interact through interfaces and contracts (types) rather than concrete implementations. This "Depend on interfaces, not implementations" rule ensures that components like the rendering engine (Canvas2D vs. Metal) or state management systems can be replaced independently.
Sources: CLAUDE.md:154-157
State Management Architecture
The project employs a hierarchical state model that differentiates between global shared state and per-document local state.
The Workspace Pattern
To support multi-image workflows, the architecture uses a Workspace class that manages an array of EditorState instances. Each EditorState retains its own independent pixel data, history, and viewport transform, while tools and colors are managed via a SharedState object.
The diagram above shows the relationship between the Workspace, individual tab states (EditorState), and the shared global tool/color state (SharedState).
Sources: issues/002-tab-system.md:46-65
State Refactoring and Reuse
By centralizing logic in the EditorState class, different UI themes (e.g., "Pebble" vs "Pixel") can be maintained without duplicating logic. Refactoring specific pages to use EditorState ensures that new features like shape tool previews or undo support are automatically enabled across all interfaces.
Sources: tasks/records/022-pixel-editor-state-refactor.md:7-18
TDD and Testing Strategy
Testing in Dotorixel is treated as a form of specification. Each test should read as a behavioral description of its subject, allowing developers to understand the module's purpose without reading the implementation.
Testing Principles
- Test Behaviors, Not Implementation: Assertions are made on outcomes (e.g., "did the pixel color change?") rather than internal steps or private method calls.
- Regression Defense: Coverage is prioritized for complex coordinate math, boundary conditions (like flood fill limits), and undo/redo state consistency.
- No Framework Testing: The project does not test the UI framework's reactivity (Svelte/SwiftUI). Instead, it tests the custom logic that feeds into those frameworks, such as event handlers and computed values.
Sources: CLAUDE.md:214-222
Multi-Platform Testing Data
The project uses pure data factory functions (e.g., createCanvas()) for consistent test data in both unit tests and Storybook. This ensures that mock data is deterministic and free from side effects.
Sources: CLAUDE.md:189-192
Multi-Platform Sync Strategy
The project maintains a strict "Web as Reference" strategy for layout while using platform-idiomatic bindings for core logic.
Shared Core Bindings
The Rust core is consumed via different bindings depending on the platform:
| Platform | Binding Technology | Generated Output |
|---|---|---|
| Web | wasm-pack (WASM) | wasm/pkg/ |
| Native Apple | UniFFI (Swift) | apple/generated/ |
Sources: issues/013-apple-native-catchup.md:11-20
Cross-Platform Data Flow
When a user interacts with the UI, the request flows through platform-specific event handlers into the shared core.
This flow illustrates how both the Web and Native shells interact with the same centralized Rust Core logic.
Sources: CLAUDE.md:14-25, issues/013-apple-native-catchup.md:11-25
Error Handling Philosophy
Dotorixel follows a "Fail at the boundary, trust the core" approach to error handling.
- Validation at the Boundary: Inputs are validated where they enter the system (user inputs, file I/O). For example, canvas dimensions are checked against a
MAX_DIMENSIONconstant (256 pixels) at the UI and WASM layers. - Trust the Core: Internal functions assume preconditions are met. This prevents code from being cluttered with redundant guards.
- Actionable Errors: Errors must tell the developer exactly what went wrong and how to fix it (e.g., "Canvas size must be between 1 and 256 pixels").
- Type-Level Prevention: The project prefers types that make illegal states unrepresentable (e.g., using enums for tool types instead of strings).
Sources: CLAUDE.md:169-176, tasks/records/057-canvas-resize-ux.md:104-106
Conclusion
The Dotorixel architecture is built on the premise that logic is more valuable than UI. By isolating the pixel art domain logic into a shared Rust core and enforcing a strict behavior-based testing regime, the project achieves high technical agility. This philosophy allows the team to overhaul UI layouts or migrate to high-performance rendering pipelines (Metal) without risking the integrity of the core drawing and editing features.
Sources: CLAUDE.md:162-168, README.md:28-40