Guards
Guards live in <entity>.guards.ts and own pure domain evaluation. No React, no DB access, no side effects.
Responsibilities
Section titled “Responsibilities”Guards compute domain truth from plain input values.
- operation guards such as
canDelete<Entity>(),canUpdate<Entity>(),canSelect<Entity>() - field editability via
get<Entity>Editability() - richer derived state such as time state, date limits, delete modes, propagation categories, or update-option inputs
Guard Input Types
Section titled “Guard Input Types”Each entity should prefer operation-specific guard input types:
export interface EntityDeleteGuardInput { ... }export interface EntityEditGuardInput { ... }export interface EntityUpdateGuardInput { ... }Simple entities may use empty input types:
type EntityDeleteGuardInput = Record<string, never>;That still preserves the same public contract as complex entities.
Consolidated Evaluation
Section titled “Consolidated Evaluation”If multiple operations depend on the same facts, prefer one consolidated evaluator to prevent rule drift:
evaluateEntity(input) // returns guards, editability, and derived stateThis is the best pattern when delete, update, select, and editability all depend on the same underlying state.
Guard Output Rules
Section titled “Guard Output Rules”- guards should return domain truth, not UI instructions
- guards should not localize dialog copy
- guards should not read query state directly
- guards should not know about React components or mutation adapters
Good Current Examples
Section titled “Good Current Examples”work/time-entryuses a consolidatedevaluateWorkTimeEntry(...)work/projectexposes both delete warnings and field editabilityfinance/recurring-cashflowexposes richer derived state for update decisions