This repo showcases several frontend architectural patterns side‑by‑side. You can run the dev server and navigate between routes to see each pattern in isolation and then all combined.
Location: src/components/MVVM/
Pieces:
model.js– Pure data source (simulated async calls, rotating text data)presenter.js– Orchestrates user intent (button click) -> model calls -> pushes state fragmentsview.jsx– Pure, stateless rendering of props (no business logic)
Value:
- Easy to test presenter & model independently
- Clear separation between data retrieval and UI
- Predictable change surface when adding new interactions
Location: src/stores/
Stores (auth_store.js, stats_store.js) implement a minimal observer pattern:
onChange(cb)subscriptionpublish()internal fan‑out- Simple state container that can be shared by unrelated components (e.g., stats across dashboards)
Value:
- Decouples producers (services/presenters) from consumers (views)
- Enables multiple views to stay in sync without prop drilling
- Provides a natural place for caching & aggregation logic
Location: src/services/
Examples:
auth_service.js– Handles login, user retrieval, and pushes results intoauth_storemultichannel.js– Manages a WebSocket connection + channel subscription fan‑out
Value:
- One boundary for side effects (HTTP, WS). Pure logic never directly touches network APIs
- Swappable / mockable for tests or storybook scenarios
- Central place for retries, instrumentation, auth token refresh, etc.
Location: src/components/MultiChannel/ + services/multichannel.js
Demonstrates two independent real-time feeds (chart and orderDepth) subscribing via the same service. Each presenter only cares about its slice, not about socket lifecycle.
Value:
- Prevents duplicate socket connections
- Encapsulates reconnection & message routing
- Reduces UI coupling to transport details
Location: src/components/PuttingItAllTogether/
Modular version integrates:
- Auth (via service + store)
- Stats (store updates + refresh action)
- Text rotation (model/presenter pattern)
- Streaming (chart + order depth) via WebSocket service
Each concern remains in its own file; presenter stitches them for the view. This mirrors how larger apps evolve: compose small, well‑scoped building blocks.
We include progressively worse versions so you can compare:
| Route | File(s) | Description |
|---|---|---|
/all |
PuttingItAllTogether/ (modular) |
Clean separation: model + presenter + services + stores + view |
/all-monolith |
PuttingItAllTogetherMonolith.jsx |
Single class using real services/stores, all orchestration inline |
/all-inline |
PuttingItAllTogetherUltraMonolith.jsx |
Everything in one file (fake auth, inline intervals, no services/stores) |
| (legacy-style example) | components/bad.jsx |
Large god component with mixed responsibilities |
Key degradations as you move right:
- Testability: pure units disappear
- Reusability: logic welded to lifecycle methods
- Cognitive load: must understand entire file to change one feature
- Risk: higher chance of regression when editing unrelated logic
- Extensibility speed: more lines touched per feature
Adopting the modular approach yields:
- Faster feature delivery (smaller, well-defined change sets)
- Safer refactors (clear ownership of logic)
- Easier onboarding (four nouns: service, store, presenter, view)
- Cleaner test boundaries (pure model + presenter)
- Centralized side-effect management (auth, sockets, API)
For very small throwaway components, MVVM + store + service layers might be overkill. Start simple, then graduate patterns when:
- Logic is reused in 2+ places
- Side-effects need retry/metrics
- State must synchronize across routes/components
- Real-time + request/response flows intersect
npm install
npm run server &
npm run dev
Navigate to the routes mentioned above to explore the differences.
- Introduce a lightweight selector layer for derived store state
- Provide unit tests for a presenter & model as reference ...