We are trying the Domain Driven Development (DDD) while working on a project.
We've got a Product
aggregate.
We've got a ProductRepository
to load/save Product
s.
We've also got a Timeline
aggregate, that contains TimeLineItem
s. And there's a TimelineRepository
.
When a product is created/changed, we have to add a TimeLineItem
to the Timeline
aggregate, like a logbook that logs the changes.
The total process of creating a Product
and saving a TimeLineItem
to the timeline, could be considered as one transaction (am I right?).
Because, the whole transaction should either succeed entirely, or fail entirely. We don't want to have a product in the database but no timeline item saying it was created and vice versa.
In DDD, an aggregate root has its repository. There's ProductRepository
and TimelineRepository
. We have to interact with both to finish our task.
I don't want to confuse the concept of transactions in DDD with a transaction in database terms. By this I mean when we're using Laravel, we do something like:
try {
DB::beginTransaction();
// Do something
DB::commit();
} catch(...) {
DB::rollBack();
}
When we're in a repository and we have to work with multiple database rows or tables, we can wrap that in a transaction like that piece of code. Because its packed together in the same class or even the same function. The world outside of the repository doesn't have to know and it's specific to the storage technology (in this case a database).
But when we have to interact with two different repositories, we cannot "share" that transaction.
Because the repositories can both use a different storage technology. Say that the
ProductRepository
uses a database and theTimeLine
repository a file or some kind of cloud service. When the transaction (DB::...
) fails, it has effect on the database, but does not rollback anything in a file or cloud service. We have to implement that mechanism ourselves.If both repositories use the exact same storage (like both one and the same MySQL database), we still are not allowed to do something like:
.
try {
DB::beginTransaction();
$productRepo->save($product);
$timelineRepo->save($timeline);
DB::commit();
} catch(...) {
DB::rollBack();
}
How do we solve this problem?