mmex-ios

Architecture and MVVM Refactoring Roadmap

This document describes the current architecture, the page boundaries already in place, and the next refactoring steps. The goal is to give new code clear ownership while gradually reducing the responsibilities of the shared application model. Shared data should not be copied into each page merely to make the architecture look more strictly layered.

Current layers

SwiftUI Views → Page ViewModels → Repositories / Query Loaders → SQLite

Data value types represent database records. Preference and AppContext provide user preferences and cross-page selection context.

Presentation

ViewModels and application data

Data and persistence

Application dependencies

Page boundaries in place

Page area ViewModel responsibilities Still provided by the shared application object
Overview Current and previous period transactions, KPIs, account balances, cancellable refresh work Database connection, account list, base currency, and display formatting
Insights Date range, trend transactions, account flows, and base currency Account and currency lists, and database connection
Journal / Transaction Journal list, search grouping, save, and delete Database connection and account/category/payee lookup data
Settings Setting selections, option lists, error presentation, and refresh coordination Setting persistence validation, shared list caches, and cache invalidation/reloading
Scheduled Overview Date grouping, sorting, and due-date actions Scheduled list/split caches, database connection, and display lookup data

These boundaries are an intermediate stage of an incremental migration. A page ViewModel can read shared Repository caches without putting its own presentation state back into the shared object.

  1. A View sends a user action or filter change to its page ViewModel.
  2. The ViewModel reads shared cached data or calls a Repository or query loader.
  3. The Repository accesses SQLite and returns Data values.
  4. The ViewModel publishes presentation state, which the View renders.

Views render state and forward user intent. ViewModels handle filtering, display models, asynchronous work, and operation results. Repositories handle persistence. Data that must be shared across pages stays in the application layer, with explicit reload and invalidation behavior.

Future TODOs

Work from lower-dependency areas toward higher-dependency areas. Preserve behavior and keep the project buildable at each stage.

1. Audit and reduce the shared ViewModel

2. Standardize database concurrency and loading

3. Clarify dependency injection and View interfaces

4. Add regression coverage

Refactoring principles

  1. Do not duplicate cross-page data to avoid shared dependencies. Every shared state value should have one clear owner.
  2. A ViewModel should expose only the state and actions its page needs. New Views should not receive Repositories, SQLite queries, or the entire application object without a clear reason.
  3. Prefer value types for inputs and outputs. Use mutable bindings only for real two-way editing.
  4. Async results must remain associated with the filter and database state that produced them; stale results must not overwrite newer state.
  5. Migrate one reviewable vertical slice at a time, preserve existing behavior, and build before completing each change.