13 KiB
ADR-014: State Management Strategy
Status: ✅ Accepted Date: 2026-02-24 Decision Makers: Frontend Team Related Documents: Frontend Guidelines, ADR-011: App Router Version Applicability: v1.8.0+ Next Review: 2026-08-01 (6-month cycle)
Gap Analysis & Requirement Linking
ปิด Gap จาก Requirements:
| Gap/Requirement | แหล่งที่มา | วิธีการแก้ไขใน ADR นี้ |
|---|---|---|
| Global State Management | Product Vision - UI/UX Requirements | Zustand for client state |
| Server State Synchronization | Acceptance Criteria - AC-UI-001 | TanStack Query for API data |
| Performance Optimization | Frontend Guidelines - Performance | Selective re-renders with Zustand |
| Type Safety | Engineering Guidelines - TypeScript | Full TypeScript support |
| Bundle Size Constraints | Architecture - Performance | Lightweight solutions (Zustand 1.2kb) |
แก้ไขความขัดแย้ง:
- Conflict: Complexity vs. Features (Redux vs. Zustand)
- Resolution: Chose Zustand + TanStack Query for simplicity and specialization
- Trade-off: Smaller ecosystem vs. Better developer experience
Impact Analysis
Affected Components (ส่วนประกอบที่ได้รับผลกระทบ):
| Component | ผลกระทบ | ความสำคัญ |
|---|---|---|
| Auth Store | User session management | 🔴 Critical |
| Notification Store | Global notifications | 🔴 Critical |
| UI Store | Theme and preferences | 🟡 Important |
| API Client | TanStack Query integration | 🔴 Critical |
| Form Components | React Hook Form + Zod | 🟡 Important |
| Server Components | Data fetching patterns | 🟡 Important |
| Component Structure | 'use client' directives | 🟡 Important |
| Testing Setup | Store testing patterns | 🟢 Guidelines |
| Documentation | State management patterns | 🟢 Guidelines |
Required Changes (การเปลี่ยนแปลงที่ต้องดำเนินการ):
Frontend (Next.js)
- Install Zustand and TanStack Query
- Create auth store with persistence
- Create notification store
- Create UI preferences store
- Setup Query Provider
- Update components to use stores
- Add 'use client' directives where needed
- Implement form validation with RHF + Zod
Architecture
- Define state management patterns
- Document when to use each solution
- Create store templates
- Setup testing utilities for stores
Context and Problem Statement
ระบบ LCBP3-DMS ต้องการจัดการ Global State เช่น User session, Notifications, UI preferences ต้องเลือก State Management solution ที่เหมาะสม
ปัญหาที่ต้องแก้:
- Global State: จัดการ State ที่ใช้ร่วมกันทั้งแอปอย่างไร
- Server State: จัดการข้อมูลจาก API อย่างไร
- Performance: หลีกเลี่ยง Unnecessary re-renders
- Type Safety: Type-safe state management
- Bundle Size: ไม่ทำให้ Bundle ใหญ่เกินไป
Decision Drivers
- ⚡ Performance: Minimal re-renders
- 📦 Bundle Size: เล็กที่สุด
- 🎯 Simplicity: เรียนรู้และใช้งานง่าย
- ✅ Type Safety: TypeScript support
- 🔄 Server State: จัดการ API data ได้ดี
Considered Options
Option 1: Redux Toolkit
Pros:
- ✅ Industry standard
- ✅ DevTools ดี
- ✅ Middleware support
Cons:
- ❌ Boilerplate มาก
- ❌ Bundle size ใหญ่ (~40kb)
- ❌ Complexity สูง
- ❌ Overkill สำหรับ App ส่วนใหญ่
Option 2: React Context API
Pros:
- ✅ Built-in (no dependencies)
- ✅ Simple
Cons:
- ❌ Performance issues (re-render ทั้ง tree)
- ❌ ไม่เหมาะสำหรับ Complex state
- ❌ ต้องจัดการ Optimization เอง
Option 3: Zustand
Props:
- ✅ Lightweight: ~1.2kb only
- ✅ Simple API: เรียนรู้ง่าย
- ✅ Performance: Selective re-renders
- ✅ TypeScript: Full support
- ✅ No boilerplate
- ✅ DevTools support
Cons:
- ❌ Smaller community กว่า Redux
Option 4: React Query (TanStack Query) for Server State
Pros:
- ✅ Specialized: จัดการ Server state ได้ดีที่สุด
- ✅ Caching: Auto cache management
- ✅ Refetching: Auto refetch on focus
- ✅ TypeScript: Excellent support
Cons:
- ❌ เฉพาะ Server state (ต้องใช้คู่กับ Client state solution)
Decision Outcome
Chosen Option: Zustand (Client State) + TanStack Query (Server State) + React Hook Form + Zod (Form State)
Rationale
For Client State (UI state, Preferences):
- Use Zustand - lightweight และเรียนรู้ง่าย
For Server State (API data):
- Use TanStack Query (React Query) สำหรับ data fetching, caching, synchronization
- Server Components สำหรับ initial data loading
For Form State:
- Use React Hook Form + Zod สำหรับ type-safe form management
Implementation Details
1. Install Zustand
npm install zustand
2. Create Global Store (User Session)
// File: lib/stores/auth-store.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface User {
user_id: number;
username: string;
email: string;
first_name: string;
last_name: string;
}
interface AuthState {
user: User | null;
token: string | null;
isAuthenticated: boolean;
// Actions
setAuth: (user: User, token: string) => void;
logout: () => void;
}
export const useAuthStore = create<AuthState>()(
persist(
(set) => ({
user: null,
token: null,
isAuthenticated: false,
setAuth: (user, token) =>
set({
user,
token,
isAuthenticated: true,
}),
logout: () =>
set({
user: null,
token: null,
isAuthenticated: false,
}),
}),
{
name: 'auth-storage', // LocalStorage key
}
)
);
3. Use Store in Components
// File: components/header.tsx
'use client';
import { useAuthStore } from '@/lib/stores/auth-store';
import { Button } from '@/components/ui/button';
export function Header() {
const { user, logout } = useAuthStore();
return (
<header className="flex justify-between items-center p-4">
<div>Welcome, {user?.first_name}</div>
<Button onClick={logout}>Logout</Button>
</header>
);
}
4. Notifications Store
// File: lib/stores/notification-store.ts
import { create } from 'zustand';
interface Notification {
id: string;
type: 'success' | 'error' | 'info';
message: string;
}
interface NotificationState {
notifications: Notification[];
addNotification: (notification: Omit<Notification, 'id'>) => void;
removeNotification: (id: string) => void;
clearAll: () => void;
}
export const useNotificationStore = create<NotificationState>((set) => ({
notifications: [],
addNotification: (notification) =>
set((state) => ({
notifications: [...state.notifications, { ...notification, id: Math.random().toString() }],
})),
removeNotification: (id) =>
set((state) => ({
notifications: state.notifications.filter((n) => n.id !== id),
})),
clearAll: () => set({ notifications: [] }),
}));
5. Server State with Server Components
// File: app/(dashboard)/correspondences/page.tsx
// Server Component - No state management needed!
import { getCorrespondences } from '@/lib/api/correspondences';
export default async function CorrespondencesPage() {
// Fetch directly on server
const correspondences = await getCorrespondences();
return (
<div>
<h1>Correspondences</h1>
{correspondences.map((item) => (
<div key={item.id}>{item.subject}</div>
))}
</div>
);
}
6. Client-Side Fetching (with TanStack Query)
npm install @tanstack/react-query
// File: components/correspondences/correspondence-list.tsx
'use client';
import { useQuery } from '@tanstack/react-query';
import { getCorrespondences } from '@/lib/api/correspondences';
export function CorrespondenceList() {
const { data, error, isLoading, refetch } = useQuery({
queryKey: ['correspondences'],
queryFn: getCorrespondences,
refetchInterval: 30000, // Auto refresh every 30s
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error loading data</div>;
return (
<div>
{data?.map((item) => (
<div key={item.id}>{item.subject}</div>
))}
<button onClick={() => refetch()}>Refresh</button>
</div>
);
}
7. UI Preferences Store
// File: lib/stores/ui-store.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface UIState {
sidebarCollapsed: boolean;
theme: 'light' | 'dark';
toggleSidebar: () => void;
setTheme: (theme: 'light' | 'dark') => void;
}
export const useUIStore = create<UIState>()(
persist(
(set) => ({
sidebarCollapsed: false,
theme: 'light',
toggleSidebar: () => set((state) => ({ sidebarCollapsed: !state.sidebarCollapsed })),
setTheme: (theme) => set({ theme }),
}),
{
name: 'ui-preferences',
}
)
);
State Management Patterns
When to Use Zustand (Client State)
✅ Use Zustand for:
- User authentication state
- UI preferences (theme, sidebar state)
- Notifications/Toasts
- Shopping cart (if applicable)
- Form wizard state
- Modal state (global)
When to Use Server Components (Server State)
✅ Use Server Components for:
- Initial data loading
- Static/semi-static data
- SEO-important content
- Data that doesn't need real-time updates
When to Use TanStack Query (Client-Side Server State)
✅ Use TanStack Query for:
- Real-time data (notifications count)
- Polling/Auto-refresh data
- User-specific data that changes often
- Optimistic UI updates
- Complex cache invalidation
- Paginated/infinite scroll data
Consequences
Positive Consequences
- ✅ Lightweight: Zustand ~1.2kb
- ✅ Simple: Easy to learn and use
- ✅ Performance: Selective re-renders
- ✅ No Boilerplate: Clean API
- ✅ Type Safe: Full TypeScript support
- ✅ Persistent: Easy LocalStorage persist
Negative Consequences
- ❌ Smaller Ecosystem: กว่า Redux
- ❌ Less Tooling: DevTools ไม่ครบเท่า Redux
Mitigation Strategies
- Documentation: Document common patterns
- Code Examples: Provide store templates
- Testing: Unit test stores thoroughly
ADR Review Cycle
Core Principle Review Schedule
- Review Frequency: ทุก 6 เดือน (กุมภาพันธ์ และ สิงหาคม)
- Trigger Events:
- Major version upgrade (v1.9.0, v2.0.0)
- Performance issues requiring state management changes
- New React/Next.js features affecting state
- Bundle size optimization requirements
Review Checklist
- State management patterns still meet requirements
- Bundle size within acceptable limits
- Performance metrics acceptable (re-render counts)
- Type safety maintained across stores
- Cross-document dependencies still valid
- New state management libraries to consider
- Testing coverage for stores adequate
Version Dependency Matrix
| System Version | ADR Version | Required Changes | Status |
|---|---|---|---|
| v1.8.0 - v1.8.5 | ADR-014 v1.0 | Base Zustand + TanStack Query setup | ✅ Complete |
| v1.9.0+ | ADR-014 v1.1 | Review bundle size and performance | 📋 Planned |
| v2.0.0+ | ADR-014 v2.0 | Consider new React state patterns | 📋 Future |
Related ADRs
- ADR-011: Next.js App Router - Server Components
- ADR-007: API Design
References
Document Version: v1.0 Last Updated: 2026-02-24 Next Review: 2026-08-01 (6-month cycle) Version Applicability: LCBP3 v1.8.0+
Change History
| Version | Date | Changes | Author |
|---|---|---|---|
| v1.0 | 2026-02-24 | Initial ADR creation with state management strategy | Frontend Team |
| v1.1 | 2026-04-04 | Added structured templates: Impact Analysis, Gap Linking, Version Dependency, Review Cycle | System Architect |