Command Response Examples
This document provides examples of how command responses work with the automatic response handling feature.
Simple Value Response
Section titled “Simple Value Response”When a command handler returns a simple value without a corresponding value handler:
[Command]public record CreateUser(string Name, string Email){ public UserId Handle(IUserRepository userRepository) { var userId = new UserId(Guid.NewGuid()); var user = new User(userId, Name, Email); userRepository.Save(user);
// Since no value handler exists for UserId, // this automatically becomes CommandResult<UserId> return userId; }}Result: CommandResult<UserId> with the UserId as the Response property.
OneOf Response
Section titled “OneOf Response”When a command returns a OneOf with values that may or may not have handlers:
[Command]public record ValidateAndCreateUser(string Name, string Email){ public OneOf<UserId, ValidationResult> Handle(IUserRepository userRepository, IUserValidator validator) { var validationResult = validator.Validate(Name, Email); if (!validationResult.IsValid) { // ValidationResult has a built-in handler, so this affects command success return OneOf<UserId, ValidationResult>.FromT1(validationResult); }
var userId = new UserId(Guid.NewGuid()); var user = new User(userId, Name, Email); userRepository.Save(user);
// No handler for UserId, so this becomes CommandResult<UserId> return OneOf<UserId, ValidationResult>.FromT0(userId); }}Result: Either a failed CommandResult (for validation errors) or CommandResult<UserId> (for success).
Tuple Response with Mixed Handlers
Section titled “Tuple Response with Mixed Handlers”When a command returns a tuple with some values having handlers and others not:
[Command]public record ProcessOrder(string OrderId){ public (OrderConfirmation, OrderProcessed, ValidationResult, AuditLog) Handle( IOrderService orderService, IOrderValidator validator, IAuditService auditService) { var validation = validator.Validate(OrderId); var confirmation = orderService.ProcessOrder(OrderId); var orderEvent = new OrderProcessed(OrderId, DateTime.UtcNow); var auditLog = auditService.CreateLog("Order processed", OrderId);
return (confirmation, orderEvent, validation, auditLog); }}Assuming the following handlers exist:
OrderProcessed→ handled by event handlerValidationResult→ handled by validation handlerAuditLog→ handled by audit handlerOrderConfirmation→ no handler
Result: CommandResult<OrderConfirmation> with the confirmation as the Response property, plus any side effects from the other handlers.
Tuple with Multiple Unhandled Values (Error)
Section titled “Tuple with Multiple Unhandled Values (Error)”This scenario throws an exception:
[Command]public record BadCommand(){ public (string, int, SomeEvent) Handle() { // If no handlers exist for string and int, but SomeEvent has a handler return ("response1", 42, new SomeEvent()); }}Result: MultipleUnhandledTupleValues because both string and int lack handlers.
Custom Value Handler Example
Section titled “Custom Value Handler Example”Creating a handler to process specific values instead of making them responses:
public class OrderConfirmationHandler : ICommandResponseValueHandler{ private readonly IEmailService _emailService;
public OrderConfirmationHandler(IEmailService emailService) { _emailService = emailService; }
public bool CanHandle(CommandContext commandContext, object value) { return value is OrderConfirmation; }
public async Task<CommandResult> Handle(CommandContext commandContext, object value) { var confirmation = (OrderConfirmation)value;
// Send confirmation email as a side effect await _emailService.SendConfirmationEmail(confirmation);
// Don't affect the command result return CommandResult.Success(commandContext.CorrelationId); }}With this handler in place, OrderConfirmation values would be processed (email sent) rather than becoming responses.
Migration from Previous Versions
Section titled “Migration from Previous Versions”Before (Required Value Handlers)
Section titled “Before (Required Value Handlers)”// Previously, this would require a value handler for UserIdpublic UserId Handle() => new UserId(Guid.NewGuid());
// You had to create a handler like this:public class UserIdHandler : ICommandResponseValueHandler{ public bool CanHandle(CommandContext context, object value) => value is UserId; public Task<CommandResult> Handle(CommandContext context, object value) { var userId = (UserId)value; // Set the response manually... return Task.FromResult(new CommandResult<UserId>(userId)); }}After (Automatic Responses)
Section titled “After (Automatic Responses)”// Now this automatically becomes CommandResult<UserId>public UserId Handle() => new UserId(Guid.NewGuid());
// No handler needed unless you want side effectsThis reduces boilerplate while maintaining the same functionality.