Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

Explore optional typed command and event unions for exhaustive handling

オープン
#627 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 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:

  1. Add BookingCancelled to the BookingEvent set.
  2. Add a command handler that emits BookingCancelled.
  3. 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 class constraints. 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 EventUsageAnalyzer checks 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

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

Eventuous/eventuous のほかの issue

Eventuous/eventuous の issue をすべて見る

似ている issue

C# の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。