CRUD with State Machines
A complete create/read/update/delete lifecycle using Grove's action-event model. Unlike traditional CRUD, every change produces immutable events, and state machines enforce valid transitions.
Prerequisites: 5-Minute Tutorial. What you'll learn: How to implement full CRUD operations with actions, events, and state machines.
The Pattern
In Grove, CRUD maps to:
- Create →
@createaction that emits an initial event - Read → The record itself (derived from events)
- Update → Actions that emit events for specific changes
- Delete →
@deleteaction that emits a delete event
Complete Example
module article
enum ArticleStatus { draft, published, archived }
record {
kind "post"
title: String
body: String
status: ArticleStatus = ArticleStatus.draft
author_id: Id
invariant title_not_blank {
record.title != ""
}
state status {
ArticleStatus.draft -> ArticleStatus.published on published
ArticleStatus.published -> ArticleStatus.archived on archived
ArticleStatus.archived -> ArticleStatus.draft on restored
}
}
// Events
event created {
title: String
body: String
author_id: Id
}
event title_updated {
title: String
}
event body_updated {
body: String
}
event published {
status: ArticleStatus = ArticleStatus.published
}
event archived {
status: ArticleStatus = ArticleStatus.archived
}
event restored {
status: ArticleStatus = ArticleStatus.draft
}
@delete
event deleted {}
// Validators
validate title_not_blank {
params.title != ""
}
// Actions
@create
action create {
title: String
body: String
author_id: Id
validate title_not_blank
apply {
emit created {
title: params.title
body: params.body
author_id: params.author_id
}
}
}
action update_title {
title: String
validate title_not_blank
apply {
emit title_updated { title: params.title }
}
}
action update_body {
body: String
apply {
emit body_updated { body: params.body }
}
}
action publish {
apply {
emit published {}
}
}
action archive {
apply {
emit archived {}
}
}
action restore {
apply {
emit restored {}
}
}
action delete {
apply {
emit deleted {}
}
}
Key Takeaways
- Separate events per field change --
title_updatedandbody_updatedrather than a generic "updated" event. This gives you a precise audit trail. - State machine guards transitions -- You can't archive a draft or publish an archived article without restoring it first.
@deleteis a soft delete -- The record is marked deleted but events are preserved. Use@delete(hard)for permanent removal.
See Also
- Validation & State Machines -- Deep dive into constraints
- API Calls with Retries -- External service integration