No Persist EventStorageEngine for saga only microservice?

I want to know if there is a way to make a no persist storage engine. I am working on a microservice that only requires a saga not an aggregate. Currently the domain event entry table is being populated with every event from the saga with seemingly random aggregateId’s. Is there a way to skip the domain event entry table and just have the saga_entry and association_value_entry tables? I tried to extend AbstractEventStorageEngine but couldn’t figure out how it worked.

I am still confused about this. If anyone could lead me in the right direction.

All I really want is for the domain_event_entry table to not be populated with events from the sagas. I am confused why this would happen by default. Every event adds a new row to domain_event_entry with a unique aggregate_identifier.

Hi @tieu,

This should definitely be doable, but does require me to ask a follow up from you.
Typically Sagas react to events and dispatch actions as an effect to those.
Those actions can be operations on third-party services or simply command messages.
So, I am uncertain how you see events being published from within your Saga given the above described paradigm.

As such it would be beneficial to see the set up of your Saga in code. So if you could share that, that would be very helpful.

Alongside that though, if you are using the Spring Boot auto configuration of Axon Framework, it will in a default set up create all required tables. Whether you want this to happen is a data source configuration thing though, no so much from the frameworks perspective. However, this obviously depends on how you are setting up your application, which at this stage is also no clear from the question. So any feedback you can give on what this microservice actually does would be very valuable to further deduce what’s going wrong here.

Let’s figure it out @tieu!

Cheers,
Steven

Thank you for responding!

So, yes I believe I am using the Spring Boot auto configuration of Axon Framework. I meant that when I publish the event from the controller to the @ SagaEventHandler, the event seems to be recorded in the domain_event_entry table.

Also I am using flyway so I had to manually create the tables. I created domain_event_entry, association_value_entry, saga_entry, and token_entry tables

Here is my basic configuration and code. Right now the saga doesn’t do anything really. I am just testing out the different parts of Axon Framework to apply it to my projects.

Configuration

@Component
class AxonConfig {

    @Bean
    fun getEntityManager(entityManager: EntityManager): EntityManager {
        return entityManager
    }

    @Bean
    fun springTransactionManager(platformTransactionManager: PlatformTransactionManager) :
            SpringTransactionManager = SpringTransactionManager(platformTransactionManager)

    @Bean
    @Primary
    fun eventScheduler(@Qualifier("eventBus") eventBus: EventBus, scheduler: Scheduler, transactionManager: SpringTransactionManager) : EventScheduler {
        return QuartzEventScheduler.builder()
                .eventBus(eventBus)
                .scheduler(scheduler)
                .transactionManager(transactionManager)
                .build()
    }

    @Bean
    fun connectionProvider(dataSource: DataSource?) : ConnectionProvider? = SpringDataSourceConnectionProvider(dataSource)

    @Bean
    fun sagaStore(connection: ConnectionProvider) : SagaStore<*>? {
        val sagaSchema = SagaSchema(
                "tondo_contract_api.saga_entry",
                "tondo_contract_api.association_value_entry"
        )
        return JdbcSagaStore.builder()
                .connectionProvider(connection)
                .sqlSchema(PostgresSagaSqlSchema(sagaSchema))
                .build()
    }
}

Basic Endpoint

    @GetMapping
    @RequestMapping("/createSaga")
    fun eventTest() {
        eb.publish(GenericEventMessage.asEventMessage<CreateSaga>(
                CreateSaga("123")
        ))

        print("Start Saga Event Sent \n")
    }

Saga Start

    @StartSaga
    @SagaEventHandler(associationProperty = "jobId")
    fun handle(createSaga: CreateSaga) {
        scheduledToken = eventScheduler.schedule(Duration.ofMillis(360000), DoSomething("123"))
        startTime = System.currentTimeMillis()
        println("${scheduledToken.toString()} \n")
        print("Saga Started\n")
    }

POM

		<dependency>
			<groupId>org.axonframework</groupId>
			<artifactId>axon-spring-boot-starter</artifactId>
			<version>4.4.2</version>
			<exclusions>
				<exclusion>
					<groupId>org.axonframework</groupId>
					<artifactId>axon-server-connector</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

Every time I put an event on the eventBus to start the saga it adds the event to the domain_eventy_entry

Hi @tieu,

Thanks for providing the additional background; that definitely helps.
Let me be upfront and state when somebody is talking about a “Saga Microservice”, I would really only assume the Saga instance to be there, with potentially any direct services attached to it.


Saga use case

In your scenario however the microservice doesn’t use messages as the means to communicate, but it uses a REST controller which publishes the event on the gateway. So in essence you are using a layer in the middle here. I would normally assume that your EventBus or EventStore would be distributed in this solution, so that your “saga microservice” can simply consume events from that. Know that Axon Server can fill that hole perfectly fine; hope you’d like to give that a try to be honest :wink:

Apart from that though, there isn’t inherently something wrong with an endpoint which triggers an event. I would, however, not assume an endpoint to publish an event directly to create a Saga through a distinct CreateSaga event (I would adjust any event to past tense by the way, so SagaCreated would be better suited here). Saga’s are intended as a means to delegate a complex business transaction, originating from other domain specific events occurring in your application. I don’t think any Saga would simply start on a SagaCreatedEvent, there must be a process before that which signals the requirement of initiating such a complex business transaction.

For example the combination of a Booking and Sales application, where somebody “places a booking” and sales should be processing the required payment for that. This would be one process (the Booking process) signalling the requirement for another (micro)service to act on this through the means of a business transaction (aka the Saga).

However, your Saga sample might just be that, a sample outside of the scope of your domain. I feel compelled to provide this description though, as Saga’s should be used consciously.


Actual problem

Now your “problem” of seeing events being stored is because the auto configuration will still introduce an EventStore given the provided configuration. Hence, Axon Framework will not see a simple EventBus, but an EventStore. It’s this solution which will be used by the EventGateway to publish the event on, but also to store it in for later use.

If you want to not use an EventStore, the most straightforward solution would be to create a bean of type EventBus in your application context. You could solve this by introducing the following:

@Bean
public EventBus eventBus() {
    return SimpleEventBus.builder().build();
}

In Axon, an EventStore is nothing more than a specific type of EventBus which also stores the events. Thus by introducing your own EventBus bean will override it.


Conclusion

The issue you are experiencing is definitely solvable! I would take into account my suggestions on how to use Sagas, if at all needed. Trust this will help you @tieu!

Thank you so much as a beginner I really appreciate the detailed response and better understanding of Saga standards which gives me a lot more to think about. This was definitely a sample as I am just trying to understand the framework.

However, I have a couple questions about some of the things you brought up.

  1. I understand that the EventStore stores the events for later use. But how could I replay these events if they have seemingly random aggregate_identifiers? And why would they have aggregate_identifiers if they aren’t aggregates?
  2. Lastly, an EventStore and SimpleEventBus both store the events in memory? The only difference is the EventStore persists them but both will forget the event if the service went down? As in, would the EventStore be able to recover if the service went down and events never reached their handlers.
1 Like

Hi @tieu,

Sure thing, that’s what the forum is for; to help everybody out.
Glad to hear this already gives you some guidelines to work on. I assume we will be seeing your name more often in the future too. Now, let me move to your follow up question:

  1. When you use the EventGateway to publish an event, you are essentially publishing what is (at this moment) called event messages. If you publish events from within the scope of an aggregate, you would be publishing domain event messages. Storing events through a RDBMS in the current implementation however requires the use of a “domain event entry”. It is this entry which enforces the use of an aggregate identifier. As a solution, and quick marker when checking the database, you should see that the event_identifier and aggregate_identifier fields are identical. This signals the system that what you are storing are regular events, not domain events. Furthermore, these events will never be used to “rehydrate” an aggregate for event sourcing, as they didn’t originate from an aggregate to begin with. Replaying the query model on the other hand does not care where the events originate from, it is just concerned in consuming the entirety of the stream. I trust this explains the seemingly random aggregate identifier and that it does not impose any strain on replaying what so ever.

  2. The EventBus is a mechanism to publish events from one component to another. The EventStore adds the behavior of storing the events which are published. Thus the EventBus does not store anything, also not in memory. Hence losing the event, or failing to handle it correctly, will currently mean the EventBus cannot help you to retry this event. Hence why using an EventStore instead is more fault tolerant as you can react however your consumer (i.e. the event handler) can cope with this exception. So, an EventStore would indeed be able to recover from such a scenario, or whenever you simply shut down the application and start it up again. The SimpleEventBus implementation however does not provide any of these guarantees.

Trusting this clarifies things @tieu.

Cheers,
Steven

Thank you! That clears up all my confusion!

1 Like