Reputation: 81
The following example implements event sourcing (and CQRS) with spring using Axon.
https://github.com/dashsaurabh/event-sourcing-cqrs-axon-spring-boot
Article regarding the code:
http://progressivecoder.com/event-sourcing-and-cqrs-with-axon-and-spring-boot-part-2//
In the application:
I'm having a hard time understanding how in the example, an Event is stored inside the EventStore. In AccountQueryServiceImpl, an EventStore is created and used to read the events from. But how is the EventStore populated exactly? (The service class is called by a REST Endpoint)
In regards to the JPA repository accountRepository, a new Account is persisted by using the save() method in AccountQueryEntityManager whenever a BaseEvent is fired, but I can't find a line of code where something is being appended to the EventStore.
AccountQueryServiceimpl
@Service
public class AccountQueryServiceImpl implements AccountQueryService {
private final EventStore eventStore;
private final AccountRepository accountRepository;
public AccountQueryServiceImpl(EventStore eventStore, AccountRepository
accountRepository) {
this.eventStore = eventStore;
this.accountRepository = accountRepository;
}
@Override
public List<Object> listEventsForAccount(String accountNumber) {
return eventStore.readEvents(accountNumber).asStream().map( s ->
s.getPayload()).collect(Collectors.toList());
}
@Override
public AccountQueryEntity getAccount(String accountNumber) {
return accountRepository.findById(accountNumber).get();
}
}
AccountQueryEntityManager
@Component
public class AccountQueryEntityManager {
@Autowired
private AccountRepository accountRepository;
@Autowired
@Qualifier("accountAggregateEventSourcingRepository")
private EventSourcingRepository<AccountAggregate> accountAggregateEventSourcingRepository;
@EventSourcingHandler
void on(BaseEvent event){
persistAccount(buildQueryAccount(getAccountFromEvent(event)));
}
private AccountAggregate getAccountFromEvent(BaseEvent event){
return accountAggregateEventSourcingRepository.load(event.id.toString()).getWrappedAggregate().getAggregateRoot();
}
private AccountQueryEntity findExistingOrCreateQueryAccount(String id){
return accountRepository.findById(id).isPresent() ? accountRepository.findById(id).get() : new AccountQueryEntity();
}
private AccountQueryEntity buildQueryAccount(AccountAggregate accountAggregate){
AccountQueryEntity accountQueryEntity = findExistingOrCreateQueryAccount(accountAggregate.getId());
accountQueryEntity.setId(accountAggregate.getId());
accountQueryEntity.setAccountBalance(accountAggregate.getAccountBalance());
accountQueryEntity.setCurrency(accountAggregate.getCurrency());
accountQueryEntity.setStatus(accountAggregate.getStatus());
return accountQueryEntity;
}
private void persistAccount(AccountQueryEntity accountQueryEntity){
accountRepository.save(accountQueryEntity);
}
}
Upvotes: 1
Views: 1271
Reputation: 7275
Axon Framework aims to provide you easy building blocks to work with DDD, CQRS and Event Sourcing. Through this, a lot of plumbing is provided out of the box to make your life on that road as easy as possible.
What's good to note to get a grasp how this works, is to assuming the shared sample focuses on all three paradigms. In such a scenario, you'd use Events as the synchronizing means between the Command and Query side of your application (typically in the form of distinct models).
The Command side of the app would deal with business validation by handling commands and validating it's own state. As a result of that, an event will be published. This in a typical Axon application occurs from within the lifecycle of an Aggregate. To not have the framework user require to wire the EventStore
or EventBus
directly, with all the added complexity of making sure the event is stored with the right sequence, Axon provides the AggregateLifecycle#apply(Object)
method.
Under the hood it's through this that the framework will be able to automatically find the EventBus
used to publish the event on, which will end up in the EventStore
too.
Side notes
Upvotes: 2