mvp-view-logic-decoupling
MVP View-Logic Decoupling
Overview
Apply a strict Presenter-Manager-Store structure that keeps UI components free of business logic and centralizes cross-module behavior.
Target Architecture
- Put module state in singleton Zustand stores under
stores/. - Add one manager class per store under
managers/. - Use manager methods to expose actions and non-subscribed read helpers.
- Create one global presenter that owns all managers and global capabilities.
- Provide presenter via React Context and expose it with
usePresenter. - Let business components call presenter/managers directly and subscribe to stores directly.
Component Boundaries
UI components- Keep pure and reusable.
- Accept only view-related props.
- Avoid business rules and side effects.
Business components- Consume presenter for global actions and cross-module communication.
- Subscribe to store state via selectors.
- Organize by domain.
Business orchestration layer- Compose lower-level business modules.
- Keep high-level flow readable in one place.
Feature implementation modules- Implement isolated business capabilities per feature.
Mandatory Rules
- Use arrow functions for all manager and presenter methods.
- Do not define constructors in manager or presenter classes.
- Avoid
this-binding ambiguity by using class fields with arrow methods. - Prefer direct presenter/store access over deep business prop drilling.
- Remove duplicate data/action plumbing when presenter already provides the capability.
Implementation Workflow
- Identify domains and split state into independent stores.
- Create each store as singleton Zustand state + actions.
- Create one manager class per store.
- Add arrow-function methods only; avoid constructor setup.
- Create global presenter class and instantiate managers as class fields.
- Add Context Provider +
usePresenterhook. - Refactor business components to use presenter/stores directly.
- Move remaining pure display parts into UI components.
- Delete unnecessary business prop forwarding.
Minimal TypeScript Skeleton
// stores/todo.store.ts
import { create } from "zustand";
type TodoState = {
items: string[];
add: (item: string) => void;
};
export const useTodoStore = create<TodoState>((set) => ({
items: [],
add: (item) => set((state) => ({ items: [...state.items, item] })),
}));
// managers/todo.manager.ts
import { useTodoStore } from "../stores/todo.store";
export class TodoManager {
addItem = (item: string) => {
useTodoStore.getState().add(item);
};
getItemsSnapshot = () => {
return useTodoStore.getState().items;
};
}
// presenter/app.presenter.ts
import { TodoManager } from "../managers/todo.manager";
export class AppPresenter {
todoManager = new TodoManager();
notifyGlobal = (message: string) => {
console.log("global event", message);
};
}
export const appPresenter = new AppPresenter();
// presenter/presenter-context.tsx
import { createContext, useContext, type PropsWithChildren } from "react";
import { appPresenter } from "./app.presenter";
const PresenterContext = createContext(appPresenter);
export const PresenterProvider = ({ children }: PropsWithChildren) => (
<PresenterContext.Provider value={appPresenter}>{children}</PresenterContext.Provider>
);
export const usePresenter = () => useContext(PresenterContext);
// business/TodoPanel.tsx
import { usePresenter } from "../presenter/presenter-context";
import { useTodoStore } from "../stores/todo.store";
import { TodoList } from "../ui/TodoList";
export const TodoPanel = () => {
const presenter = usePresenter();
const items = useTodoStore((s) => s.items);
return (
<TodoList
items={items}
onAdd={(v) => presenter.todoManager.addItem(v)}
/>
);
};
Refactor Checks
Run this check before finishing:
- Verify UI components do not import presenter/manager/store.
- Verify business components avoid unnecessary prop relays.
- Verify every store has exactly one manager owner.
- Verify manager/presenter methods are arrow functions.
- Verify manager/presenter classes do not declare constructors.
- Verify cross-domain communication goes through presenter-level APIs.
Anti-Patterns
- Put business logic in UI components.
- Duplicate one capability in multiple managers.
- Pass action/state through several business layers when presenter direct access is possible.
- Mix orchestration logic into low-level feature modules.
- Use prototype methods (
foo() {}) in manager/presenter classes.
More from peiiii/nextclaw
ui-ux-pro-max
Use when the user wants professional UI/UX design guidance, design-system generation, UX review, or stack-specific frontend guidance through a bundled local UI/UX Pro Max dataset and Python search runtime.
2impeccable
Use when the user wants distinctive, production-grade frontend design, anti-generic AI aesthetics, UX critique, technical UI audits, or final polish through bundled Impeccable references and an optional upstream detector CLI.
2lark-cli
Use when the user wants to operate Lark or Feishu via the local lark-cli (@larksuite/cli), including install, app credentials, OAuth, readiness checks, and safe read/write boundaries.
1opencli
Use when the user wants to use websites, browser login sessions, Electron apps, or external CLIs through a local OpenCLI setup, especially when setup guidance, readiness checks, and safe task execution are needed.
1find-skills
Use when the user wants to discover, evaluate, and install external agent skills from the open skills ecosystem, especially through the Vercel Skills CLI.
1superpowers
Use when the user wants a disciplined software development workflow with design-first planning, implementation plans, TDD, systematic debugging, code review, or verification-before-completion, adapted from obra/superpowers.
1