Explore optional typed command and event unions for exhaustive handling
メンテナーはふだん 7 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 初心者へのやさしさ
- 35/100
- issue の種類
- 機能追加
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- csharp
調査の方向性
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.
索引モデルが issue の本文から書いたものです。
説明
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.
- 主要言語
- C#
- スター
- 512
- フォーク
- 99
- 平均マージ
- 5日 6時間
- マージ済み PR(30日)
- 5
環境構築
- Dockerfile または Docker Compose ファイルあり
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
Eventuous/eventuous のほかの issue
-
MongoDB integration tests fail on newer GitHub Actions runner kernels対応中かも @nmummau が 1 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
Eventuous/eventuous#626 · コメント 2 件 ·
メンテナーはふだん 7 日以内に返信
-
`dotnet format style --verify-no-changes` fails with naming violations and unnecessary imports対応中かも @nmummau が 1 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 80/100
Eventuous/eventuous#624 · コメント 1 件 ·
メンテナーはふだん 7 日以内に返信
-
SQL Server: `append_events` does not need the `@inserted` table variable対応中かも @nmummau が 7 日前に担当しました。 オープン
難易度 3/5 1〜2日 初心者へのやさしさ 20/100
Eventuous/eventuous#619 · コメント 1 件 ·
メンテナーはふだん 7 日以内に返信
-
SQL Server: stream reads do one key lookup per event, and `IDX_EventsStream` duplicates `UQ_StreamIdAndStreamPosition`対応中かも @nmummau が 7 日前に担当しました。 オープン
難易度 3/5 1〜2日 初心者へのやさしさ 18/100
Eventuous/eventuous#618 · コメント 1 件 ·
メンテナーはふだん 7 日以内に返信
-
SQL Server: every stream lookup by name does a key lookup to get `Version`対応中かも @nmummau が 7 日前に担当しました。 オープン
難易度 3/5 1〜2日 初心者へのやさしさ 25/100
Eventuous/eventuous#617 · コメント 1 件 ·
メンテナーはふだん 7 日以内に返信
Eventuous/eventuous の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 64/100
joinrpg/joinrpg-net#5752 ·
メンテナーはふだん 1 日以内に返信
-
bug ux
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
CodeForFire/lagebuch#714 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
dimitar-radenkov/Pointframe#294 ·
メンテナーはふだん 1 日以内に返信
-
kind/feature-request
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
Altinn/altinn-correspondence#2154 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 61/100
Okabintaro/SubmersedVR#253 ·