一键导入
state-change-slice
Generate commands, events, aggregates, Wolverine HTTP endpoints (POST), and tests for STATE_CHANGE slices
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Generate commands, events, aggregates, Wolverine HTTP endpoints (POST), and tests for STATE_CHANGE slices
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Generate event-driven automation processors for AUTOMATION slices using Wolverine message handlers
Transform slice definitions from config.json into C# code using Wolverine + Marten (Critter Stack)
Generate Marten read models, projections, and Wolverine HTTP GET endpoints for STATE_VIEW slices
Generate translators that map external events (from other bounded contexts or systems) into internal domain events using Wolverine handlers
| name | state-change-slice |
| description | Generate commands, events, aggregates, Wolverine HTTP endpoints (POST), and tests for STATE_CHANGE slices |
A STATE_CHANGE slice modifies aggregate state through commands and events. It generates:
Domain/Commands)Events)Domain)<Context>/Slices/<SliceName>)Note: STATE_CHANGE slices do NOT generate projections. Those belong to STATE_VIEW slices.
HTTP POST → Command → Aggregate (Wolverine handler) → Event appended to Marten stream
| Java / Axon | C# / Critter Stack |
|---|---|
@CommandHandler on Aggregate | Handle(Command, IEventStream<T>) returning events |
@EventSourcingHandler | Apply(Event e) methods on aggregate |
CommandGateway.sendAndWait() | Wolverine HTTP endpoint cascade |
AggregateLifecycle.apply() | stream.AppendOne(event) |
AggregateTestFixture | Wolverine TrackedSession + Alba |
CommandResult(id, version) | CommandResult(Guid, long) |
Location: Domain/Commands/<CommandName>.cs
using SlicingWorkshop.Common;
namespace SlicingWorkshop.Domain.Commands;
public record <CommandName>(
Guid AggregateId
// Add fields from slice command definition
) : ICommand;
Location: Events/<EventName>.cs
using SlicingWorkshop.Common;
namespace SlicingWorkshop.Events;
public record <EventName>(
Guid AggregateId
// Add fields from slice event definition
) : IEvent;
Location: Domain/<Aggregate>.cs
For new aggregate (createsAggregate: true or first command):
using Marten;
using Marten.Events;
using SlicingWorkshop.Common;
using SlicingWorkshop.Domain.Commands;
using SlicingWorkshop.Events;
namespace SlicingWorkshop.Domain;
public class <Aggregate>
{
public Guid Id { get; private set; }
// Add state fields
public static IEnumerable<IEvent> Handle(<CommandName> command, IEventStream<<Aggregate>> stream)
{
yield return new <EventName>(
command.AggregateId
// Map command fields to event fields
);
}
public void Apply(<EventName> @event)
{
Id = @event.AggregateId;
// Update state fields
}
}
For existing aggregate:
public IEnumerable<IEvent> Handle(<CommandName> command, IEventStream<<Aggregate>> stream)
{
// Business validation (throw for hard failures)
yield return new <EventName>(
command.AggregateId
// Map command fields to event fields
);
}
Location: <Context>/Slices/<SliceName>/<SliceName>Endpoint.cs
using Marten;
using SlicingWorkshop.Common;
using SlicingWorkshop.Domain.Commands;
using Wolverine.Http;
using Wolverine.Marten;
namespace SlicingWorkshop.<Context>.Slices.<SliceName>;
public static class <SliceName>Endpoint
{
[WolverinePost("/api/<context>/<resource>")]
public static async Task<CommandResult> Post(
<CommandName> command,
IDocumentSession session)
{
var stream = await session.Events.FetchForWritingAsync<<Aggregate>>(command.AggregateId);
// Validation: throw if precondition fails
var @event = <Aggregate>.Handle(command, stream);
stream.AppendMany(@event);
await session.SaveChangesAsync();
return new CommandResult(command.AggregateId, stream.CurrentVersion);
}
}
Note: When using [AggregateHandler] pattern (recommended for Wolverine+Marten):
[WolverinePost("/api/<context>/<resource>")]
public static (CommandResult, <EventName>) Post(
<CommandName> command,
[Aggregate] <Aggregate> aggregate)
{
var @event = new <EventName>(command.AggregateId /*, fields */);
return (new CommandResult(command.AggregateId, 0), @event);
}
Location: <Context>/Slices/<SliceName>/Tests/<CommandName>Tests.cs
using Alba;
using Marten;
using SlicingWorkshop.Events;
using Xunit;
namespace SlicingWorkshop.<Context>.Slices.<SliceName>.Tests;
public class <CommandName>Tests : IClassFixture<AppFixture>
{
private readonly AppFixture _fixture;
public <CommandName>Tests(AppFixture fixture) => _fixture = fixture;
[Fact]
public async Task <SpecTitleAsPascalCase>()
{
var aggregateId = Guid.NewGuid();
await _fixture.Host.Scenario(x =>
{
x.Post.Json(new { AggregateId = aggregateId /*, fields */ })
.ToUrl("/api/<context>/<resource>");
x.StatusCodeShouldBe(200);
});
using var session = _fixture.Store.LightweightSession();
var events = await session.Events.FetchStreamAsync(aggregateId);
Assert.Contains(events, e => e.Data is <EventName> ev && ev.AggregateId == aggregateId);
}
}
ICommandIEventRegisterCustomer, not CustomerRegistrationRequest