Doctrine
This package provides a TransactionMiddleware
and a RollbackOnlyTransactionMiddleware
. Both middleware execute each command in a separate Doctrine ORM transaction.
The TransactionMiddleware
will start a transaction before each command begins. If the command is successful, it will flush and commit the EntityManager (saving you keystrokes). If an exception is raised, TransactionMiddleware
rolls back the transaction, closes the EntityManager and rethrows the exception (saving you from corrupt data).
RollbackOnlyTransactionMiddleware
does the same thing, but only rolls back the transaction: it does not close the entity manager.
Setup is simple:
use League\Tactician\CommandBus;
use League\Tactician\Doctrine\ORM\TransactionMiddleware;
$commandBus = new CommandBus(
[
new TransactionMiddleware($entityManager),
// other middleware...
]
);
Transactions and Subcommands
Sometimes, you might have a Command that fires off more commands, usually via events. The question then becomes, should these subcommands run inside the same transaction as the command that originally fired them?
The recommended approach is having each command run in a separate transaction. This keeps transaction boundaries smaller and makes commands easy to reason about because they only rely on themselves.
You can configure this by using the LockingMiddleware
shipped in the core Tactician package. By placing the LockingMiddleware
above the TransactionMiddleware
, you can ensure that each Command executes separately. For more details, see the LockingMiddleware documentation.
Still, Tactician is flexible and you can choose to run all subcommands inside the same transaction if you want. Just place the TransactionMiddleware
above the LockingMiddleware
or leave the LockingMiddleware
off entirely. Again, we don’t recommend this but it is possible.