UI-Agnostic MVVM ArchitecturePure Logic. Zero-Mock Tests.

State management that never touches the UI

Define your business logic in standard JavaScript classes. Let Strator wrap your state in reactive Proxies when mounted to the UI to automatically dispatch updates—delivering lightning-fast, zero-mock testability across any framework.

Get Started in 2 Minutes
$
pnpm add @strator/core @strator/react
Unit Tests
0 UI Mocks

Test pure classes in <1ms

Architecture
True MVVM

Strict Model / View separation

Reactivity
Fine-Grained Proxy

Zero unnecessary re-renders

Portability
100% Agnostic

React, Vue, Svelte, Vanilla, Node

strator-state-demo
import { Model } from "@strator/core";

export interface CartState {
  items: Array<{ id: string; name: string; price: number }>;
  discount: number;
}

export class CartModel extends Model<CartState> {
  static initialState: CartState = { items: [], discount: 0 };

  addItem(item: { id: string; name: string; price: number }) {
    // State is proxied when mounted to UI, automatically dispatching updates
    this.state.items.push(item);
  }

  getTotal(): number {
    const raw = this.state.items.reduce((sum, item) => sum + item.price, 0);
    return raw * (1 - this.state.discount);
  }
}
MVVM Internal Mechanics

How Strator separates the View from the Model

A battle-tested architecture that isolates state transitions inside pure ES classes while seamlessly providing reactive bindings to UI components.

MODEL (Pure Logic)

Standard JavaScript Class

Holds state, domain methods, business validation, and API integrations. Zero React/DOM dependencies.

class CounterModel extends Model {
  // Pure properties
  state = { count: 0 };
  increase() { this.state.count++; }
}
STRATOR ENGINE

Proxy Reactivity & Dispatcher

Wraps state in an ES Proxy and supplies the reactive Dispatcher only when wired to UI components.

Proxy TrapDeep Path Intercept
DispatcherAuto-Dispatched Updates
SubscriptionsFine-grained Selectors
VIEW (UI Layer)

Declarative UI Component

Consumes models via clean hooks (useLocalModel, useGlobalModel). Renders state and triggers actions.

function View() {
  const [m, count] = useLocalModel(
    CounterModel, (s) => s.count
  );
  return <button onClick={() => m.increase()}>
    Count: {count}
  </button>;
}

Interactive Lifecycle Simulator

Step through what happens inside Strator during a single state mutation.

1. View Invokes Model Method
Component calls model method (e.g. `counter.increase()`). The UI invokes logic without handling state changes directly.
Event:User Click ➔ Model Method
Interactive Sandbox

See the Reactive Dispatcher in Real Time

Interact with the live UI on the left and watch the Strator Dispatcher and Proxy mutation stream in the live telemetry console on the right.

Interactive TodoModel Component
2/4 done
Filter:
Total Items: 4
Write domain model as pure ES Class
high
Mutate state directly (Proxied auto-dispatch)
medium
Execute unit tests in 0.5ms with zero UI mocks
high
Connect to React with useLocalModel()
low
Dispatcher Event Stream
LIVE TELEMETRY
UI_UPDATE12:18:45 PM
TodoModel mounted via useLocalModel()
payload: {"initialCount":4}
Auto-tracked by Strator EngineConnected
The #1 Selling Point

Unit test business logic with Zero UI Mocks

Because Strator models are pure JavaScript classes, you never need JSDOM, React test harnesses, mock dispatchers, or brittle UI click simulations to verify critical business logic.

The Hard Way: Coupled to UI & DOM
~450ms / test
// Requires JSDOM, React wrappers, and heavy providers
import { render, screen, fireEvent } from "@testing-library/react";
import { Provider } from "react-redux";
import { store } from "../store";
import { CheckoutModal } from "../components/CheckoutModal";

test("applies discount coupon", async () => {
  // 1. Heavy DOM mount
  render(
    <Provider store={store}>
      <CheckoutModal />
    </Provider>
  );

  // 2. Brittle selector queries & async DOM events
  const input = screen.getByPlaceholderText(/coupon/i);
  fireEvent.change(input, { target: { value: "SAVE20" } });
  fireEvent.click(screen.getByRole("button", { name: /apply/i }));

  // 3. Slow assertions on rendered text
  expect(await screen.findByText(/\$80.00/)).toBeInTheDocument();
});
Drawbacks:Slow test suites, flaky selectors on DOM refactors, complex mock setups for hooks and contexts.
The Strator Way: Pure Class Unit Test
< 1ms / test
// Pure JavaScript. No DOM. No React harness. No Mocks.
import { describe, it, expect } from "vitest";
import { CheckoutModel } from "../models/CheckoutModel";

it("applies discount coupon", () => {
  // 1. Direct class instantiation with state
  const model = new CheckoutModel({ cartTotal: 100, discount: 0 });

  // 2. Direct method invocation
  model.applyCoupon("SAVE20");

  // 3. Pure deterministic state & computed assertions
  expect(model.state.discount).toBe(0.2);
  expect(model.getFinalPrice()).toBe(80);
});
Advantages:100x faster test runs, immune to UI styling changes, runs effortlessly in CI, Vitest, Node, or Bun.
Vitest Benchmark Simulation

Execute 100 Strator Model Unit Tests

Experience instant feedback loops with zero mock instantiation overhead.

Engineered for Maximum Utility

Built for developers who value clean architecture

Every feature in Strator is designed to maximize testability, eliminate UI coupling, and minimize cognitive friction.

OOP & Clean Code

Pure JavaScript Classes

Define domain logic using standard ES classes, TypeScript types, getters, and methods without framework boilerplate or vendor lock-in.

Learn more about this pattern →
Zero Boilerplate

Automatic Proxied Dispatch

No action decorators, types, or reducers. When mounted to the UI, mutating the reactive proxied state automatically dispatches granular updates.

Learn more about this pattern →
Fine-Grained

Deep Proxy Reactivity

Mutate arrays and nested objects naturally. Strator's reactive Proxy traps mutations down to the exact property path.

Learn more about this pattern →
100x Faster CI

Zero-Mock Unit Testing

Instantiate models directly in Vitest or Jest. Verify complex domain logic without mounting React components or simulating clicks.

Learn more about this pattern →
Universal Logic

UI-Framework Agnostic

Run the same Model across React, Vue, Svelte, or even Node.js CLI tools. Your business layer becomes truly portable.

Learn more about this pattern →
Lifecycle Control

Flexible State Scopes

Choose between useLocalModel (component-scoped), useSharedModel (keyed sharing), and useGlobalModel (app-wide singleton).

Learn more about this pattern →
Developer Ergonomics

Clean code across the full stack

Look at how effortless it is to define a Model, wire it to React, test it in Vitest, and reuse it anywhere.

import { Model } from "@strator/core";

export interface UserState {
  profile: { name: string; email: string } | null;
  isLoading: boolean;
  error: string | null;
}

export class UserModel extends Model<UserState> {
  static initialState: UserState = {
    profile: null,
    isLoading: false,
    error: null,
  };

  public async fetchUserProfile(userId: string) {
    this.state.isLoading = true;
    this.state.error = null;

    try {
      const res = await fetch(`/api/users/${userId}`);
      if (!res.ok) throw new Error("Failed to load user profile");
      this.state.profile = await res.json();
    } catch (err: any) {
      this.state.error = err.message;
    } finally {
      this.state.isLoading = false;
    }
  }

  public updateEmail(newEmail: string) {
    if (!this.state.profile) throw new Error("No profile loaded");
    this.state.profile.email = newEmail;
  }
}
State Management Matrix

How Strator compares

A direct feature comparison for engineering teams seeking maximum testability and minimal boilerplate.

Feature / Capability
Strator
Redux ToolkitMobXZustand
True MVVM Separation (Logic isolated from View)
Yes
Core architectural foundation
No
Coupled via action types/dispatch
Yes
Observable classes
No
Hook-bound closures
Zero-Mock Unit Testing (Pure Class Instantiation)
Yes
new Model(state) & test directly
No
Requires store config & mocks
Yes
Direct class testing
No
Requires hook wrappers / reset hacks
Automatic Proxied Dispatch
Yes
Proxy state automatically dispatches
No
Manual createAsyncThunk / actions
Yes
@action decorator
No
Manual function assignments
Multi-Framework Portability (React, Vue, Svelte, Node)
Yes
100% Agnostic core
Yes
Agnostic core, but verbose
Yes
Multi-framework bindings
No
Primarily React focused
Deep Nested Proxy Path Reactivity
Yes
Tracks exact nested property path
No
Immer produces new tree refs
Yes
Observable getters
No
Manual selectors required
Boilerplate & Mental Overhead
Yes
Minimal: Standard JS Class
No
High: Slices, Thunks, Selectors
No
Moderate: makeObservable rules
Yes
Minimal
Core Library Footprint
Yes
< 2.5 kB min+gzip
No
> 12 kB (RTK + Immer + Reselect)
No
> 15 kB
Yes
~1.5 kB
Quickstart Guide

Get up and running in minutes

Follow these 3 simple steps to add MVVM state management and zero-mock testing to your app.

1

Install packages

$pnpm add @strator/core @strator/react
2

Define your domain model

CounterModel.ts

Extend Model<T> and write standard class methods. State is proxied when mounted to the UI to dispatch updates.

import { Model } from "@strator/core";

export interface CounterState {
  count: number;
}

export class CounterModel extends Model<CounterState> {
  static initialState: CounterState = { count: 0 };

  public increase() {
    this.state.count += 1;
  }

  public reset() {
    this.state.count = 0;
  }
}
3

Connect to your UI component

Counter.tsx

Use useLocalModel or useGlobalModel with a selector for fine-grained re-renders.

import React from "react";
import { useLocalModel } from "@strator/react";
import { CounterModel } from "./CounterModel";

export function Counter() {
  const [model, count] = useLocalModel(CounterModel, (state) => state.count);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => model.increase()}>Increment</button>
      <button onClick={() => model.reset()}>Reset</button>
    </div>
  );
}
Frequently Asked Questions

Everything you need to know

Answers to common questions about Strator's architecture, Proxy reactivity, and testing methodology.

In conventional frontend frameworks, business logic frequently gets coupled with UI components, hooks, and component lifecycle events. This creates fragile codebases where testing a single business rule requires heavy component rendering, DOM mocks, and complex context providers. Strator isolates your business state and domain rules into pure JavaScript classes, making logic 100% portable, reusable, and testable in milliseconds.