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.