# Commands with Results Commands that return data after execution. ## Overview Commands with results use the `ICommandHandler` interface and return data such as IDs, status information, or computed results. ## Interface ```csharp public interface ICommandHandler { Task HandleAsync(TCommand command, CancellationToken cancellationToken = default); } ``` ## When to Return Data ### ✅ Return Data When: - **Created IDs** - Return newly created entity IDs - **Operation status** - Return success/failure details - **Computed results** - Return calculated values - **Confirmation data** - Return what was created/updated - **Batch results** - Return summary of batch operations ### ❌ Don't Return: - Domain entities directly (use DTOs) - Sensitive data - Unnecessary data ## Common Return Types ### 1. Primitive Types (IDs) ```csharp public record CreateUserCommand { public string Name { get; init; } = string.Empty; public string Email { get; init; } = string.Empty; } public class CreateUserCommandHandler : ICommandHandler { public async Task HandleAsync(CreateUserCommand command, CancellationToken cancellationToken) { var user = new User { Name = command.Name, Email = command.Email }; await _context.Users.AddAsync(user, cancellationToken); await _context.SaveChangesAsync(cancellationToken); return user.Id; // Return ID } } ``` **HTTP Response:** ``` 200 OK Content-Type: application/json 123 ``` ### 2. DTOs (Confirmation) ```csharp public record CreateOrderResult { public int OrderId { get; init; } public decimal TotalAmount { get; init; } public string OrderNumber { get; init; } = string.Empty; public DateTime CreatedAt { get; init; } } public class PlaceOrderCommandHandler : ICommandHandler { public async Task HandleAsync(PlaceOrderCommand command, CancellationToken cancellationToken) { var order = new Order { CustomerId = command.CustomerId, Items = command.Items, TotalAmount = CalculateTotal(command.Items) }; await _orders.AddAsync(order, cancellationToken); return new CreateOrderResult { OrderId = order.Id, TotalAmount = order.TotalAmount, OrderNumber = order.OrderNumber, CreatedAt = order.CreatedAt }; } } ``` **HTTP Response:** ```json { "orderId": 456, "totalAmount": 99.99, "orderNumber": "ORD-2025-001", "createdAt": "2025-01-15T10:30:00Z" } ``` ### 3. Status/Summary Objects ```csharp public record ImportResult { public int TotalRecords { get; init; } public int SuccessCount { get; init; } public int ErrorCount { get; init; } public List Errors { get; init; } = new(); } public class ImportUsersCommandHandler : ICommandHandler { public async Task HandleAsync(ImportUsersCommand command, CancellationToken cancellationToken) { var result = new ImportResult { TotalRecords = command.Users.Count }; var successCount = 0; var errors = new List(); foreach (var userDto in command.Users) { try { await CreateUserAsync(userDto, cancellationToken); successCount++; } catch (Exception ex) { errors.Add($"{userDto.Email}: {ex.Message}"); } } return result with { SuccessCount = successCount, ErrorCount = errors.Count, Errors = errors }; } } ``` **HTTP Response:** ```json { "totalRecords": 100, "successCount": 95, "errorCount": 5, "errors": [ "user1@example.com: Email already exists", "user2@example.com: Invalid email format" ] } ``` ### 4. Boolean (Success/Failure) ```csharp public class ActivateUserCommandHandler : ICommandHandler { public async Task HandleAsync(ActivateUserCommand command, CancellationToken cancellationToken) { var user = await _userRepository.GetByIdAsync(command.UserId, cancellationToken); if (user == null) return false; // User not found if (user.IsActive) return true; // Already active user.IsActive = true; user.ActivatedAt = DateTime.UtcNow; await _userRepository.UpdateAsync(user, cancellationToken); return true; } } ``` ### 5. Complex Objects ```csharp public record PaymentResult { public string PaymentId { get; init; } = string.Empty; public string Status { get; init; } = string.Empty; public decimal Amount { get; init; } public string TransactionId { get; init; } = string.Empty; public DateTime ProcessedAt { get; init; } } public class ProcessPaymentCommandHandler : ICommandHandler { public async Task HandleAsync(ProcessPaymentCommand command, CancellationToken cancellationToken) { var payment = await _paymentService.ChargeAsync( command.PaymentMethod, command.Amount, cancellationToken); await _orders.UpdatePaymentAsync(command.OrderId, payment.Id, cancellationToken); return new PaymentResult { PaymentId = payment.Id, Status = payment.Status, Amount = payment.Amount, TransactionId = payment.TransactionId, ProcessedAt = payment.ProcessedAt }; } } ``` ## Best Practices ### ✅ DO - Return IDs for created entities - Return DTOs, not domain entities - Include enough data for client confirmation - Return operation status for batch operations - Document what's returned in XML comments ### ❌ DON'T - Return entire domain entities - Return sensitive data (passwords, tokens) - Return more data than needed - Return null for success cases - Return complex nested structures ## See Also - [Basic Commands](basic-commands.md) - Commands without results - [Command Registration](command-registration.md) - How to register - [Best Practices: Command Design](../../best-practices/command-design.md)