Thank you very much, Steven, for coming back to me on this. I really appreciate your input.
Please find following my comments/feedback. Kindly do let me know should something is not clear or you need more information.
Your DownloadEmailMessageCommand
doesn’t sound like a command to me, but a query. You’re requesting the Aggregate for a certain bit of state, in this case, whether you may download that email or not. Modelling it as a command is thus not the way to go. You could instead contain all the do-not-download-emails in your ‘EmailsRetreiveStarteEvent’ for example.
My motivation for putting the DownloadEmailMessageCommand
as a command is the state. If the download commences, then the aggregate state changes from idle to downloading, for example. I found that aggregates simplify state management of objects and I believe that’s their main scope of existence. When the user clicks a button that starts the emails download or a scheduler triggers this process, the request goes to the aggregate. The aggregate then decides whether to start the download or not. All business logic related download starting or not is captured by an aggregate.
You’re talking about ‘the service’ in your set up, but it’s not clear what ‘the service’ is. Is this your current solution to this problem? Is that just a regular Event Handler? Or is ‘the service’ the Saga approach you’re going for?
Sorry for not explaining this better. I added some code which is responsible for downloading the emails and referred to it as ‘the service’. This code is not part of the aggregate nor part of a saga. It interacts with the AXON framework by capturing events and emitting commands. Please find attached a diagram capturing the interaction between the aggregate and the code responsible for the actual downloads.
The code that is actually downloading the emails is dumb and contains no business rules/logic. It simply delegates all logic to the aggregate which in turn is managing its state.
Kindly do let me know if this is not clear and will try to explain things further.
The use of a TrackingEventProcessor
does not necessarily mean you should be interested in events in the past, it’s just changing the way to get events from a push to a pull mechanism. Especially in combination with a Saga it is definitely not recommended to ever replay the events against it. Saga’s trigger new action, thus replaying a Saga would mean you’d create side effects like sending new emails, or in your example download emails a second time. Anyhow, I’d not be afraid to use a TrackingEventProcessor
in this scenario if I were you.
The TrackingEventProcessor
(like the SubscribingEventProcessor
) works well when the events are isolated (not sure if I am using the correct term), otherwise, you need to rely on something else to link different events together. For example, if I have the events UsernameUpdatedEvent
and PasswordChangedEvent
, using a TrackingEventProcessor
(a SubscribingEventProcessor
) you need to link these two events, usually using the user ID so that you update the proper table record with the new changes. This is fairly straightforward with databases, but a bit more complex with in-memory objects. In my example, I have an in-memory object that is downloading the emails to which I want to send specific commands. With a Saga, this happens automatically with the AXON framework as the events are routed to the proper Saga instance. Given that I am not using a Saga, I had to write some code that routes the events to the proper in-memory object instance.
Grouping events per aggregate within a TrackingEventProcessor
used to not be an option in 3.0, but it is in 3.1 and up. You can now select how many threads and segments your TrackingEventProcessor
should run with and in addition to that, you can provide your own SequencingPolicy
. The SequencingPolicy is in charge of deciding which thread gets which events. This can, for example, be set to SequentialPerAggregatePolicy
, which will thus ensure that all the events for an aggregate will be routed to the same thread.
I am not sure I am following you here. Can you please point me to an example that I can use? Maybe this has the solution I need. I love coding, but I do not want to build something that already exists. I rather use the AXON framework in its entirety and add where needed, then reinvent the wheel. Please note that I am not bound to any version of AXON framework as yet.
Apart from this, a Saga could just as well be of use here.
From what you’ve described, I get the feeling that your Saga will perform the download of the emails and after that verify whether it could download those.
As such, it’s a component coordinating actions between your ‘Email Account Applicant’ (the one containing your aggregate) and the ‘Email Download Service’.
You are thus to some extent coordinating actions between different contexts. Additionally, depending on the number of emails, it might just as well be a long-running transaction. And lastly, it could be the container of state whether certain emails cant or cannot be downloaded (thus removing your query-command, the DownloadEmailMessageCommand
).
That was my first thought as it seemed to me that Saga fits the bill. But I need to be able to run this particular Saga on a background thread without changing the other existing Sagas. In fact, the title of this thread reads “How to run a particular Saga asynchronously?”
Thank you once more for your time and help. It is greatly appreciated.