CoreMesh.Dispatching 0.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package CoreMesh.Dispatching --version 0.1.0
                    
NuGet\Install-Package CoreMesh.Dispatching -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CoreMesh.Dispatching" Version="0.1.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CoreMesh.Dispatching" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="CoreMesh.Dispatching" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CoreMesh.Dispatching --version 0.1.0
                    
#r "nuget: CoreMesh.Dispatching, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CoreMesh.Dispatching@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CoreMesh.Dispatching&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=CoreMesh.Dispatching&version=0.1.0
                    
Install as a Cake Tool

CoreMesh.Dispatching

CoreMesh.Dispatching 是 CoreMesh 的輕量 Dispatching 模組,提供:

  • Send:請求/回應(request/response)
  • Send:無回傳 command
  • Publish:事件通知(notification)

設計目標:

  • 簡單
  • 可讀
  • 易學習
  • 低執行成本(lazy cache)

目前特性

  • Dispatcher 使用 wrapper + cache(懶加載)
  • Notification 預設為串行執行(安全優先)
  • 支援 Microsoft.Extensions.DependencyInjection 註冊與 assembly 掃描
  • 不包含 pipeline

核心介面

  • IRequest<TResponse>:有回傳的請求
  • IRequest:無回傳 command
  • IRequestHandler<TRequest, TResponse>
  • IRequestHandler<TRequest>
  • INotification:事件通知
  • INotificationHandler<TNotification>
  • IDispatcher

快速開始

1. 定義 request/response 與 handler

using CoreMesh.Dispatching;

public sealed record SampleQuery(string Foo, string Bar) : IRequest<SampleResponse>;

public sealed record SampleResponse(string Foo, string Bar);

public sealed class SampleHandler : IRequestHandler<SampleQuery, SampleResponse>
{
    public Task<SampleResponse> Handle(SampleQuery request, CancellationToken cancellationToken = default)
        => Task.FromResult(new SampleResponse(request.Foo, request.Bar));
}

2. 註冊 Dispatcher 與 Handlers

using CoreMesh.Dispatching;

builder.Services.AddDispatching(typeof(SampleHandler).Assembly);

或手動註冊:

builder.Services.AddScoped<IDispatcher, Dispatcher>();
builder.Services.AddScoped<IRequestHandler<SampleQuery, SampleResponse>, SampleHandler>();

3. 呼叫 Send

app.MapGet("/sample", async (IDispatcher dispatcher, CancellationToken ct) =>
{
    var result = await dispatcher.Send(new SampleQuery("Foo", "Bar"), ct);
    return Results.Ok(result);
});

Notification 範例(Publish)

using CoreMesh.Dispatching;

public sealed record UserCreated(int UserId, string Email) : INotification;

public sealed class AuditLogOnUserCreatedHandler : INotificationHandler<UserCreated>
{
    public Task Handle(UserCreated notification, CancellationToken cancellationToken = default)
    {
        Console.WriteLine($"[Audit] User created: {notification.UserId}");
        return Task.CompletedTask;
    }
}

public sealed class WelcomeEmailOnUserCreatedHandler : INotificationHandler<UserCreated>
{
    public Task Handle(UserCreated notification, CancellationToken cancellationToken = default)
    {
        Console.WriteLine($"[Mail] Send welcome email to {notification.Email}");
        return Task.CompletedTask;
    }
}

註冊:

builder.Services.AddScoped<INotificationHandler<UserCreated>, AuditLogOnUserCreatedHandler>();
builder.Services.AddScoped<INotificationHandler<UserCreated>, WelcomeEmailOnUserCreatedHandler>();

呼叫:

await dispatcher.Publish(new UserCreated(123, "demo@coremesh.dev"), ct);

行為說明

Send

  • Send(IRequest<TResponse>):必須有且只有一個對應 handler
  • Send(IRequest):必須有且只有一個對應 handler
  • 若找不到 handler,會拋出 DI 解析例外(InvalidOperationException

Publish

  • Publish(INotification) 會執行所有對應的 INotificationHandler<T>
  • 目前為 串行執行(依註冊順序)
  • 適合安全優先場景(例如 handler 共用 scoped 依賴)

適用場景(這個設計模式適合用在哪裡)

CoreMesh.Dispatching 採用的是「應用層請求分派(Dispatcher / Mediator-like)」模式,適合以下場景:

1. Web API / Minimal API 的應用層協調

當 endpoint 不想直接依賴 service/repository,改為送出一個 request 給對應 handler 處理。

適合:

  • 查詢(Query)
  • 命令(Command)
  • 應用層流程協調

效果:

  • endpoint 更薄
  • 業務流程集中在 handler
  • 邏輯更容易測試

2. 單一職責拆分(避免 God Service)

把大量方法的 ApplicationService 拆成多個 request handler,每個 handler 只處理一個 use case。

適合:

  • 功能逐步增長的專案
  • 團隊多人協作(每人維護不同 handler)

3. 事件通知(Notification)驅動的後續動作

一個主流程完成後,透過 Publish 廣播事件,讓多個 handler 處理後續副作用。

例如:

  • 使用者建立後:寫審計、寄信、記錄 metrics
  • 訂單付款後:更新報表、通知外部系統、寫 outbox

4. 想保留簡單架構,但需要統一入口

不想一開始導入完整 CQRS/DDD 框架,只需要一個輕量、可控、低開銷的分派機制。

這個模組幫你處理了哪些事情

CoreMesh.Dispatching 主要處理的是「請求/通知的分派與調用」,而不是業務邏輯本身。

已處理的事情

  • Request -> Handler 對應與呼叫(Send
  • Notification -> 多個 Handlers 的分派(Publish
  • DI 容器中的 handler 解析
  • Handler 探索與註冊(assembly 掃描)
  • 執行期 wrapper 快取(lazy cache)
  • 首次型別包裝建立(wrapper factory cache)

刻意不處理的事情(目前版本)

  • Validation pipeline
  • Logging pipeline
  • Retry / Circuit breaker
  • Transaction / Unit of Work
  • Authorization
  • ASP.NET Core endpoint 抽象
  • Outbox / Message broker 發送

這些屬於 cross-cutting concern 或基礎設施整合,建議放在外層模組處理(例如 endpoint、middleware、decorator、背景工作)。

設計取捨

為什麼沒有 Pipeline?

目前版本刻意不包含 pipeline,以保持:

  • 更低延遲
  • 更少配置
  • 更簡單的執行路徑

Validation / logging / proxy 等 cross-cutting concern 建議先放在外層(例如 endpoint、middleware、decorator)。

注意事項

  • 建議明確傳入 assembly 掃描(例如 typeof(SomeHandler).Assembly
  • 若使用 AddDispatching() 無參數版本,會掃描目前已載入 assemblies(啟動成本較高)
  • 一個 request 型別應對應一種回應型別(IRequest<TResponse>

後續方向

  • AddDispatchingFromAssemblyContaining<T>()
  • 更精細的 DI 註冊選項(lifetime / filter)
  • Notification publisher 策略(串行 / 並行可切換)
  • Source Generator(進一步降低 runtime 型別解析成本)
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.2 164 3/14/2026
1.1.1 124 3/11/2026
1.1.0 130 3/11/2026
1.0.0 87 3/4/2026
0.1.1 90 2/22/2026
0.1.0 87 2/22/2026