원클릭으로
원클릭으로
| name | state change slice |
| description | Knows how to build state change slices |
A STATE_CHANGE slice represents a business operation that modifies aggregate state through commands and events. This is the core CQRS/Event Sourcing pattern.
{
"sliceType": "STATE_CHANGE",
"title": "slice: <Name>",
"context": "<BoundedContext>",
"commands": [...],
"events": [...],
"readmodels": [...],
"screens": [...],
"processors": [...],
"specifications": [...]
}
Screen (UI) → Command → Aggregate (@CommandHandler) → Event → ReadModel (via @EventHandler Projector)
↓
Automation (if processors exist)
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,
// Add fields from slice command definition
// Map types: UUID, String, Date → LocalDate, Integer → Int, Boolean, Decimal → BigDecimal
// Handle cardinality: Single → regular type, Multiple → List<type>
) : 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?) |
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,
// Add fields from slice event definition
// Fields typically mirror command fields
) : Event
Location: src/main/kotlin/de/alex/domain/<Aggregate>Aggregate.kt
Template for existing aggregate:
@CommandHandler
fun handle(command: <CommandName>Command): CommandResult {
// Business validation logic
AggregateLifecycle.apply(
<EventName>Event(
command.aggregateId,
// Map command fields to event fields
)
)
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,
// Map command fields to event fields
)
)
return CommandResult(command.aggregateId.toString(), AggregateLifecycle.getVersion())
}
@EventSourcingHandler
fun on(event: <EventName>Event) {
// Update aggregate state from event
this.aggregateId = event.aggregateId
// Update other state fields
}
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.ktsrc/main/kotlin/de/alex/events/ClerkAssignedToCaseEvent.ktsrc/main/kotlin/de/alex/domain/CaseAggregate.kt with handlersEnsure 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>"
}
}