-
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 asynchronous execution Common way to execute commands is fire-and forget
var command = new CreateOrderCommand(orderNum);
GridNode.Execute(command);
//add any waiting here
var order = LoadAggregate<OrderAggregate>(command.OrderId);
Assert.AreEqual(command.OrderNum, order.Num);
We are not waiting and command is processed asynchronously, on different thread or machine. We don't know how much time command execution will take, and we don't wait for command faults.
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
###Waiting for messages from event bus
GridDomain has functionality to wait any message coming from internal event bus. Quick example :
var cmd = new CreateAndChangeAggregateCommand(Guid.NewGuid());
Task<IWaitResults> waiter = GridNode.NewWaiter()
.Expect<SampleAggregateChangedEvent>(e => e.SourceId == cmd.AggregateId)
.And<SampleAggregateCreatedEvent>(e => e.SourceId == cmd.AgregateId)
.Or<IFault<SampleAggregateChangedEvent>>( f => f.Message.SourceId == cmd.AggregateId)
.Create(Timeout);
waiter.Wait();
waiter is created in caller thread and subscribes for messages, all predicates are executed locally. Please pay attention that expectations are combined in special way : each new statement is placed in brackets, e.g. Expect(A).Or(B).And(C) converts into ((A or B) and C) and Expect(A).And(B).Or(C).And(D) into (((A and B) or C) and D)
General way to wait for command-produced message is to create a waiter and, execute command and wait.
var cmd = new CreateAndChangeAggregateCommand(Guid.NewGuid());
Task<IWaitResults> waiter = GridNode.NewWaiter()
.Expect<SampleAggregateChangedEvent>(e => e.SourceId == cmd.AggregateId)
.Create(Timeout);
GridNode.Execute(cmd);
waiter.Wait();
It is possible to get expected messages from waiter. It contains IMessageWaiter.All read-only collection that holds all received messages. It is filled asynchronously, by each message arrival. To access specific message, use IMessageWaiter.Message() extensions method. It will return found message of type T, and also it is supporting predicate to filter over several messages of same type.
var cmd = new CreateAndChangeAggregateCommand(Guid.NewGuid());
Task<IWaitResults> waiter = GridNode.NewWaiter()
.Expect<SampleAggregateChangedEvent>(e => e.SourceId == cmd.AggregateId)
.Create(Timeout);
GridNode.Execute(cmd);
var res = waiter.Result;
var evt = res.Message<SampleAggregateChangedEvent>();
There is a syntax sugar to simplify command execute-and-receive-message flow, available by GridNode.NewCommandWaiter method. Following example creates command, configures expectations and executes command waiting until expectations fulfill and provides received messages to caller.
var cmd = new CreateAndChangeAggregateCommand(Guid.NewGuid());
var expectedEvent = GridNode.NewCommandWaiter()
.Expect<SampleAggregateChangedEvent>(e => e.SourceId == cmd.AggregateId)
.Create(Timeout)
.Execute(cmd)
.Result
.Message<SampleAggregateChangedEvent>();
Any fault for waiter is just another message, there is no special logic inside.
Exception is when we are executing command by GridNode.NewCommandWaiter().Execute()
method - it will add fault handling for executing command by adding .Or<IFault<TCommand>>(f => f.Message.Id == cmd.Id)
at the end of exiting expectations.
Also Execute() has optional bool parameter to configure should it raise an exception if any faults will be received on not.
Default value is true.
All functionality relying on ExpectedMessage and CommandPlan class is obsolete, please avoid its usage as it will removed in future. Under the hood it was refactored to Waiter approach.
To delegate work to dedicated grid node use GridNodeConnector class. It can connect to other GridNode and executo command on it as common IGridDomainNode.
_connector = new GridNodeConnector(serverConfig.Network);
_connector.Connect();
var res= await _connector.NewCommandWaiter(TimeSpan.FromDays(1))
.Expect<SampleAggregateCreatedEvent>()
.Create()
.Execute(command)
var evt = res.Message<SampleAggregateCreatedEvent>()