| name | state change slice |
| description | Knows how to build state change slices |
STATE_CHANGE Slice Skill
Overview
A STATE_CHANGE slice represents a business operation that modifies aggregate state through commands and events. This is the core CQRS/Event Sourcing pattern.
Input Structure
{
"sliceType": "STATE_CHANGE",
"title": "slice: <Name>",
"context": "<BoundedContext>",
"commands": [...],
"events": [...],
"readmodels": [...],
"screens": [...],
"processors": [...],
"specifications": [...]
}
Flow Pattern
Screen (UI) → Command → Aggregate (@CommandHandler) → Event → ReadModel (via @EventHandler Projector)
↓
Automation (if processors exist)
Code Generation Steps
Step 1: Create Command
Location: src/main/kotlin/de/alex/<module>/domain/commands/<commandname>/<CommandName>Command.kt
Template:
package de.alex.<module>.domain.commands.<commandname>
import de.alex.common.Command
import org.axonframework.modelling.command.TargetAggregateIdentifier
import java.util.UUID
data class <CommandName>Command(
@TargetAggregateIdentifier var aggregateId: UUID,
) : Command
Field Mapping Rules:
| Slice Type | Kotlin Type |
|---|
| UUID | UUID |
| String | String |
| Date | LocalDate |
| Integer | Int |
| Boolean | Boolean |
| Decimal | BigDecimal |
| Multiple cardinality | List |
| optional: true | Nullable (T?) |
Step 2: Create Event(s)
Location: src/main/kotlin/de/alex/events/<EventName>Event.kt
Template:
package de.alex.events
import de.alex.common.Event
import java.util.UUID
data class <EventName>Event(
var aggregateId: UUID,
) : Event
Step 3: Add CommandHandler to Aggregate
Location: src/main/kotlin/de/alex/domain/<Aggregate>Aggregate.kt
Template for existing aggregate:
@CommandHandler
fun handle(command: <CommandName>Command): CommandResult {
AggregateLifecycle.apply(
<EventName>Event(
command.aggregateId,
)
)
return CommandResult(command.aggregateId.toString(), AggregateLifecycle.getVersion())
}
Template for new aggregate creation (when createsAggregate: true):
@CreationPolicy(AggregateCreationPolicy.ALWAYS)
@CommandHandler
fun handle(command: <CommandName>Command): CommandResult {
AggregateLifecycle.apply(
<EventName>Event(
command.aggregateId,
)
)
return CommandResult(command.aggregateId.toString(), AggregateLifecycle.getVersion())
}
Step 4: Add EventSourcingHandler to Aggregate
@EventSourcingHandler
fun on(event: <EventName>Event) {
this.aggregateId = event.aggregateId
}
Example: Processing "slice: Assign Clerk to Case"
Input:
{
"sliceType": "STATE_CHANGE",
"title": "slice: Assign Clerk to Case",
"commands": [{
"title": "Assign Clerk to Case",
"aggregate": "Case",
"fields": [
{"name": "caseId", "type": "UUID", "idAttribute": true},
{"name": "clerkId", "type": "UUID"},
{"name": "hourlyRate", "type": "Decimal"}
]
}],
"events": [{
"title": "Clerk Assigned to Case",
"fields": [...]
}]
}
Generated Files:
src/main/kotlin/de/alex/cases/domain/commands/assignclerktocase/AssignClerkToCaseCommand.kt
src/main/kotlin/de/alex/events/ClerkAssignedToCaseEvent.kt
- Update
src/main/kotlin/de/alex/domain/CaseAggregate.kt with handlers
Processing Group Configuration
Ensure the module has a ProcessingGroups file:
Location: src/main/kotlin/de/alex/<module>/ProcessingGroups.kt
package de.alex.<module>
class ProcessingGroups {
companion object {
const val <MODULE> = "<MODULE>"
}
}