Skip to content

Getting Started Partie

SteffenBlake edited this page Sep 28, 2026 · 3 revisions

CQRS routing with Partie

Partie connects typed query and command handlers to routes. Handler contracts describe the request and response types, and framework engines connect those handlers to various consuming frameworks.

Setting up

Install Brigade.Net.Partie in the project that you want to handle your business logic:

dotnet add package Brigade.Net.Partie

Brigade.Net.Partie provides the request binding attributes and handler contracts.

Integrating with ASP.NET Core Framework

For an ASP.NET Core API, install these packages:

dotnet add package Brigade.Net.Partie.AspNetCore
dotnet add package Brigade.Net.Partie.Engines.AspNetCore

Brigade.Net.Partie.AspNetCore provides route declarations and HTTP binding.

Brigade.Net.Partie.Engines.AspNetCore generates the route attributes for your handlers and the helper that registers them with ASP.NET Core.

Register routes with ASP.NET Core

var app = builder.Build();

...

// Make Partie routes available to HTTP requests.
app.UsePartieRoutes();

Declaring routing class

[PartieSystemBundle]
[AspNetSystemBundle]
[BrigadeGroup("/api/v1")]
public static partial class RoutingV1
{
    
}

Define a request and response

[FromPath] reads the course ID from the URL. The handler returns a course with that ID.

public sealed class CourseSearchQuery
{
    [FromPath]
    public required int CourseId { get; init; }
}

public sealed record CourseSearchResult(int CourseId, string Title);

Define a handler

public sealed class CourseSearchHandler : IQueryHandler<CourseSearchQuery, CourseSearchResult, Unit>
{
    public static async Task<Result<CourseSearchResult>> RunAsync(
        Unit context,
        CourseSearchQuery query,
        CancellationToken cancellationToken
    )
    {
        ...
    }
}

Add it to your routing

[PartieSystemBundle]
[AspNetSystemBundle]
[BrigadeGroup("/api/v1")]
public static partial class RoutingV1
{
    // GET /api/v1/courses/{courseId}
    [CourseSearchHandlerRoute.Get("/courses/{courseId}")]
    public static partial void Search();
}

The Unit Type

Use Unit when there is no value to pass or return. A handler with Unit as its context needs no extra context object. A command with Unit as its result completes without returning a response value.

Results

Result holds either a value of type T or a failure such as NotFound or Conflict. Result pattern is designed to avoid the common "Use Exceptions as GOTO statement" antipattern many developers tend to fall for in C#.

UnitOfWork Transactions

UnitOfWork groups changes to transactional resources into one operation. If a command succeeds, Partie commits those changes; if it fails or throws, Partie rolls them back.

Provided Values, Partie, and Providers

Both Providers and Parties can provide values to a handler’s context. For example, [UnitOfWorkPartie] provides a UnitOfWork to command handlers.

A Provider runs on demand when a handler or Partie asks for its value. For example, a handler can request the current academic year when it needs to choose the right course offering.

A Partie runs for every matching request, whether or not another part asks it for a value. The LogCourseSearchPartie below, for example, logs each matching course search.

Define your own Provider, Partie

Queries use IQueryProvider<TProvided, TContext, TQuery, TResult> and IQueryPartie<TProvided, TContext, TQuery, TResult>. Commands use ICommandProvider and ICommandPartie. Implement OnQueryAsync or OnCommandAsync; invoke next(value) to continue with the value you provide.

public sealed class AcademicYearProvider :
    IQueryProvider<string, Unit, CourseSearchQuery, CourseSearchResult>
{
    public static ValueTask<Result<CourseSearchResult>> OnQueryAsync(
        Unit context,
        CourseSearchQuery query,
        Next<string, CourseSearchResult> next,
        CancellationToken cancellationToken)
    {
        return next("2026-2027");
    }
}
public sealed class LogCourseSearchPartie :
    IQueryPartie<Unit, Unit, CourseSearchQuery, CourseSearchResult>
{
    public static async ValueTask<Result<CourseSearchResult>> OnQueryAsync(
        Unit context,
        CourseSearchQuery query,
        Next<Unit, CourseSearchResult> next,
        CancellationToken cancellationToken)
    {
        Console.WriteLine($"Searching for course {query.CourseId}");
        return await next(Unit.Default);
    }
}

Use a provided value in a Handler

Add [Provide] to a context parameter to receive the value created by a provider:

public sealed record CourseSearchContext([Provide] string AcademicYear);

public sealed class CourseSearchHandler :
    IQueryHandler<CourseSearchQuery, CourseSearchResult, CourseSearchContext>
{
    public static Task<Result<CourseSearchResult>> RunAsync(
        CourseSearchContext context,
        CourseSearchQuery query,
        CancellationToken cancellationToken)
    {
        // Use context.AcademicYear to choose the course offering.
        ...
    }
}

[Provide] requests the value from [AcademicYearProvider].

Registering custom Providers and Parties

Add your custom created Providers and Parties via their automatically generated attributes:

[AcademicYearProvider]
[LogCourseSearchPartie]
[CourseSearchHandlerRoute.Get("/{courseId}")]
public static partial void Search();

Integrating with Dependency Injection

Use [Inject] when a handler context needs a service registered with the framework’s dependency injection system. IE ASP.NET Core's builder.Services:

builder.Services.AddScoped<ICourseCatalog, CourseCatalog>();

...

public sealed record CourseCatalogContext([Inject] ICourseCatalog Catalog);

Core system: Common built-in Parties

Use the [PartieSystemBundle] on the root grouping, it includes the UnitOfworkPartie automatically.

UnitOfWorkPartie

Automatically configures commit and rollback of the UnitOfWork system within Partie, which broadly allows for transactional behaviors.

ASP.NET Core: Common built-in Parties

For an ASP.NET Core API, use [AspNetSystemBundle] on the root route group. It includes HttpResultPartie automatically.

HttpResultPartie

Automatically maps Result types back to Http Status codes

Success: 200 OK, or 204 No Content for Unit Deprecated: Same status as success, with a Deprecation response header and expiry Error: The status set on the error, or 400 Bad Request by default NotFound: 404 Not Found Conflict: 409 Conflict Forbidden: 403 Forbidden GatewayError: 502 Bad Gateway Timeout: 504 Gateway Timeout

What to do next?

Check out installing the Expo system for Validation, and Mise system for Database ORM integration!

  1. Validation with Expo
  2. Database access with Mise

Further Reading

See the Partie guide for request binding, services, providers, Parties, and policies. The Example project shows handlers with application services and route policies.

Clone this wiki locally