مدیریت State در فرانت‌اند؛ معماری، ابزار و الگوی انتخاب

مدیریت State در فرانت‌اند از انتخاب Redux یا Context شروع نمی‌شود. ابتدا باید بدانید هر داده متعلق به کجاست، چه کسی آن را تغییر می‌دهد، تا چه زمانی معتبر است و در تعارض میان Browser، Server، Tab دیگر یا پاسخ دیررس کدام نسخه برنده است. اگر این قرارداد روشن نباشد، هر Store فقط آشفتگی را در یک مکان متمرکز می‌کند.

یک Filter قابل اشتراک بهتر است در URL باشد؛ وضعیت بازبودن Modal محلی است؛ Product list نسخه Cacheشده از حقیقت Server است؛ Draft شاید نیاز به Persistence داشته باشد؛ Checkout یک Workflow با Transitionهای محدود است. این راهنما State را طبقه‌بندی می‌کند، مدل سازگاری می‌سازد و بعد ابزار مناسب React، Vue یا معماری دیگر را انتخاب می‌کند.

دادهمالک محتملانتخاب اولیهخطای رایج
بازبودن DropdownComponentLocal stateقرار دادن در Store سراسری
Filter و Page نتایجNavigation/URLQuery parameterاز دست‌رفتن Back/Share/Refresh
فهرست سفارش‌هاServerQuery cacheCopy در Global store و Cache دوگانه
سبد خریدبسته به محصول؛ Server یا Client+syncDomain contractاعتماد به Total سمت Client
مرحله پرداختWorkflow/ServerState machine + persisted order stateچند Boolean متناقض

State چیست و چه چیزی State نیست؟

State حداقل اطلاعاتی است که برای توصیف وضعیت فعلی سیستم و تعیین رفتار بعدی لازم است. هر مقدار محاسبه‌شده لزوماً State نیست. اگر fullName از firstName و lastName قابل محاسبه است، ذخیره هر سه، احتمال ناسازگاری می‌سازد.

راهنمای رسمی React برای ساختار State بر گروه‌بندی داده‌های مرتبط و حذف State متناقض، تکراری، زائد و بیش‌ازحد تودرتو تأکید می‌کند. این اصول به React محدود نیستند.

مفهومتعریفمثال
Source stateاطلاعات حداقلی مستقلselectedId
Derived valueاز Source در Render/Selector محاسبه می‌شودSelected product
Eventچیزی که رخ دادهcheckout/submitted
Effectتعامل با سیستم بیرونیRequest، Storage، Analytics
Cacheنسخه قابل‌حذف و بازسازی از Source دیگرQuery result

مرز این مفاهیم از نام کتابخانه مهم‌تر است. وقتی Effect داخل Reducer، Cache به‌عنوان Truth یا Derived value به‌عنوان Source ذخیره شود، Debug دشوار می‌شود.

یک Source of Truth برای هر State، نه یک Store برای همه

«Single source of truth» یعنی برای هر قطعه State یک Owner روشن وجود دارد؛ نه اینکه تمام State برنامه باید داخل یک Object سراسری باشد. React نیز State مشترک را به نزدیک‌ترین والد مشترک Lift می‌کند و تصریح می‌کند Stateهای مختلف می‌توانند در مکان‌های مختلف زندگی کنند.

State را تا جای ممکن نزدیک مصرف‌کننده نگه دارید. بالا بردن Scope هزینه Subscription، Coupling، تست و احتمال تغییر ناخواسته را زیاد می‌کند. تنها وقتی چند شاخه واقعاً هماهنگی می‌خواهند، Owner را بالا ببرید.

هشت نوع State در اپلیکیشن وب

نوعنمونهLifetimeابزار اولیه
Ephemeral UIModal، hover، accordionComponent/session کوتاهLocal state
URL/navigationFilter، search، tab، pageHistory/share/refreshRouter و URL
Form/draftField، touched، error، stepتا submit یا resumeForm/local reducer
Server stateProduct، order، profileCache با freshnessQuery/cache layer
Client domainBuilder canvas، انتخاب پیچیدهSession/featureReducer/store/state machine
Session/auth projectionUser، capability، expirySessionServer session + client projection
Persisted/offlineDraft، outbox، preferenceبین reload/device محدودStorage/IndexedDB + migration
External environmentOnline status، media query، browser APIخارج React/VueExternal-store adapter

یک ویژگی می‌تواند چند نوع داشته باشد. Search page ممکن است Query در URL، نتایج در Server cache، focus محلی و Recent searches در Persistence داشته باشد. آن‌ها را یک Object «searchState» نکنید مگر Update و Lifetime مشترک داشته باشند.

State contract؛ قبل از انتخاب ابزار

فیلدپرسشمثال
Identityاین State دقیقاً چه چیزی را توصیف می‌کند؟Order ID و Version
OwnerBrowser، Server، URL یا Worker؟Server مالک Payment status
ScopeComponent، Route، Tab، User یا Device؟Filter در Route
LifetimeRender، Session، Reload یا Offline؟Draft هفت روز
Initial sourceاز کجا Boot می‌شود؟SSR payload یا API
Mutationچه Event/Commandی تغییر می‌دهد؟Quantity changed
ConsistencyStrong، eventual یا optimistic؟Stock نیاز به Server confirm
Freshnessچه زمانی Stale می‌شود؟Price پس از ۳۰ ثانیه/رویداد
Concurrencyپاسخ دیررس/دو Tab/دو Device؟Version conflict
Securityآیا Sensitive یا Authoritative است؟Access token در State ممنوع
FailureRetry/Rollback/Recovery چیست؟Optimistic rollback
Evidenceچه Event/Metricی Debug می‌کند؟Mutation ID و cache invalidation

اگر تیم درباره Owner یا Consistency جواب متفاوت می‌دهد، افزودن کتابخانه هنوز زود است.

نردبان تصمیم: ساده‌ترین مکان معتبر را انتخاب کنید

  1. آیا قابل محاسبه است؟ State نسازید؛ Derived value/Selector بسازید.
  2. آیا Server/Framework آن را مالک است؟ دوباره در Client کپی نکنید.
  3. آیا باید Share/Bookmark/Back شود؟ URL.
  4. آیا فقط یک Component نیاز دارد؟ Local state.
  5. آیا چند Component نزدیک هماهنگ‌اند؟ Lift state.
  6. آیا Transitionهای پیچیده در یک Feature دارید؟ Reducer/state machine.
  7. آیا Server data با Cache/freshness است؟ Query layer.
  8. آیا چند Feature دور، Subscription دقیق و DevTools می‌خواهند؟ External/global store.

Props drilling همیشه مشکل نیست؛ Prop صریح dependency را آشکار می‌کند. Context نیز «State manager» کامل نیست؛ کانال توزیع Value است و Scope/Update آن باید طراحی شود.

State محلی، Lift و Context در React

برای هماهنگی دو Component، ابتدا State را به نزدیک‌ترین والد مشترک Lift کنید. اگر Tree عمیق شد، Reducer و Context در مستندات React منطق Transition و توزیع State/Dispatch را جدا می‌کنند.

الگومناسبهشدار
Local stateUI مستقل و موقتDuplicate در sibling
Lift stateچند Component نزدیکOwner بیش‌ازحد بالا
ReducerTransitionهای مرتبط و قابل تستEffect داخل Reducer
ContextValue در SubtreeProvider بزرگ و update پر频
External storeچند Feature/خارج ReactGlobal dumping ground

برای Store خارج React، useSyncExternalStore API رسمی Subscription به External store است و برای Server rendering نیز snapshot جدا می‌خواهد. Subscription دستی با Effect ممکن است در Concurrent rendering snapshot ناسازگار بدهد.

Server State با Global Client State یکی نیست

Server state Remote، Shared، Async و ممکن است بدون اطلاع Client تغییر کند. علاوه بر Value، Loading، Error، freshness، retry، cancellation، pagination، invalidation و garbage collection دارد. Copy پاسخ API در Store عمومی معمولاً شما را وادار می‌کند Cache را دوباره و ناقص بسازید.

ویژگیClient/UI StateServer State
AuthorityBrowser/featureServer
Syncهمان RuntimeNetwork و async
Freshnessتا Event محلیStale/invalidate/refetch
Concurrencyمحدود به UIClientها و Workerهای دیگر
FailureValidation/logicTimeout، retry، partial، offline
LifecycleComponent/routeCache/subscriber/GC

در Redux، راهنمای رسمی Redux Redux Toolkit را ابزار پیشنهادی و RTK Query را مسیر پیش‌فرض Fetch/Cache در اپ Redux معرفی می‌کند. این Recommendation مخصوص اکوسیستم Redux است؛ به معنی نیاز همه پروژه‌ها به Redux نیست.

Cache key، Freshness و Invalidation

Cache key هویت Data است. اگر Query function به status، page و market وابسته است، Key باید آن‌ها را نمایندگی کند. مستندات TanStack Query نیز متغیرهای مؤثر بر Query را بخشی از Query key می‌داند.

قرارداد CacheسؤالFailure
KeyTenant/User/Locale/Filter/Version در Identity هستند؟نشت یا داده اشتباه
Freshnessچه مدت داده Fresh است؟Refetch زیاد یا اطلاعات کهنه
Invalidationکدام Mutation/Event چه Keyهایی را stale می‌کند؟UI ناسازگار
Retention/GCCache بدون subscriber تا کی؟Memory growth
Retryکدام خطا، چند بار و با چه Backoff؟Request storm
CancellationResponse قدیمی چگونه بی‌اثر می‌شود؟Race و overwrite

Important Defaults رسمی TanStack Query یادآوری می‌کند Queryها به‌طور پیش‌فرض Stale در نظر گرفته می‌شوند و Retry/GC رفتارهای پیش‌فرض دارند. Default را Contract محصول فرض نکنید؛ برای Price، Profile و Static catalog نیازهای متفاوت تعریف کنید.

برای تفاوت Cache با Source of truth، Invalidation، Stale budget و Stampede، راهنمای معماری Cache در وب را ببینید.

Form State و Draft؛ یک Domain مستقل

Form فقط Map فیلدها نیست. Raw value، parsed value، touched/dirty، sync/async validation، submit status، server error، draft version و accessibility دارد. همه فرم را Global نکنید؛ Scope معمولاً Route/Feature است.

قرارداد Submit

  • Client validation برای UX است؛ Server دوباره اعتبارسنجی می‌کند؛
  • Double submit با disabled UI به‌تنهایی حل نمی‌شود؛ Idempotency سمت Server لازم است؛
  • Server error را به Field/Form/Global درست Map کنید؛
  • Draft persistence باید Schema version و Migration داشته باشد؛
  • بعد از Success، Reset فقط State متعلق به همان Form را پاک کند؛
  • Navigation با Unsaved changes باید Recovery و Accessibility روشن داشته باشد.

URL State؛ State قابل اشتراک و قابل بازگشت

Search، Sort، Filter، Pagination، selected tab و گاهی Modal قابل لینک باید در URL قرار گیرند. مزیت: Refresh، Back/Forward، Bookmark، Share، Analytics و SSR قابل پیش‌بینی‌تر می‌شوند.

قاعدهنمونههشدار
Canonical serializationترتیب و Default ثابتURLهای معادل متعدد
ValidationPage عدد مثبت، sort از enumاعتماد به Query خام
Replace vs pushTyping شاید replace؛ submit pushHistory پر از هر keystroke
Sensitive dataهرگز Token/PII در URLLog، Referrer و Share
UnicodeUTF‑۸ و normalizationفارسی/نیم‌فاصله معادل‌های متفاوت

URL را هم State بی‌اعتماد ورودی بدانید: Parse، Validate و Default کنید. Server query را صرفاً با String کلاینت اجرا نکند.

Derived State، Selector و Effectهای غیرضروری

Derived data را هنگام Render یا با Selector محاسبه کنید. استفاده از Effect برای Sync کردن دو State داخلی، یک Render اضافی و Window ناسازگاری می‌سازد. مستندات React درباره Effectهای غیرضروری نیز محاسبه داده برای Render و Event handling را از Effect بیرونی جدا می‌کند.

Memoization فقط وقتی لازم است که محاسبه گران یا Reference stability برای Subscription/child مهم باشد. Memoize همه Selectorها می‌تواند Memory و پیچیدگی را زیاد کند. ابتدا Profile کنید.

ساختار State؛ Normalization و Impossible state

داده تکراری را با ID reference و Entity map مدیریت کنید، به‌خصوص وقتی یک Entity در چند List دیده می‌شود. اما Response ساده و کوچک را بی‌دلیل شبیه Database نکنید.

چند Boolean می‌توانند حالت ناممکن بسازند:

{ isIdle, isSubmitting, isPaid, hasError }

به‌جای ترکیب‌های متناقض، State را به Union/State machine تبدیل کنید:

idle → submitting → awaiting_payment → paid
                  ↘ failed → retrying

Transition باید Event، Guard و Effect مشخص داشته باشد. برای Checkout، Onboarding، Upload و Approval، State machine اغلب از Store عمومی قابل توضیح‌تر است.

Optimistic Update؛ سرعت ادراکی با قرارداد Rollback

Optimistic update قبل از تأیید Server UI را تغییر می‌دهد. برای Like ساده مناسب‌تر از انتقال پول یا موجودی بحرانی است. چهار چیز لازم است: mutation identity، snapshot/patch، rollback/reconcile و نمایش وضعیت Pending.

مرحلهکارریسک
PrepareValidation و mutation IDDuplicate
Optimistic applyPatch محلی و Pending markerUI ادعای قطعی
Server commitIdempotency/version checkConflict
SuccessReconcile با Response authoritativeTemporary ID mismatch
FailureRollback یا refetch و Error قابل اقدامSilent divergence

Rollback به «برگرداندن کل Cache» محدود نیست؛ ممکن است Mutation دیگر بعد از آن آمده باشد. Patch معکوس، Version و refetch هدفمند را متناسب با Concurrency طراحی کنید.

Race condition و پاسخ دیررس

کاربر «تهران» را جست‌وجو و بلافاصله «شیراز» را وارد می‌کند؛ پاسخ تهران دیرتر می‌رسد و Result جدید را overwrite می‌کند. راه‌حل می‌تواند Cancellation، request ID، sequence، query key جدا یا بررسی latest intent باشد.

Raceکنترل
Out-of-order responseAbort/sequence/latest-intent guard
Double submitIdempotency key و server dedupe
Stale closureUpdater/event design و dependency درست
Two-tab editVersion/ETag، conflict UI و channel sync
Background refetch vs editDraft/source separation و merge policy

Realtime State؛ Event ordering، Dedupe و Resync

WebSocket/SSE State را «خودکار Real-time» نمی‌کند. Connection ممکن است قطع، Event تکراری، جاافتاده یا خارج ترتیب باشد. Event contract شامل ID، entity ID، version/sequence، timestamp، type و payload schema لازم است.

  1. Snapshot اولیه بگیرید؛
  2. از Cursor/Version مشخص subscribe کنید؛
  3. Event را Dedupe و ترتیب را Validate کنید؛
  4. Gap را تشخیص و Resync کنید؛
  5. پس از reconnect، Subscription و State را Reconcile کنید.

اگر Event فقط «چیزی تغییر کرد» می‌گوید، Query مربوط را Invalidate کنید. اگر Patch کامل و Versioned است، Cache را دقیق Update کنید.

Persistence و localStorage؛ Convenience با ریسک

localStorage میان Sessionهای Browser باقی می‌ماند و API آن Synchronous است؛ MDN رفتار آن را برای Origin توضیح می‌دهد. برای Payload بزرگ یا Query زیاد، IndexedDB معمولاً مناسب‌تر است.

ریسککنترل
XSS به Storage دسترسی داردToken/Secret/PII حساس را ذخیره نکنید؛ CSP و AppSec
Schema تغییر می‌کندVersion، migration و fallback پاک‌سازی
State کهنه استTimestamp/TTL و server reconcile
Quota/Private modeError handling و optional persistence
چند Tabstorage event/BroadcastChannel و conflict policy
User مشترک روی DeviceNamespace per account و پاک‌سازی Logout

Persist کردن کل Store آسان ولی خطرناک است: Loading/Error، Cache، Feature flag و Data کاربر قبلی را برمی‌گرداند. فقط State ضروری و Versioned را Allowlist کنید.

Auth و Session State؛ Client مرجع مجوز نیست

UI می‌تواند Capability را برای نمایش مناسب نگه دارد، اما Server باید Authentication و Authorization هر درخواست حساس را اجرا کند. isAdmin=true در Store مجوز نیست. Price، Discount، Stock و Order total نیز باید Server-side تأیید شوند.

  • Session expiry و refresh/re-auth را مدل کنید؛
  • Logout را در Tabها و Deviceهای لازم propagate کنید؛
  • User switch باید Cache و persisted namespace را جدا کند؛
  • خطای ۴۰۱، ۴۰۳ و Network error را یکی نکنید؛
  • Token را در URL، Log، Redux DevTools یا persisted store نگذارید؛
  • Hydration نباید Data کاربر دیگر را از Cache مشترک دریافت کند.

SSR، Server Components و Hydration

در SSR، State اولیه روی Server ساخته و روی Client Hydrate می‌شود. خطرها: Cache مشترک میان Requestها، Timestamp/Freshness متفاوت، Data غیرقابل Serialization، XSS در JSON و markup mismatch.

قرارداد SSRقاعده
Request isolationCache/Store User-specific per request
SerializationEscape امن و type mapping صریح
OwnershipServer-rendered data را بی‌دلیل دوباره Client مالک نکند
FreshnessFetched-at/stale policy مشترک
HydrationServer/client snapshot سازگار
ErrorCritical ۴۰۴/۵۰۰ با Loading silent یکی نشود

راهنمای Advanced SSR در TanStack Query نیز بر Data ownership و Revalidation در Server Components و Hydration تأکید دارد. Framework و Version هدف را در Decision record ثبت کنید؛ الگوها با Rendering model تغییر می‌کنند.

Offline و PWA؛ Outbox و Conflict بخشی از State هستند

Offline-first فقط Persist کردن Cache نیست. باید Intentهای Write در Outbox ذخیره، ترتیب/Idempotency مدیریت، Connectivity تغییر و Conflict حل شود.

جزءوظیفه
Local snapshotRead در Offline با Freshness label
OutboxCommandهای Pending با ID و retry state
Sync engineBackoff، ordering، auth refresh و resume
Conflict policyServer wins، client wins، merge یا user choice
ReconciliationTemporary ID، version و authoritative response

برای Service Worker، Update lifecycle، Cache و SEO/Accessibility در PWA، راهنمای تصمیم و اجرای PWA مکمل این بخش است.

Performance؛ Subscription را اندازه بگیرید

Store به‌خودی‌خود Performance را بهتر نمی‌کند. Context بزرگ، Selector ناپایدار، Object جدید، Subscription وسیع و update پر頻 می‌توانند Render اضافی بسازند. در مقابل، بهینه‌سازی زودهنگام State را تکه‌تکه و Debug را سخت می‌کند.

Metricسؤالابزار
Commit/render durationکدام update گران است؟Framework profiler
Render countآیا Component بدون data change render شد؟Profiler/instrumentation
Selector costمحاسبه یا allocation زیاد؟CPU profile
Store update rateEvent high-frequency کجاست؟Custom metric
Memory/cache sizeGC/retention درست است؟Heap/cache inspector
Interaction latencyکاربر چه می‌بیند؟RUM/INP و trace

برای پیوند Performance فنی به UX و Outcome، راهنمای سرعت سایت، UX و تبدیل را ببینید.

مقایسه ابزارها براساس مسئله، نه محبوبیت

دستهنمونهنقطه قوتهزینه/ریسک
Framework localReact state/reducer، Vue reactiveکمترین dependency و co-locationCross-tree coordination
Context/ProvideReact Context، Vue provide/injectDistribution در SubtreeUpdate scope و hidden dependency
Structured global storeRedux Toolkit، PiniaConvention، DevTools، team scaleBoilerplate/centralization
Minimal/atomic storeZustand، Jotai و مشابهAPI کوچک و subscription granularGovernance/fragmentation
Server cacheTanStack Query، RTK Query، SWR، ApolloFreshness/invalidation/async lifecycleCache contract و defaults
State machineLibrary یا reducer صریحTransition/guard/impossible-stateمدل‌سازی و یادگیری

مستندات رسمی Vue برای برنامه‌های جدید Pinia را توصیه می‌کند و SSR را یکی از دلایل نیاز به الگوی State management قوی‌تر می‌داند. Recommendation هر اکوسیستم را در Version جاری بررسی کنید؛ «Redux پادشاه» یا «Zustand همیشه سریع‌تر» معیار معماری نیست.

معیار انتخاب ابزار State

معیارسؤال Pilot
State fitUI، server cache، workflow یا external store؟
Rendering modelCSR/SSR/streaming/server components؟
SubscriptionGranularity و concurrency semantics؟
Async modelCancellation/retry/invalidation/optimistic؟
Type safetyEvent/state impossible چگونه رد می‌شوند؟
DebugTimeline، reason، replay و redaction؟
TestReducer/selector/integration بدون UI؟
Bundle/runtimeهزینه واقعی روی route هدف؟
TeamConvention، onboarding و ownership؟
ExitDomain logic از API کتابخانه جداست؟

دو Thin slice انتخاب کنید: یک Server query با optimistic mutation و یک Workflow چندمرحله‌ای. با داده واقعی، SSR/offline در صورت نیاز و ابزار Debug تیم Pilot کنید.

معماری نمونه فروشگاه ایرانی

StateOwner/محلنکته ایران
Query «کفش ورزشی» و FilterURLUnicode/نیم‌فاصله و share
Product/price/stockServer query cacheتومان/ریال فقط نمایش؛ مبلغ authoritative Server
Cart drawer openLocal UIPersist لازم نیست
Cart itemsServer یا client projection با syncStock/price در Checkout دوباره تأیید
Address draftForm + optional encrypted/limited persistencePII و Device مشترک
PaymentServer order state machinePending/unknown/callback duplicate/reconciliation
ConnectivityExternal/browser signalNetwork متغیر؛ offline label

کاربر بعد از بازگشت از درگاه نباید فقط براساس Query string «موفق» ببیند. Client با Order ID وضعیت Server را می‌خواند؛ Pending را مدل و Callback/Query تکراری را Reconcile می‌کند.

امنیت و حریم خصوصی State

  • Client state را ورودی بی‌اعتماد بدانید؛ Authorization و Price سمت Server؛
  • Token/Secret/PII را در URL، Log، DevTools snapshot یا persistence نگذارید؛
  • Cache key باید Tenant/User boundary را رعایت کند؛
  • SSR cache را میان Userها Share نکنید؛
  • State debug export قبل از ارسال به Support Redact شود؛
  • Logout، account switch و consent withdrawal State مربوط را پاک/Invalidate کنند؛
  • Third-party store/plugin را از نظر supply chain و update بررسی کنید.

برای BOLA، Token، OAuth، Webhook و Server-side authorization، راهنمای امنیت API را ببینید.

تست State؛ Transition و Concurrency را هدف بگیرید

لایهآزمون
UnitReducer/selector pure، transition و invariant
ContractAPI schema، error mapping و version
IntegrationQuery→mutation→invalidate/reconcile
Concurrencyout-of-order، duplicate، cancel و two-tab
SSR/Hydrationrequest isolation، user switch و mismatch
Offlinereload، reconnect، outbox replay و conflict
E2EJourney واقعی با slow/failed network

Snapshot test بزرگ Store معمولاً دلیل Failure را پنهان می‌کند. Event و Invariant را تست کنید: «Paid هرگز به Submitting برنمی‌گردد» یا «Result مربوط به Query قبلی UI جدید را overwrite نمی‌کند».

Observability و Debug بدون نشت داده

Eventهای Domain، mutation ID، query key امن، cache hit/stale/refetch، transition، rollback، conflict و hydration error را قابل مشاهده کنید. Payload حساس را ثبت نکنید؛ Entity ID را در صورت نیاز Pseudonymize کنید.

Signalسؤال
Mutation failure/rollbackکدام Action و Error class؟
Invalidation/refetchچرا و چند Request ساخت؟
Outbox depth/ageOffline writeها گیر کرده‌اند؟
Conflict rateکدام Entity/Journey؟
Hydration mismatchServer/client owner یا serialization؟
Render/interactionState update چه اثر UX داشت؟

برای Correlation، Sampling و کنترل Cardinality، راهنمای Observability سایت مکمل است.

مهاجرت از Store سراسری بدون بازنویسی بزرگ

  1. Inventory sliceها و Consumer/Writerها را بسازید؛
  2. هر State را طبقه‌بندی و Owner/Source of truth را مشخص کنید؛
  3. Derived data را حذف و Selector بسازید؛
  4. Server data را Feature-by-feature به Query layer منتقل کنید؛
  5. URL state را از Store بیرون ببرید؛
  6. UI local را به Component/Feature برگردانید؛
  7. Workflow را با Reducer/state machine مرزبندی کنید؛
  8. Adapter بسازید تا Consumerها یک‌باره تغییر نکنند؛
  9. Metric/Tests را قبل و بعد مقایسه و Slice قدیمی را حذف کنید.

Dual source of truth طولانی خطرناک است. برای هر مرحله یک Writer اصلی و تاریخ حذف Bridge تعیین کنید. Release را با CI/CD، Feature flag و Rollback کنترل کنید؛ راهنمای CI/CD امن الگوی Delivery را تکمیل می‌کند.

برنامه ۳۰روزه بهبود State

بازهخروجیGate
روز ۱ تا ۵Inventory، State type، Owner و duplicate mapCrown bugها و Source روشن
روز ۶ تا ۱۰State contract و query/cache policyFreshness/concurrency/security تعریف
روز ۱۱ تا ۱۵Derived/URL/local extractionTest و metric بدون Regression
روز ۱۶ تا ۲۲Server cache/optimistic/realtime contractFailure/reconcile test پاس
روز ۲۳ تا ۲۷SSR/offline/persistence hardeningIsolation/migration/security پاس
روز ۲۸ تا ۳۰Decision record، conventions و roadmapOwner و حذف legacy مشخص

اشتباه‌های رایج مدیریت State

  • Global store برای همه‌چیز: Scope و Lifetimeهای متفاوت مخلوط می‌شوند.
  • Copy کردن Server response: Cache دوگانه و Invalidation ناقص می‌سازد.
  • ذخیره Derived state: Sourceها از هم جدا می‌شوند.
  • Effect برای Sync داخلی: Render اضافه و حالت واسط ناسازگار.
  • چند Boolean برای Workflow: Impossible state تولید می‌شود.
  • Persist کل Store: Token، Cache، User قبلی و Schema کهنه برمی‌گردند.
  • Optimistic بدون Rollback: UI و Server واگرا می‌شوند.
  • Realtime بدون Version/Resync: Event گم‌شده دیده نمی‌شود.
  • SSR cache مشترک: خطر نشت داده میان Requestها.
  • انتخاب با محبوبیت: مسئله State و Rendering model فراموش می‌شود.
  • Memoization همه‌جا: پیچیدگی و Memory بدون Evidence.
  • Client به‌عنوان مرجع امنیت: Authorization/Price قابل دست‌کاری می‌شود.

سؤالات متداول درباره مدیریت State

چه زمانی به کتابخانه مدیریت State سراسری نیاز داریم؟

وقتی State واقعاً میان Featureهای دور مشترک است، Transition/Subscription و Debug قراردادی می‌خواهد و Local/Lift/URL/Query cache کافی نیست. Props drilling به‌تنهایی دلیل قطعی نیست؛ Context یا Composition شاید مسئله را حل کند.

Redux بهتر است یا Context و Zustand؟

«بهتر» بدون نوع State معنی ندارد. Context کانال توزیع در Subtree، Redux Toolkit Store ساختاریافته با Convention/DevTools و ابزارهای مینیمال API/Subscription متفاوت دارند. Workload، SSR، async، team و Pilot را مقایسه کنید.

آیا داده API را داخل Redux یا Store عمومی بگذاریم؟

اغلب Query/cache layer مناسب‌تر است چون Freshness، retry، invalidation و GC را مدل می‌کند. اگر Redux دارید، RTK Query مسیر رسمی پیشنهادی همان اکوسیستم است. Domain client-only یا workflow ممکن است همچنان در Store بماند.

آیا localStorage برای سبد خرید و Login امن است؟

برای Token/Secret مناسب نیست چون JavaScript و XSS می‌توانند آن را بخوانند. Cart ساده می‌تواند Projection محلی Versioned باشد، اما Price/Stock/Discount/Order باید Server-side تأیید و پس از Login/Tab sync Reconcile شوند.

چگونه از باگ Stateهای متناقض جلوگیری کنیم؟

State حداقلی، حذف duplicate/derived data، Union یا state machine برای Workflow، Eventهای صریح، Reducer pure، Version/Idempotency برای async و تست Invariant/Concurrency بهترین پایه‌اند.

جمع‌بندی: ابزار آخرین تصمیم است

مدیریت State خوب از Ownership و Consistency شروع می‌شود. برای هر داده Identity، Owner، Scope، Lifetime، Mutation، Freshness، Concurrency، Security و Failure را تعریف کنید؛ سپس آن را در ساده‌ترین محل معتبر—Render، Server، URL، Component، Query cache، Reducer یا Store—قرار دهید. این کار هم باگ را کم می‌کند و هم مهاجرت ابزار را ساده‌تر نگه می‌دارد.

اگر برای Audit State، طراحی Cache/SSR/Offline یا انتخاب و مهاجرت Store نیاز به همراهی دارید، از فرم درخواست مشاوره مایندیو معماری، Framework، Journeyهای حساس و نمونه باگ‌های فعلی را بفرستید.

مطالب مرتبط

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *