Content: computed fields and rollups, stored and kept in step
- Dominant language
- C#
- Stars
- 6
- Forks
- 7
- Avg merge
- 4h 42m
- Merged PRs (30d)
- 307
Description
Comes up with almost every client: hours on a time entry, a line total on an order, a teacher's hours this month, an invoice's balance. Today a value like that is typed in by hand or written by a workflow after the save, so it can be wrong or stale, and a workflow cannot compute one at all yet (#575).
## Change
**Computed fields.** A field declares how it is computed from other fields of the same entry:
```json
{ "name": "Hours", "type": "decimal", "computed": { "fn": "hours", "args": ["TimeIn", "TimeOut"], "round": 2 } }
```
- Computed inside the write transaction, stored, so it can be filtered, sorted and delivered like any field.
- Recomputed whenever an input changes, including through a transition.
- Read only: a write that sets it is refused with a 400 naming the field.
- A fixed list of functions, the same one the workflow `calculate` step uses (#575): `add`, `subtract`, `multiply`, `divide`, `round`, `min`, `max`, `hours`, `days`, `duration`, `concat`, `coalesce`, `if` over a choice value (#803). No expression language and no code.
- Validated when the type is saved: inputs exist, types fit the function, no field computes from itself or through a cycle.
**Rollups.** A field summarising related entries that reference this one:
```json
{ "name": "HoursThisMonth", "type": "decimal", "rollup": { "from": "timeEntry", "via": "Teacher", "fn": "sum", "field": "Hours", "where": { "createdAt": "thisMonth" } } }
```
- `count`, `sum`, `min`, `max`, `avg`, over one reference hop.
- Updated when a related entry is created, changed or removed, asynchronously after commit, and marked with the time it was last updated, so a reader knows it can lag by seconds.
- A rollup over Sensitive fields is only visible to roles that may read those fields.
**Adding one later.** Adding a computed or rollup field to a type with entries fills existing entries in the background, with progress visible, instead of leaving them empty.
## Done when
- A time entry's Hours is correct after time out, changes when TimeOut is edited, and a request setting Hours directly is a 400.
- A teacher's HoursThisMonth reflects a new time entry within the lag, and a test proves concurrent time entries sum correctly.
- A cycle between two computed fields is refused when the type is saved.
## Where it lives
Core: field definitions, the write path and the rollup projection. A computed value that a module could skip would not be a rule.
Contributor guide
Research direction
Begin by locating the core field-definition, write-path, and rollup-projection entry points; the issue does not name specific files or tests. Use the listed done conditions as the acceptance checks: recomputation and read-only enforcement, concurrent rollup correctness, and cycle rejection when a type is saved.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend, databases
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100