true ;
if (raiseProductPriceChangedEvent)
// Create event if price has changed
{
var
oldPrice = catalogItem.
Price
;
priceChangedEvent =
new ProductPriceChangedIntegrationEvent
(catalogItem.
Id
,
productToUpdate.
Price
,
oldPrice);
}
// Update current product
catalogItem = productToUpdate;
// Just save the updated product if the Product's Price hasn't changed.
if (!raiseProductPriceChangedEvent)
{
await _catalogContext.
SaveChangesAsync
();
}
else // Publish to event bus only if product price changed
{
// Achieving atomicity between original DB and the IntegrationEventLog
// with a local transaction
using (
var
transaction = _catalogContext.
Database
.
BeginTransaction
())
{
_catalogContext.
CatalogItems
.
Update
(catalogItem);
await _catalogContext.
SaveChangesAsync
();
await _integrationEventLogService.
SaveEventAsync
(priceChangedEvent);
transaction.
Commit
();
}
// Publish the integration event through the event bus
_eventBus.
Publish
(priceChangedEvent);
_integrationEventLogService.
MarkEventAsPublishedAsync
(
priceChangedEvent);
}
return Ok
();
}
After the ProductPriceChangedIntegrationEvent integration event is created, the transaction that
stores the original domain operation (update the catalog item) also includes the persistence of the
event in the EventLog table. This makes it a single transaction, and you will always be able to check
whether event messages were sent.
The event log table is updated atomically with the original database operation, using a local
transaction against the same database. If any of the operations fail, an exception is thrown and the
transaction rolls back any completed operation, thus maintaining consistency between the domain
operations and the event messages saved to the table.
148
CHAPTER 5 | Designing and Developing Multi-Container and Microservice-Based .NET Applications