Explore optional typed command and event unions for exhaustive handling
Los mantenedores suelen responder en 7 días
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Aptitud para principiantes
- 35/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- csharp
- Área
- backend-api-design, devtools
Línea de trabajo
Start by reading src/Core/src/Eventuous.Domain/State.cs, then compare the command and aggregate event APIs, target frameworks, and serialization paths named in the issue. Build a small end-to-end prototype with explicit command and event sets, and verify omitted and out-of-set cases produce errors while existing payloads replay unchanged. Done means the prototype, compatibility and runtime notes, and an API recommendation address the acceptance criteria.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Is your feature request related to a problem? Please describe.
Adding a command or event can leave handling incomplete without producing a compiler diagnostic. Eventuous provides typed handler registration, but it does not declare a closed set of commands and events for an aggregate against which handling can be checked.
For example, an application adds BookingCancelled and starts emitting it, but forgets to update the booking state. The default State<T>.When(object) returns the existing state when no handler is registered. Ignoring an event can be intentional, but an accidental omission can produce incorrect state during execution and replay. Similarly, a missing command registration is detected when the command is handled rather than when the application is compiled.
A community suggestion on Discord describes using F# discriminated unions and generics in Rosetta to make these omissions visible during compilation.
Describe the solution you'd like
Explore an optional API that lets an application declare the complete command and event sets for an aggregate and use exhaustive handling at its command and state-transition boundaries.
The initial deliverable should be a small prototype and design recommendation. Keep the existing APIs available and preserve their behavior for applications that do not opt in.
Example: forgetting to apply a cancellation event
An application initially supports creating a booking. Its declared event set, BookingEvent, contains only RoomBooked, and its state-update function handles that event.
A developer then adds cancellation:
- Add
BookingCancelledto theBookingEventset. - Add a command handler that emits
BookingCancelled. - Forget to add cancellation handling to the state-update function.
With the current default state handling, the application can compile and store the cancellation event while leaving the booking state unchanged.
With the proposed typed API, the compiler knows that BookingEvent has two possible cases, but the state-update function handles only one. It reports the missing BookingCancelled case. Adding that case to update the booking's status resolves the diagnostic.
The same principle applies to commands: adding a command to the declared command set should produce a diagnostic wherever exhaustive command handling omits it.
The prototype should demonstrate build failures with the relevant diagnostics treated as errors. A general catch-all can hide newly added cases, so the example should use explicit cases, including explicit no-op handling where appropriate.
Exhaustiveness comes from the closed set and matching semantics. A marker interface alone does not provide this guarantee. These checks cover known cases in the consuming compilation; they do not prove business correctness, registration completeness elsewhere, or compatibility with unknown persisted events.
Design questions to resolve:
- API placement: Can typed adapters or additional generic abstractions support both aggregate-based and functional command services without replacing existing APIs?
- Language and runtime requirements: Compare F# unions, C# unions, and analyzer/generator approaches. C# union declarations produce structs, while current command handling and aggregate event application have
classconstraints. Account for those constraints and Eventuous's supported target frameworks. - Persistence compatibility: Keep existing event type names and serialized payloads stable. Determine how concrete events are wrapped and unwrapped at typed boundaries, including historical event versions and unknown-event behavior.
- Selective consumption: Keep projection and subscription event sets independent. A projection that consumes a subset of events should not be required to handle every event in the system.
Acceptance criteria for this exploration:
- Provide one end-to-end aggregate or functional service example with explicit command and event sets.
- Demonstrate diagnostics for an omitted command case and an omitted event case after adding them to their respective sets.
- Demonstrate rejection of commands or events outside the declared set at the typed API boundary.
- Demonstrate explicit intentional ignoring without a catch-all that hides future cases.
- Verify that existing serialized event payloads can be replayed through the prototype without changing their storage format.
- Document compatibility, required language/runtime versions, and which guarantees remain outside compile-time checking.
- Recommend an API direction, or explain why the tradeoffs do not justify adopting one.
Describe alternatives you've considered
- Retain the current APIs and add application tests that check expected handler registrations and replay behavior.
- Add opt-in startup validation against explicitly declared command/event sets. This catches omissions earlier than message processing, but still at runtime.
- Extend Roslyn analyzers or source generators to check declared sets against handler registrations. The existing
EventUsageAnalyzerchecks event type annotations; it does not establish exhaustive command or event handling. - Keep unions in application domain code and adapt to Eventuous at the boundary, minimizing changes to the library.
Additional context
This proposal originates from the Discord discussion, which shared Rosetta's approach as inspiration for Eventuous.
Rosetta's aggregate example demonstrates typed command and event unions with exhaustive matching. Microsoft's C# union tutorial describes the preview feature, and the language reference explains exhaustiveness diagnostics and the generated struct representation. Evaluate the supported feature and runtime requirements during the prototype rather than assuming a particular release commitment.
- Lenguaje dominante
- C#
- Estrellas
- 512
- Forks
- 99
- Merge medio
- 5 d 6 h
- PR fusionados (30 d)
- 5
Preparar el entorno
- Incluye un Dockerfile o un archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de Eventuous/eventuous
-
MongoDB integration tests fail on newer GitHub Actions runner kernelsPosiblemente ocupada @nmummau la tomó hace 4 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
Eventuous/eventuous#626 · 2 comentarios ·
Los mantenedores suelen responder en 7 días
-
`dotnet format style --verify-no-changes` fails with naming violations and unnecessary importsPosiblemente ocupada @nmummau la tomó hace 4 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 80/100
Eventuous/eventuous#624 · 1 comentario ·
Los mantenedores suelen responder en 7 días
-
SQL Server: `append_events` does not need the `@inserted` table variablePosiblemente ocupada @nmummau la tomó hace 10 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 20/100
Eventuous/eventuous#619 · 1 comentario ·
Los mantenedores suelen responder en 7 días
-
SQL Server: stream reads do one key lookup per event, and `IDX_EventsStream` duplicates `UQ_StreamIdAndStreamPosition`Posiblemente ocupada @nmummau la tomó hace 10 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 18/100
Eventuous/eventuous#618 · 1 comentario ·
Los mantenedores suelen responder en 7 días
-
SQL Server: every stream lookup by name does a key lookup to get `Version`Posiblemente ocupada @nmummau la tomó hace 10 días. Abierto
Dificultad 3/5 1-2 días Aptitud para principiantes 25/100
Eventuous/eventuous#617 · 1 comentario ·
Los mantenedores suelen responder en 7 días
Todos los issues de Eventuous/eventuous
Issues similares
-
dev/release: verify-release-candidate.sh runs 0 Java tests with Debian's MavenPosiblemente ocupada @kou la tomó hoy. AbiertoType: bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
apache/arrow-adbc#4947 ·
Los mantenedores suelen responder en 1 día
-
:watch: Not Triaged CAC
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
dotnet/AspNetCore.Docs#38001 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Nimblesite/SharpLsp#468 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
elastic/ecs-dotnet#575 ·
-
V4 alert-rule reads have no scope gate, so any authenticated credential can list alert rulesAbiertoalerts bug effort:S P2
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
nightscout/nocturne#2511 ·
Los mantenedores suelen responder en 1 día