This episode covers production-ready store architecture: a consistent folder structure, slices patterns per feature, action naming conventions, and per-domain code-splitting. You also build a quality pipeline with linting, type-checking, test coverage for critical stores, and documentation for onboarding.

A store that works on your laptop doesn't necessarily work in a team of twenty. Episode 21 covers production-ready store architecture: a consistent folder structure, slices patterns per feature, action naming conventions, and per-domain code-splitting. We also build a quality pipeline — linting, type-checking, test coverage for critical stores, and documentation for onboarding.
All these patterns are practical and can be adapted directly to your project.
Good store architecture isn't about the number of files, but about consistency: anyone on the team must be able to find a store, understand its responsibility, and add features without disturbing other domains.
Start with a folder structure that makes store files easy to find:
src/stores/
auth.store.ts
cart.store.ts
ui.store.ts
slices/
user.slice.ts
preferences.slice.ts
middleware/
logger.ts
index.tsThe src/stores folder holds all stores with file names like domain.store.ts. Slices and custom middleware go in subfolders so the main store stays short and focused on combining, not on content.
The .store.ts suffix distinguishes store files from other files at a glance and makes editor globbing easy. Small consistencies like this keep a large codebase navigable.
One store per domain, and large domains are split with the slice pattern from episode 16. Each feature holds one slice: user, preferences, notifications. The main store only combines:
export const useStore = create<AppState>()((...a) => ({
...createUserSlice(...a),
...createPreferencesSlice(...a),
}))createUserSlice(...a) and the other slices are written in separate files with a single responsibility. The rule: if two slices never need each other, consider making them separate stores.
Separately written slices are also easier to review: each pull request diff only touches one domain, so cross-feature conflicts are rare and responsibilities stay clear.
Consistent action names make review and code search far easier. Adopt the following conventions:
setTheme(theme) makes its purpose clear compared to update('theme', theme). These conventions make code grep and PR review much faster.
As a guideline, an action name should be able to complete a simple sentence: setTheme sets the theme, loadUser loads the user, addItem adds an item. If an action can't be explained in one short sentence, it's probably doing too much.
A bundle that carries all stores in the entry point makes the application slow to load. Split large stores into independent modules, then import them only when needed:
const useEditorStore = (await import('./editor.store')).useEditorStoreWith lazy loading, a large editor store is only loaded when the editor feature is opened. For React-side code, combine with React.lazy and Suspense so store modules split along with feature chunks.
Before deciding what to split, measure the bundle first. Libraries like webpack-bundle-analyzer show each module's size — focus splitting on stores that are large and rarely opened, not on small stores that are always used.
Make linting and type-checking part of CI, not a personal habit. In a Next.js project, run:
bun run lint
bun run typecheckbun run lint and bun run typecheck make sure stores don't carry type errors or convention violations into production. Block merges when either fails in the pipeline.
Also add linting at pre-commit or pre-push so mistakes are caught before reaching review. Fast feedback reduces waiting time and keeps quality at the cheapest point to fix.
Critical stores — auth, cart, permission — must have test coverage. Write unit tests like in episode 18 for actions and middleware. Document store contracts with short JSDoc: the state held, the actions provided, and the persist side effects. This documentation speeds up onboarding and reduces miscommunication between teams.
Coverage doesn't have to be 100 percent; focus on stores that touch money, security, and user data. To make sure stores are measured, run coverage only for the stores folder:
bun vitest run --coverage --include src/storesbun vitest run --coverage --include src/stores limits the report to store modules, so the coverage number reflects the domain that most needs testing.
Episode 21 lays out team-ready store architecture: a consistent folder structure, slices per feature, clear action naming conventions, per-domain code-splitting, and a quality pipeline from linting, type-checking, and test coverage to onboarding documentation.
Good architecture is a long-term investment: the consistency you apply now will pay off as the team grows and features multiply.
Key takeaways:
In the next episode we will discuss the alternative ecosystem and final reflection — comparing Zustand with Jotai, Valtio, Redux Toolkit, Context API, and URL state, building a decision framework for 2026, and recapping the journey of the entire series from episode 0 to 21.