Skip to content

Example of Spring Boot Config

Eugene Getman edited this page Jan 31, 2020 · 4 revisions

This is just a quick tip on how to configure Jes:

Suppose all of your beans are placed in:

@Configuration
@EnableAutoConfiguration
public class Config {

First of all, we need to declare a JEventStore bean - its the central component of the framework:

@Bean
public JEventStore eventStore(StoreProvider storeProvider) {
    return new JEventStore(storeProvider);
}

JEventStore accepts a StoreProvider as an underlying component that handles all specific communication:

@Bean(destroyMethod = "close")
public StoreProvider storeProvider(DataSource dataSource, SerializationOption[] options) {
    return new JdbcStoreProvider<>(dataSource, options);
}

// or

@Bean(destroyMethod = "close")
public StoreProvider storeProvider(EntityManagerFactory entityManagerFactory, SerializationOption[] options) {
    return new JpaStoreProvider<>(entityManagerFactory, options);
}

// or

public StoreProvider storeProvider() {
    return new InMemoryStoreProvider();
}

SerializationOptions is an optional parameter, it allows, for example, use event types aliasing when written in the event store:

// You can use any aliases for events. So you don't need to hardcode serialized class name of the event.
// Every client can create its model to deserialize events from the event store just using aliasing.
@Bean
public SerializationOption[] serializationOptions() {
    final TypeRegistry registry = new TypeRegistry();
    registry.addAlias(ItemCreated.class, ItemCreated.class.getSimpleName());
    registry.addAlias(ItemRemoved.class, "ItemRemoved");
    registry.addAlias(OrderPlaced.class, "Any other name I want");
    return new SerializationOption[]{registry, Format.JSON_JACKSON};
}

If you plan to make some validation against aggregate state you need an AggregateStore component:

@Bean
public AggregateStore aggregateStore(JEventStore eventStore) {
    return new AggregateStore(eventStore);
}

// or    

@Bean
public AggregateStore aggregateStore(JEventStore eventStore, SnapshotProvider snapshotProvider) {
    return new AggregateStore(eventStore, snapshotProvider);
}

// or

@Bean
public AggregateStore aggregateStore(JEventStore eventStore, SnapshotProvider snapshotProvider,
                                     SnapshotStrategy snapshotStrategy) {
    return new AggregateStore(eventStore, snapshotProvider, snapshotStrategy);
}

Additional SnapshotProvider and SnapshotStrategy components are responsible for 'how' to make snapshots and 'when' to make it:

@Bean
public SnapshotProvider snapshotProvider() {
    return new InMemorySnapshotProvider(CACHE_SIZE);
}

// or

@Bean
public SnapshotProvider snapshotProvider(DataSource dataSource) {
    return new JdbcSnapshotProvider<>(dataSource);
}

// or

@Bean
public SnapshotProvider snapshotProvider(RedissonClient client) {
    return new RedisSnapshotProvider(client);
}

and

@Bean
public SnapshotStrategy snapshotStrategy() {
    return new SizeBasedSnapshotStrategy(SNAPSHOT_AFTER_SIZE);
}

// or

@Bean
public SnapshotStrategy snapshotStrategy() {
    return new TimeBasedSnapshotStrategy(Duration.ofMillis(SNAPHOT_AFTER_DURATION));
}

// or just

@Bean
public SnapshotStrategy snapshotStrategy() {
    return new AlwaysSnapshotStrategy();
}

To handle commands we need a command handler component. It accepts commands via command bus:

@Bean
public CommandBus commandBus() {
    return new SyncCommandBus();
}

After that, you can create a component as usual:

@Component
public class StockHandler extends CommandHandler {
    
    @Autowire
    public StockHandler(@Nonnull JEventStore store, @Nonnull CommandBus bus) {
        super(store, bus);
    }

    // or
    @Autowire
    public StockHandler(@Nonnull AggregateStore aggregateStore, @Nonnull CommandBus bus) {
        super(aggregateStore, bus);
    }

There another 2 top-level components left:

  1. Projector - make custom data view the easy way
  2. Saga - call appropriate action on event store changes

They require 2 additional beans to be declared in config:

@Bean
public Offset offset() {
    return new InMemoryOffset();
}
    
// or

@Bean
public Offset offset(DataSource dataSource) {
    return new JdbcOffset(dataSource);
}

// or

@Bean
public Offset offset(RedissonClient client) {
    return new RedisOffset(client);
}

and

@Bean
public Lock lock() {
    return new InMemoryReentrantLock();
}

// or
    
@Bean
public Lock lock(DataSource dataSource) {
    return new JdbcLock(dataSource);
}
    
// or

@Bean
public Lock lock(RedissonClient client) {
    return new RedisReentrantLock(client);
}

That's all. Now you can declare components as follows:

@Service
public class StockProjector extends Projector {

    @Autowire
    public StockProjector(@Nonnull JEventStore store, @Nonnull Offset offset, @Nonnull Lock lock) {
        super(store, offset, lock);
    }
@Service
public class StockSaga extends Saga {

    @Autowire // stateless version
    public OrderSaga(@Nonnull JEventStore store, @Nonnull Offset offset, @Nonnull Lock lock) {
        super(store, offset, lock);
    }
    
    //or 

    @Autowire // stateful version
    public OrderSaga(@Nonnull AggregateStore aggregateStore, @Nonnull Offset offset, @Nonnull Lock lock) {
        super(aggregateStore, offset, lock);
    }

That's all.

Clone this wiki locally