Order of execution

Hello,

I have a question regarding to order of execution operations on an object.
As in example below. If we create a new object and assign it with Id witch we generate can we be sure that the handle method will wait until the object is actually created and inserted into aggregate root and into repository?
In the following example can we be sure that everything will be executed in this order and there will be no situation that in some part of this code it will accessing something witch is not finished and will occur error?

    public void Example()
    {
        var objectId = Guid.NewGuid();
        var someChilds = new List<int>();

        //....


        _commandContextManager.Handle(new CreateObject
        {
            objectId = objectId
        });

        foreach (var child in someChilds)
        {
            using (var context = _commandContextManager.EstablishForCommand(new NullCommand()))
            {
                var root = _aggregateRootForObject.Get(objectId);
                root.AddChild(child);
                context.Commit();
           }
        }

        var objectWithChilds = _repositoryForObject.GetById(objectId);
    }
1 Like

You have to think about what you are actually trying to achieve. A command is a transaction, it succeeds or fails. If you want the creation of the aggregate with the children to either succeed (everything is created) or fail (nothing is created) you have to model it that way. On the other hand, if you want the “children” to be added such that some can succeeed and some fail, you model them as separate commands (as you have done here).

You don’t have to have multiple commands to have multiple events. You can have a CreateWithChildren command which has the object id and an array of children ids. This can then result in a stream of events (objectcreated, childadded, childadded, childadded, childadded, etc.) which are then committed to the event store as a single commit (and thus an atomic action).

Some definitions to keep in mind:

  • An aggregate root is the sole entry point to a piece of domain state / logic that has to be kept consistent. You cannot access “child” objects directly, only through the root.
  • A command is a transaction. You model what you want to succeed or fail as a transaction. A command can only update a single aggregate root.
  • An event represents a state change in your domain that is meaningful to you. While they might trigger other actions, they are not themselves triggers. If you need to trigger something, you don’t need to go through commands / aggregates / events.

Thanks for answer,

Since the command is transaction it succeeds or fails, but if we call it like in the example:
_commandContextManager.Handle(new CreateObject
{
objectId = objectId
});

the program will wait until this transaction finish and give results ? or it will make a transaction in the background but execute other operation like iterating through someChilds list. If it would be second case then it is risky because we can access to object witch is not processed (transaction is still running, and we dont know if it finished). If it would be the first case, then I suppose it will be safe, because if we already started iterated in someChilds it means that the transaction is already finished and all data are already processed by this command.

The command is a transaction. It will have completed before proceeding to doing anything with the children. You need to check the CommandResult to see if it has succeeded when using the CommandCoordinator.Handle method.

You don’t need to use the repository within the loop. You have that instance and when you call the method on it, any events get applied and the internal state gets updated. These uncommitted events are flushed to the Event Store
on the commit. If this succeeds, your instance is already in sync with the Event Store there’s no need to refresh it. It would only fail to write the event if there was some transient error due to network for example, or, more likely, due to a concurrency
exception where the version you are trying to write is behind the current version. In this case, because you are doing it without going through the coordinator, there will be an exception thrown rather than a CommandResult returned.

If you are actually doing something like this, I’m 99% certain you are doing it wrong. You probably haven’t modelled your aggregate and/or command correctly. Don’t think of the command as an update against the database or the
event as a callback / trigger. Think instead about your failure scenarios and what they mean. Don’t do complex work in a Processor, use the processor to schedule the work for your own service. When the processor has done this, its job is done. Your service
can be async, can handle long running business transactions, retries and failure scenarios. You have to model this explicitly though, like a Saga or Process Manager. How this is done depends on your domain and requirements.

2 Likes