2.7 KiB
Flux vs. MVVM Architecture
Decision Matrix: Flux vs. MVVM
Criterion | Plain Effector (Flux) | MVVM Layer on Top |
---|---|---|
Boilerplate | Low | Higher |
React Ecosystem Fit | Native | Non-idiomatic |
Two-way Binding Risk | None | Possible |
Testability | High (domain files are pure) | High but more layers |
Refactor Complexity | Lower | Higher |
Why We Chose Flux with Effector
For our React SPA, we've adopted an "Effector-based Flux architecture" for the following reasons:
-
Better React Integration: Flux's unidirectional data flow aligns perfectly with React's rendering model.
-
Simplicity: Fewer abstractions and less boilerplate compared to MVVM.
-
Predictability: One-way data flow makes it easier to trace how state changes propagate through the application.
-
Testability: Pure functions in model.ts are easy to test without complex mocking.
-
TypeScript Friendly: Effector has excellent TypeScript support, providing type safety throughout the application.
Flux Architecture Flow
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Actions │────>│ Store │────>│ View │
│ (Events) │ │ (State) │ │ (React) │
└─────────────┘ └─────────────┘ └─────────────┘
▲ │
│ │
└───────────────────────────────────────┘
User Interaction
Effector Implementation
Effector provides three main primitives that map to Flux concepts:
-
Events (Actions): Trigger state changes
export const setProductName = createEvent<string>();
-
Stores (State): Hold application state
export const $product = createStore(initialState) .on(setProductName, (state, name) => ({ ...state, name }));
-
Effects (Async Actions): Handle side effects
export const fetchProductFx = createEffect(async (id: string) => { return await api.getProduct(id); });
Conclusion
While MVVM is a valid architecture pattern, Flux with Effector provides a more natural fit for React applications, with less boilerplate and better alignment with React's unidirectional data flow model. This approach keeps our codebase clean, testable, and maintainable.