I'm working on an application for managing bank credit cards.
CQRS and Event Sourcing architecture was chosen for the app.
The most important aggregate in the app is CreditCard which controls the credit card lifecycle.
It looks something like:
class CreditCard {
private int status;
public void activate() {...}
public void deactivate () {...}
...
}
Its activate and deactivate methods protects credit card invariants and publish CardActivatedEvent and CardDeactivatedEvent, respectively, if the invocation of the method succeeds.
We store these events in the event store for later aggregate reconstruction on the command side.
We apply these events to various views.
We use these events to notify other third party systems.
All good for now.
Recently, we got a new requirement to charge all active credit cards on monthly basis.
My first instinct was, ok we can add charge method to the same CreditCard aggregate.
This method can check some invariants relevant to charging. Like, is the card in correct status for charging, was it charged already, etc.
On successful invocation, this method can publish CardChargedEvent.
Then we can create some process manager which will once per month query view side for active credit cards to get their IDs.
Having these IDs, the process manager can issue multiple charge commands (one per credit card aggregate) to the command side.
For each charge command received, the command side will reconstruct CreditCard aggregate object and call it's charge method.
The only problem is that this approach looks quite inefficient. Especially regarding database roundtrips on the command side (one read and one write per aggregate instance).
If we take into the equation that we can easily have 100k plus credit cards in our app, this roundtrip overhead starts looking to me as a bit of a problem.
Does anyone have any experience with batch operations on CQRS/ES systems? Is my concern valid? What to do in such cases? How you implement batches in CQRS systems?
One alternative that pops to my mind is that for charging use case I ditch CQRS/ES/DDD principles, and implement the whole thing using stored procedures on one of our view databases. This procedure can search for suitable credit cards in the credit card view table and populate the "to be charged queue" table with records found. Then I can have some external process that reads this second table and do whatever it needs to do.