-
Notifications
You must be signed in to change notification settings - Fork 6
Commands
CoreDdd encourages CQRS and application requests modifying an application state to be explicitly implemented as commands and command handlers. A command handler should be simple, and merely instantiate an aggregate root domain entity by either creating a new aggregate root entity or fetching an existing aggregate root entity from a database, and call a respective domain behaviour method on it. The complex domain code should live inside domain entities, and not inside command handlers.
A CoreDdd command implements ICommand interface, and a command handler implements ICommandHandler<TCommand> interface. CoreDdd provides a command handler base class BaseCommandHandler to derive your command handlers from, providing a helper method RaiseCommandExecutedEvent to raise a command executed event when you need to pass some data like generated Id to the caller of the command.
Create a new command, for instance create a new ship command:
public class CreateNewShipCommand : ICommand
{
public string ShipName { get; set; }
public decimal Tonnage { get; set; }
}
Add a new command handler:
public class CreateNewShipCommandHandler : BaseCommandHandler<CreateNewShipCommand>
{
private readonly IRepository<Ship> _shipRepository;
public CreateNewShipCommandHandler(IRepository<Ship> shipRepository)
{
_shipRepository = shipRepository;
}
public override async Task ExecuteAsync(CreateNewShipCommand command)
{
var newShip = new Ship(command.ShipName, command.Tonnage);
await _shipRepository.SaveAsync(newShip);
RaiseCommandExecutedEvent(new CommandExecutedArgs { Args = newShip.Id });
}
}
Execute the command using the following code:
using (var unitOfWork = new NhibernateUnitOfWork(nhibernateConfigurator))
{
unitOfWork.BeginTransaction();
var shipRepository = new NhibernateRepository<Ship>(unitOfWork);
try
{
var createNewShipCommand = new CreateNewShipCommand {ShipName = "lady", Tonnage = 10m };
var createNewShipCommandHandler = new CreateNewShipCommandHandler(shipRepository);
var generatedShipId = 0;
createNewShipCommandHandler.CommandExecuted += args => generatedShipId = (int) args.Args;
await createNewShipCommandHandler.ExecuteAsync(createNewShipCommand);
Console.WriteLine($"Create new ship command was executed. Generated ship id: {generatedShipId}");
await unitOfWork.CommitAsync();
}
catch
{
await unitOfWork.RollbackAsync();
throw;
}
}
The code sample above is available here as .NET Core console app.
Another way how to execute a command is to use CoreDdd CommandExecutor. Instead of instantiating and executing the command handler directly, CommandExecutor
can be used to instantiate and execute the command handler based on the command type:
var commandExecutor = new CommandExecutor(new FakeCommandHandlerFactory(unitOfWork));
var createNewShipCommand = new CreateNewShipCommand {ShipName = "lady", Tonnage = 10m };
await commandExecutor.ExecuteAsync(createNewShipCommand);
The code above uses FakeCommandHandlerFactory
which is a command handler abstract factory implementation, which creates an instance of CreateNewShipCommandHandler
based on the CreateNewShipCommand
type.
The code sample above is available here.
CommandExecutor is not supposed to be used directly, instead ICommandExecutor interface should be used together with an IoC container to execute commands. Most IoC containers (e.g. Castle Windsor or Ninject) are able to provide an abstract factory implementation out of the box, so no manual implementation like FakeCommandHandlerFactory
is needed.
To start using CommandExecutor with an IoC container, add an IoC container into the project (e.g. Castle Windsor or Ninject) and register the components into the IoC container, Castle Windsor example:
ioCContainer.AddFacility<TypedFactoryFacility>();
ioCContainer.Register(
Component.For<ICommandHandlerFactory>().AsFactory(), // register command handler factory (no real factory implementation needed :)
Component.For<ICommandExecutor>() // register command executor
.ImplementedBy<CommandExecutor>()
.LifeStyle.Transient,
Classes
.FromAssemblyContaining<CreateNewShipCommand>() // register all command handlers in this assembly
.BasedOn(typeof(ICommandHandler<>))
.WithService.FirstInterface()
.Configure(x => x.LifestyleTransient()),
Component.For<INhibernateConfigurator>() // register nhibernate configurator
.ImplementedBy<CoreDddSampleNhibernateConfigurator>()
.LifeStyle.Singleton,
Component.For<NhibernateUnitOfWork>() // register nhibernate unit of work
.LifeStyle.PerThread,
Component.For(typeof(IRepository<>)) // register repositories
.ImplementedBy(typeof(NhibernateRepository<>))
.LifeStyle.Transient
And then resolve ICommandExecutor
and execute the command:
var commandExecutor = ioCContainer.Resolve<ICommandExecutor>();
var createNewShipCommand = new CreateNewShipCommand { ShipName = "lady", Tonnage = 10m };
await commandExecutor.ExecuteAsync(createNewShipCommand);
The code sample above is available here.
Now getting to a real life usage example. In the real life, an application component will get ICommandExecutor
injected into a constructor by an IoC container:
public class ShipController
{
private readonly ICommandExecutor _commandExecutor;
public ShipController(ICommandExecutor commandExecutor)
{
_commandExecutor = commandExecutor;
}
public async Task<int> CreateNewShipAsync(string shipName, decimal tonnage)
{
var createNewShipCommand = new CreateNewShipCommand {ShipName = shipName, Tonnage = tonnage};
var generatedShipId = 0;
_commandExecutor.CommandExecuted += args => generatedShipId = (int)args.Args;
await _commandExecutor.ExecuteAsync(createNewShipCommand);
return generatedShipId;
}
}
The code sample above is available here.
All code samples above execute CreateNewShipCommandHandler which creates a new aggregate root domain entity Ship
. Let's see how to execute a domain behaviour on an existing persisted aggregate root domain entity. Add a new command update ship data command:
public class UpdateShipDataCommand : ICommand
{
public int ShipId { get; set; }
public string ShipName { get; set; }
public decimal Tonnage { get; set; }
}
Add a command handler:
public class UpdateShipDataCommandHandler : BaseCommandHandler<UpdateShipDataCommand>
{
private readonly IRepository<Ship> _shipRepository;
public UpdateShipDataCommandHandler(IRepository<Ship> shipRepository)
{
_shipRepository = shipRepository;
}
public override async Task ExecuteAsync(UpdateShipDataCommand command)
{
var ship = await _shipRepository.GetAsync(command.ShipId);
ship.UpdateData(command.ShipName, command.Tonnage);
}
}
And here's a ShipController
code to execute it:
public class ShipController
{
private readonly ICommandExecutor _commandExecutor;
public ShipController(ICommandExecutor commandExecutor)
{
_commandExecutor = commandExecutor;
}
public async Task UpdateShipData(int shipId, string shipName, decimal tonnage)
{
var updateShipDataCommand = new UpdateShipDataCommand
{
ShipId = shipId,
ShipName = shipName,
Tonnage = tonnage
};
await _commandExecutor.ExecuteAsync(updateShipDataCommand);
}
}
The code sample above is available here.