Skip to main content
Commands represent write operations that modify system state. In this architecture, commands follow the CQRS pattern using MediatR and are processed by dedicated handlers.

Core Interfaces

IRequestCommand

Base interface for commands that don’t return a value.
namespace Core.Application
{
    public interface IRequestCommand : IRequest
    {
    }
}
Source: Core.Application.ComandQueryBus/Commands/IRequestCommand.cs:5

IRequestCommand<TResponse>

Base interface for commands that return a value.
namespace Core.Application
{
    public interface IRequestCommand<out TResponse> : IRequest<TResponse>
    {
    }
}
Source: Core.Application.ComandQueryBus/Commands/IRequestCommand.cs:9 Type Parameters:
  • TResponse - The type of value returned by the command (e.g., string, Guid, int)

IRequestCommandHandler<TRequest, TResponse>

Interface for command handlers that process commands and return results.
namespace Core.Application
{
    public interface IRequestCommandHandler<TRequest, TResponse> : IRequestHandler<TRequest, TResponse>
        where TRequest : IRequest<TResponse>
    {
    }
}
Source: Core.Application.ComandQueryBus/Commands/IRequestCommandHandler.cs:10 Type Parameters:
  • TRequest - The command type
  • TResponse - The response type
Methods:
  • Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken) - Processes the command

IRequestCommandHandler<TRequest>

Interface for command handlers that don’t return a value.
namespace Core.Application
{
    public interface IRequestCommandHandler<in TRequest> : IRequestHandler<TRequest>
        where TRequest : IRequest
    {
    }
}
Source: Core.Application.ComandQueryBus/Commands/IRequestCommandHandler.cs:5

Command Examples

CreateDummyEntityCommand

Command to create a new domain entity.
using Core.Application;
using System.ComponentModel.DataAnnotations;
using static Domain.Enums.Enums;

namespace Application.UseCases.DummyEntity.Commands.CreateDummyEntity
{
    public class CreateDummyEntityCommand : IRequestCommand<string>
    {
        [Required]
        public string dummyPropertyOne { get; set; }
        public DummyValues dummyPropertyTwo { get; set; }

        public CreateDummyEntityCommand()
        {
        }
    }
}
Source: Application/UseCases/DummyEntity/Commands/CreateDummyEntity/CreateDummyEntityCommand.cs:13 Returns: The ID of the created entity as a string

UpdateDummyEntityCommand

Command to update an existing entity.
using Core.Application;
using System.ComponentModel.DataAnnotations;
using static Domain.Enums.Enums;

namespace Application.UseCases.DummyEntity.Commands.UpdateDummyEntity
{
    public class UpdateDummyEntityCommand : IRequestCommand
    {
        [Required]
        public int DummyIdProperty { get; set; }
        [Required]
        public string dummyPropertyOne { get; set; }
        public DummyValues dummyPropertyTwo { get; set; }

        public UpdateDummyEntityCommand()
        {
        }
    }
}
Source: Application/UseCases/DummyEntity/Commands/UpdateDummyEntity/UpdateDummyEntityCommand.cs:8 Returns: No value (uses IRequestCommand)

DeleteDummyEntityCommand

Command to delete an entity.
using Core.Application;
using MediatR;
using System.ComponentModel.DataAnnotations;

namespace Application.UseCases.DummyEntity.Commands.DeleteDummyEntity
{
    public class DeleteDummyEntityCommand : IRequestCommand<Unit>
    {
        [Required]
        public int DummyIdProperty { get; set; }

        public DeleteDummyEntityCommand()
        {
        }
    }
}
Source: Application/UseCases/DummyEntity/Commands/DeleteDummyEntity/DeleteDummyEntityCommand.cs:7 Returns: MediatR.Unit (represents void)

Handler Implementation Patterns

Basic Handler Structure

Command handlers follow a consistent pattern:
internal sealed class CreateDummyEntityHandler : IRequestCommandHandler<CreateDummyEntityCommand, string>
{
    private readonly ICommandQueryBus _domainBus;
    private readonly IDummyEntityRepository _context;
    private readonly IDummyEntityApplicationService _dummyEntityApplicationService;

    public CreateDummyEntityHandler(
        ICommandQueryBus domainBus,
        IDummyEntityRepository dummyEntityRepository,
        IDummyEntityApplicationService dummyEntityApplicationService)
    {
        _domainBus = domainBus ?? throw new ArgumentNullException(nameof(domainBus));
        _context = dummyEntityRepository ?? throw new ArgumentNullException(nameof(dummyEntityRepository));
        _dummyEntityApplicationService = dummyEntityApplicationService ?? throw new ArgumentNullException(nameof(dummyEntityApplicationService));
    }

    public async Task<string> Handle(CreateDummyEntityCommand request, CancellationToken cancellationToken)
    {
        // 1. Create domain entity
        Domain.Entities.DummyEntity entity = new(request.dummyPropertyOne, request.dummyPropertyTwo);

        // 2. Validate entity
        if (!entity.IsValid) throw new InvalidEntityDataException(entity.GetErrors());

        // 3. Business rule validation
        if (_dummyEntityApplicationService.DummyEntityExist(entity.Id)) 
            throw new EntityDoesExistException();

        try
        {
            // 4. Persist entity
            object createdId = await _context.AddAsync(entity);

            // 5. Publish domain event
            await _domainBus.Publish(entity.To<DummyEntityCreated>(), cancellationToken);

            return createdId.ToString();
        }
        catch (Exception ex)
        {
            throw new BussinessException(ApplicationConstants.PROCESS_EXECUTION_EXCEPTION, ex.InnerException);
        }
    }
}
Source: Application/UseCases/DummyEntity/Commands/CreateDummyEntity/CreateDummyEntityHandler.cs:17

Handler Workflow

  1. Entity Creation - Instantiate domain entity from command data
  2. Validation - Validate domain invariants and business rules
  3. Persistence - Use repository to save changes
  4. Event Publishing - Publish domain events via ICommandQueryBus
  5. Error Handling - Wrap exceptions in application-specific types

Update Handler Pattern

internal sealed class UpdateDummyEntityHandler : IRequestCommandHandler<UpdateDummyEntityCommand>
{
    private readonly ICommandQueryBus _domainBus;
    private readonly IDummyEntityRepository _context;

    public UpdateDummyEntityHandler(
        ICommandQueryBus domainBus,
        IDummyEntityRepository dummyEntityRepository)
    {
        _domainBus = domainBus ?? throw new ArgumentNullException(nameof(domainBus));
        _context = dummyEntityRepository ?? throw new ArgumentNullException(nameof(dummyEntityRepository));
    }

    public async Task Handle(UpdateDummyEntityCommand request, CancellationToken cancellationToken)
    {
        // 1. Retrieve existing entity
        Domain.Entities.DummyEntity entity = await _context.FindOneAsync(request.DummyIdProperty) 
            ?? throw new EntityDoesNotExistException();
        
        // 2. Update entity properties
        entity.SetdummyPropertyOne(request.dummyPropertyOne);
        entity.SetdummyPropertyTwo(request.dummyPropertyTwo);

        try
        {
            // 3. Persist changes
            _context.Update(request.DummyIdProperty, entity);

            // 4. Publish domain event
            await _domainBus.Publish(entity.To<DummyEntityUpdated>(), cancellationToken);
        }
        catch (Exception ex)
        {
            throw new BussinessException(ApplicationConstants.PROCESS_EXECUTION_EXCEPTION, ex.InnerException);
        }
    }
}
Source: Application/UseCases/DummyEntity/Commands/UpdateDummyEntity/UpdateDummyEntityHandler.cs:10

Delete Handler Pattern

internal sealed class DeleteDummyEntityHandler : IRequestCommandHandler<DeleteDummyEntityCommand, Unit>
{
    private readonly ICommandQueryBus _domainBus;
    private readonly IDummyEntityRepository _context;

    public DeleteDummyEntityHandler(
        ICommandQueryBus domainBus,
        IDummyEntityRepository dummyEntityRepository)
    {
        _domainBus = domainBus ?? throw new ArgumentNullException(nameof(domainBus));
        _context = dummyEntityRepository ?? throw new ArgumentNullException(nameof(dummyEntityRepository));
    }

    public Task<Unit> Handle(DeleteDummyEntityCommand request, CancellationToken cancellationToken)
    {
        try
        {
            _context.Remove(request.DummyIdProperty);

            _domainBus.Publish(new DummyEntityDeleted(request.DummyIdProperty), cancellationToken);

            return Unit.Task;
        }
        catch (Exception ex)
        {
            throw new BussinessException(ApplicationConstants.PROCESS_EXECUTION_EXCEPTION, ex.InnerException);
        }
    }
}
Source: Application/UseCases/DummyEntity/Commands/DeleteDummyEntity/DeleteDummyEntityHandler.cs:10

Best Practices

  • Use verb-noun pattern: CreateEntity, UpdateEntity, DeleteEntity
  • Be specific: ActivateUser vs UpdateUser
  • Append Command suffix for clarity
  • Use DataAnnotations for basic validation ([Required], etc.)
  • Validate domain invariants in the entity itself
  • Perform business rule validation in the handler
  • Throw specific exceptions for different failure types
  • Inject repositories for data access
  • Inject ICommandQueryBus for publishing domain events
  • Inject application services for complex business logic
  • Use primary constructor syntax (C# 12) for conciseness
  • Always publish domain events after successful persistence
  • Use entity.To<TEvent>() for entity-to-event mapping
  • Pass cancellationToken to support operation cancellation
  • Publish events before returning from the handler

Build docs developers (and LLMs) love