SpringBoot, Axon, Multiple Data Sources = ? are you doin’?

Ok, so, after playing with this for few days, I have decided to put this into a form of a post, to serve mostly as a reference for anyone facing the same or similar problem. After all, how do you think I solve my problems? Just like that, I search online for solution, then for documentation, then trial and error, then going to github to read the source files, then go into libraries and decompile to see the content etc… and it can be pretty… unpleasent process sometimes…

Lets say we have situation like this. Spring app is connected to multiple databases. I’m just improvising here, but this is the general idea.

This means you can’t rely on default auto-configuration classes any longer, the framework would not know which data source to use and for what. So, you have couple of options, but having multiple configurations is just what must be done. What is Spring after all, just a framework that creates beans. 😉

Before we proceed, I advise you take a look at following post from John T. (I’m not payed in any way, shape or form for promoting his work, Its just the best out there as far as Im concerned…) How to configura multiple data sources in spring boot?

So, John’s post can solve your basic needs, but it does not cover axon configuration. For axon to work properly, you need to do some additional configuration. To figure out which classes need to be scanned, I used stack-overflow and went into axon packages, one by one, to find the JPA ones…

My config is as follows:

  • Have a default application data configuration (annotate beans with Primary) and then let Axon’s autoconfiguration use this. Otherwise, you would take out the Axon’s auto-configuration related to data source and write your own -> point it to whatever data source you want.
  • Have additional data configurations for different database adapter modules.

LocalContainerEntityManagerFactoryBean is the most important part to note here.

@Profile("my-axon-profile")
@Configuration
public class PostgresDataSourceConfig {

    @ConfigurationProperties("my.application.axon.db.spring.datasource")
    @Bean
    @Primary
    public DataSourceProperties getDatasourceProperties() {
        return new DataSourceProperties();
    }

    @Primary
    @Bean
    public DataSource getDatasource(DataSourceProperties dataSourceProperties) {
        return dataSourceProperties
                .initializeDataSourceBuilder()
                .build();
    }

    @Primary
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityManagerFactoryBuilder builder, DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages(
                        "org.axonframework.eventsourcing.eventstore.jpa",
                        "org.axonframework.eventhandling.tokenstore.jpa",
                        "org.axonframework.modelling.saga.repository.jpa"
                        )
                .build();
    }

    @Primary
    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

Ok, now, how about adding another data source? Here is my example, tested and working and it’s pretty similar to what you can find in other examples.

@Profile("my-profile")
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        basePackages = { "com.dimitar.somemodule.adapters.repositories.postgres.repositories" },
        entityManagerFactoryRef = "myCustomEntityManagerFactory",
        transactionManagerRef= "myCustomPostgresTransactionManager"
       )
public class MyCustomPostgresDataConfig {

    @Bean(name = "myCustomDataSourceProperties")
    @ConfigurationProperties("my.custom.postgres.datasource")
    public DataSourceProperties myCustomPostgresDataSourceProperties() {
        return new DataSourceProperties();
    }

    @Bean(name = "myCustomDataSource")
    @ConfigurationProperties("my.custom.postgres.datasource.configuration")
    public DataSource dataSource(@Qualifier("myCustomDataSourceProperties") DataSourceProperties dataSourceProperties) {
        return dataSourceProperties.initializeDataSourceBuilder().type(HikariDataSource.class)
                .build();
    }

    @Bean(name = "myCustomEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean clinicPostgresEntityManagerFactory(
            EntityManagerFactoryBuilder builder, @Qualifier("myCustomDataSource") DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages("com.dimitar.somemodule.adapters.repositories.postgres.repositories.entities")
                .build();
    }

    @Bean(name = "myCustomPostgresTransactionManager")
    public PlatformTransactionManager myCustomTransactionManager(
            @Qualifier("myCustomEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
        return new JpaTransactionManager(entityManagerFactory);
    }
}

So, what you need are:

  • data souce
  • entity manager factory
  • transaction manager

If you want more info on these, then I can say that the official spring docs are pretty *** (more accurate is to say what docs!?!)….

And, when you think about it, who would do something like this? With everything moving towards microservices, something like this would only be seen in monolithic applications…. but you never know, perhaps some service needs multiple sources (instead of pub sub!?!)… who knows… anyway, good luck!

Update to original post

After receiving some questions and request for an example repository, I have put quickly something together and hosted it on github. Feel free to check it out and add PRs to expand the example (some commands, events, aggregates, rest-apis etc)… For now, idea was to see how to customize the location of the axon database. Code is here: https://github.com/divukman/axon-db-source-example/tree/main

Result is something like this (view fro DBeaver on my local machine):