-
Notifications
You must be signed in to change notification settings - Fork 7
Command execution
##Overview GridDomain supports command execution in two modes: fully asynchronous non-blocking and synchronous, waiting for given event by timeout. In both cases interface IGridDomainNode is used. ###Command synchronous execution Common way to execute commands is fire-and forget
var syncCommand = new OperationCommand(42, Guid.NewGuid());
GridNode.Execute(syncCommand);
var aggregate = LoadAggregate<SampleAggregate>(syncCommand.AggregateId);
Assert.AreNotEqual(syncCommand.Parameter, aggregate.Value);
Command results can be obtained only from read model after some period of time. Any command fault will be ignored. You can find example in unit test
###Command asynchronous execution
Sometimes it is very useful to execute command and wait for some results, or command fault. GridDomain thinks about command results as messages, commonly it will be Domain events or projection builder notifications. To wait for command execution we need ExpectedMessage class instance, describing message we want to wait. ExpectedMessage instance can be created by constructor or by one of static factory methods .
Having ExpectedMessage and declaring Timeout, you can execute command following way:
var syncCommand = new LongOperationCommand(42, Guid.NewGuid());
var expectedMessage = GetExpectedMessage();
GridNode.Execute(syncCommand,Timeout,expectedMessage);
var aggregate = LoadAggregate<SampleAggregate>(syncCommand.AggregateId);
Assert.AreEqual(syncCommand.Parameter.ToString(), aggregate.Value);
IGridDomain node have a lot of extension methods for different kinds of command sync execution, you can find them here with usage detailed examples
If command will fail in synchronious execution mode, GridDomainNode will deliver fault back to caller and raise initial exception from it. Stack trace also will be kept to show initial source of exception.
##Aggregate async methods Most aggregate methods are synchronious, as it implement domain logic. But sometimes long asynchronious calls are required, for example when aggregate need to call exteral service via http. In this case aggregate could execute other commands not been blocked until external call finished. As any other method, async method in aggregate should produce events. But result is Task with set of events, whitch will be available after Task complete. It is important for async method to be pure, e.g. not modifing any aggregate state in async manner. It will corrupt aggregate state. Async method invocations will not be persisted, and if aggregate instance will be crashed before async method call finish, all results will be lost. To persist invocations, use FutureEvents instead.
To use async calls aggreagate shoud be inherited from Aggregate class, and use RaiseEventsAsync method. Example:
var aggregateId = Guid.NewGuid();
var asyncCommand = new AsyncMethodCommand(43, Guid.NewGuid(),Guid.NewGuid(),TimeSpan.FromSeconds(3));
var syncCommand = new ChangeAggregateCommand(42, aggregateId);
var asyncCommandTask = GridNode.Execute<AggregateChangedEvent>(asyncCommand,
ExpectedMessage.Once<AggregateChangedEvent>(nameof(AggregateChangedEvent.SourceId),
asyncCommand.AggregateId));
GridNode.Execute<AggregateChangedEvent>(syncCommand, Timeout,
ExpectedMessage.Once<AggregateChangedEvent>(nameof(AggregateChangedEvent.SourceId),
syncCommand.AggregateId)
);
var sampleAggregate = LoadAggregate<SampleAggregate>(syncCommand.AggregateId);
Assert.AreEqual(syncCommand.Parameter.ToString(), sampleAggregate.Value);
var asyncResult = asyncCommandTask.Result;
Assert.AreEqual(asyncCommand.Parameter.ToString(), asyncResult.Value);
You can find more detailed examples at https://github.com/andreyleskov/GridDomain/tree/master/GridDomain.Domain.Tests/AsyncAggregates
If async method will fall, exception will be passed back to caller keeping stack trace.