Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 192 additions & 0 deletions .planning/codebase/ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# Architecture

**Analysis Date:** 2026-01-23

## Pattern Overview

**Overall:** MVVM (Model-View-ViewModel) with Message-Based Communication

**Key Characteristics:**
- Strict separation between Views (UI), ViewModels (presentation logic), and Services (business logic)
- Decoupled components communicate via a message aggregator (publish-subscribe pattern)
- Observable collections and bindable properties for reactive data flow
- Async/await patterns for long-running operations (file scanning, preview generation)

## Layers

**Presentation Layer (Views):**
- Purpose: Render UI and handle user input through Unity UI components
- Location: `Assets/Scripts/Views/`
- Contains: View classes inheriting from `ViewBase<T>`, dialog implementations, UI component bindings
- Depends on: ViewModels, Unity UI framework
- Used by: User interaction triggers, MonoBehaviour lifecycle

**ViewModel Layer:**
- Purpose: Manage presentation state, commands, and observable properties; translate user actions to service calls
- Location: `Assets/Scripts/ViewModels/`
- Contains: Model classes for each screen/dialog (e.g., `SearchModel`, `ItemsModel`, `CollectionsModel`), command definitions, selection tracking
- Depends on: Services, Messages, Utilities (BindableProperty, ObservableList, Commands)
- Used by: Views (bound via `BindTo()` method), Message receivers

**Service Layer (Business Logic):**
- Purpose: Core application logic, data management, and file operations
- Location: `Assets/Scripts/Services/`
- Contains: `Library` (main data service), `ConfigStore` (persistence), `ImportFolder` (file source abstraction), preview builders, metadata stores
- Depends on: Config, Utilities, FileSystem, Logging, Messaging
- Used by: ViewModels, other services

**Utility Layer:**
- Purpose: Cross-cutting concerns and infrastructure
- Location: `Assets/Scripts/Util/`
- Contains: Messaging system, commands, collections, file system abstractions, tag filtering, logging, STL file parsing
- Depends on: Minimal dependencies; Some core .NET/Unity only
- Used by: All layers

**Configuration Layer:**
- Purpose: Define data structures for application and user settings
- Location: `Assets/Scripts/Config/`
- Contains: Config classes (`CollectionConfig`, `ImportFolderConfig`, `SavedSearchConfig`), metadata structures
- Depends on: No dependencies (pure data)
- Used by: Services and ViewModels for persistent storage

**Messages:**
- Purpose: Define loosely-coupled communication contracts
- Location: `Assets/Scripts/Messages/`
- Contains: Message classes for cross-component communication (e.g., `SearchChangedMessage`, `SelectionChangedMessage`)
- Depends on: No dependencies
- Used by: ViewModels to publish events, message receivers to subscribe

## Data Flow

**Search and Filter Flow:**

1. User enters search tags in `SearchModel` (ViewModel)
2. `SearchModel` sends `SearchChangedMessage` via message aggregator
3. `ItemsModel` receives the message, calls `Library.GetItemPreviewMetadata(filters)`
4. `Library` queries `TagManager` to filter items by tags
5. Filtered items returned as observable list (`IPreviewList`)
6. `ItemsModel` updates its observable collection, triggering View updates
7. Views display updated item list via data binding

**Item Selection Flow:**

1. User clicks item in View (e.g., `ItemView`)
2. View sends `SelectionChangedMessage` via message aggregator
3. `ItemsModel` receives and processes selection, managing range selection state
4. Selection state reflected in `ItemPreviewModel.Selected` (BindableProperty)
5. Views observe property changes and update UI accordingly
6. Mass selection operations broadcast `MassSelectionStartingMessage` and `MassSelectionFinishedMessage` to suppress intermediate updates

**File Import Flow:**

1. User configures import folder via `AddImportFolderModel`
2. Folder path saved to config via `ConfigStore`
3. `ImportFolder` (file source) scans directory for STL files using `IFileSystem`
4. Files indexed and metadata extracted via `StlImporter` and geometry parsing
5. Preview images generated by `PreviewCam` (Unity camera rendering)
6. Metadata and previews cached in `PreviewImageStore`
7. `Library` notifies subscribers (ViewModels) of new items via observable collections

**State Synchronization Flow:**

1. User makes changes (tags, selection, settings)
2. ViewModels update via `BindableProperty` value changes
3. Changes trigger `ValueChanged` events
4. Views observe events and re-render
5. Services persist changes asynchronously to disk via `ConfigStore`
6. Layout state captured during application shutdown, restored on startup

## Key Abstractions

**ILibrary:**
- Purpose: Central interface for accessing and manipulating file metadata and preview data
- Examples: `Assets/Scripts/Services/Library.cs`
- Pattern: Facade pattern providing unified access to complex subsystems (tags, previews, file sources)

**IFileSource:**
- Purpose: Abstract different sources of 3D model files (folders, containers, cloud storage)
- Examples: `Assets/Scripts/Services/ImportFolder.cs`, `FileSourceBase.cs`
- Pattern: Strategy pattern allowing pluggable file source implementations

**IMessageRelay:**
- Purpose: Decoupled pub-sub messaging between components
- Examples: `Assets/Scripts/Util/Messaging/MessageAggregator.cs`
- Pattern: Observer/Mediator pattern using WeakReferences to avoid memory leaks

**IBindableProperty<T>:**
- Purpose: Observable property with change notification for reactive UI binding
- Examples: `Assets/Scripts/Util/BindableProperty.cs`
- Pattern: Observable pattern with value transformation and update suppression support

**ICommand/IAsyncCommand:**
- Purpose: Encapsulate user actions and async operations with execution state tracking
- Examples: `Assets/Scripts/Util/Commands/DelegateCommand.cs`, `AsyncCommand.cs`
- Pattern: Command pattern with ICommand interface for UI binding

**IReadOnlyObservableCollection<T>:**
- Purpose: Provide change notifications for collection modifications
- Examples: `Assets/Scripts/Util/Collections/ObservableList.cs`
- Pattern: Observer pattern on collections with add/remove/clear events

**IConfigStore:**
- Purpose: Abstract persistence layer for application configuration and metadata
- Examples: `Assets/Scripts/Services/ConfigStore.cs`
- Pattern: Repository pattern with async JSON serialization and ZIP compression

## Entry Points

**ViewInitializer:**
- Location: `Assets/Scripts/Views/ViewInitializer.cs`
- Triggers: Unity `Start()` lifecycle method on application launch
- Responsibilities:
- Creates and wires all services (Library, ConfigStore, MessageAggregator, UpdateChecker)
- Instantiates all ViewModels
- Binds ViewModels to Views
- Subscribes ViewModels to message aggregator
- Initializes application state (settings, layout, file sources)
- Handles application shutdown (saves state, disposes resources)

**ApplicationView:**
- Location: `Assets/Scripts/Views/ApplicationView.cs`
- Triggers: View binding from ViewInitializer
- Responsibilities: Displays top-level commands (settings, feedback, help)

**LibraryView:**
- Location: `Assets/Scripts/Views/LibraryView.cs` (via `Util/Unity/LibraryView.cs`)
- Triggers: View binding from ViewInitializer
- Responsibilities: Renders grid of 3D file preview items

## Error Handling

**Strategy:** Exception catching with logging, graceful degradation

**Patterns:**
- Try-catch blocks in async initialization methods (e.g., `ViewInitializer.Start()`)
- Logging via `ILogger` (UnityLogger) for diagnostics
- User feedback via dialogs for critical errors (update checks, missing dependencies)
- File system errors handled in `ImportFolder.RescanItemsAsync()` with exception logging
- STL parsing errors caught and logged without halting import process
- Config file not found handled gracefully with default values

## Cross-Cutting Concerns

**Logging:**
- Approach: Dependency injection of `ILogger` interface; UnityLogger implementation
- Entry point: `Assets/Scripts/Util/Logging/UnityLogger.cs`
- Configured by: `ApplicationSettings.LogLevel` property bound in ViewInitializer

**Validation:**
- Approach: Input validation in ViewModels before service calls; null checking with NotNull attributes
- Examples: `SearchModel` validates search tags before calling Library; dialog models validate user input

**Authentication:**
- Approach: Not applicable; desktop application without user accounts
- File access: Controlled by OS file permissions on import folders

**Async State Management:**
- Approach: `NotifyTask` and `NotifyingTaskWrapper` wrap async operations for UI binding
- Progress tracking: `ProgressMessage` and `MassSelectionStartingMessage` / `MassSelectionFinishedMessage` suppress UI updates during batch operations

---

*Architecture analysis: 2026-01-23*
Loading