What you’ll learn
- How to define computed properties alongside schema and actions
- What computed is for — and what it must never do
- How computed differs from actions
- The boundary between computed and use-case logic
Defining computed
computed: {
displayName(props) { return props.username; },
isVerified(props) { return props.emailVerified; },
initials(props) { return props.username.slice(0, 2).toUpperCase(); },
}
Usage:
user.displayName; // 'alice' — not user.displayName()
user.isVerified; // false
user.initials; // 'AL'
They look like getters, work like getters, but are defined alongside your schema and actions.
What computed is FOR
- Formatting:
initials,fullName,abbreviatedAddress - Boolean checks:
isVerified,isExpired,hasChildren - Aggregations:
totalPrice = items.reduce(...),wordCount - Lookups:
primaryEmail = emails[0]
Keep them synchronous, pure, and fast. No I/O, no async, no mutation.
What computed is NOT for
❌ I/O in computed:
computed: {
avatarUrl(props) {
return s3.getSignedUrl(props.avatarKey); // ❌ runs on every access
},
}
Computed runs on every access. A network call here means your entity is making HTTP requests when you read a property. Generate signed URLs at the use-case level.
❌ Heavy computation:
computed: {
recommendationScore(props) {
return mlModel.predict(props.history); // ❌ CPU-heavy, runs every access
},
}
If the value is expensive, compute it once in the use case.
❌ Mutating state from computed:
computed: {
nextId(props) {
props.counter++; // ❌ computed should never mutate
return props.counter;
},
}
Computed receives a read-only snapshot. Use an action for mutations.
❌ Async computed — not supported:
computed: {
async recommendations(props) { ... }
}
Computed vs. action
| Computed | Action |
|---|---|
| Reads state, returns value | Reads state, mutates state |
| No side effects | Changes entity state |
Property access (user.isVerified) | Method call (user.actions.verifyEmail()) |
| Can’t reject or throw | Can throw to reject mutation |
❌ Using an action when computed would do:
actions: {
getDisplayName(state) { return state.username; }, // computed territory
}
❌ Using computed to enforce validation:
computed: {
isValid(props) {
if (props.items.length === 0) return false; // silently returns false
return true;
},
}
Don’t use computed for validation. Invariants (on Aggregate) or guard clauses (in actions) actively reject invalid states. Computed is passive.
What you’ve learned
Computed properties derive values from state. Defined alongside schema and actions in the entity config. Accessed as properties — no function call syntax.
Computed is synchronous and pure. No I/O, no async, no mutation. If you need a database call or heavy computation, do it in the use case and attach the result separately.
Computed ≠ validation. Computed reports state. Actions and invariants enforce
it. Don’t use isValid() to silently track broken state — throw in the action
that would cause the invalid state.
What’s next?
Computed properties derive values. Invariants enforce them. Invariants covers cross-field consistency rules — where they live, when they run, and why they’re the backbone of domain integrity.