The Eventuate Tram framework enables a Java/Spring application to send messages as part of an database transaction. This enables an application atomically update state and send a message or a domain event. It is a foundation of ensuring data consistency within a microservice architecture.
Eventuate Tram (which was formerly known as Tarr) is described in more detail in my book Microservice Patterns. It provides several messaging abstractions:
-
messaging - send and receive messages over named channels
-
events - publish domain events and subscribe to domain events
-
commands - asynchronously send a command to a service and receive a reply
There are the following examples applications:
-
Todo list - a Java/JPA/Spring Boot Todo list application, which publishes domain event using Eventuate Tram
-
Customers and Orders, which is built using the [Eventuate Tram Sagas framework](https://github.com/eventuate-tram/eventuate-tram-sagas)
Don’t hesitate to create an issue or see
-
[Mailing list](https://groups.google.com/d/forum/eventuate-users)
-
[Slack](https://eventuate-users.slack.com). [Get invite](https://eventuateusersslack.herokuapp.com/)
-
[Contact us](http://eventuate.io/contact.html).
Send a message using MessageProducer
:
public interface MessageProducer {
void send(String destination, Message message);
}
Receive messages using:
public interface MessageConsumer {
void subscribe(String subscriberId, Set<String> channels, MessageHandler handler);
}
See this example of transactional messaging.
The domain event package builds on the core APIs.
Publish domain events using the DomainEventPublisher
interface:
public interface DomainEventPublisher {
void publish(String aggregateType, Object aggregateId, List<DomainEvent> domainEvents);
...
Subscribe to domain events using a DomainEventDispatcher
:
public class DomainEventDispatcher {
public DomainEventDispatcher(String eventDispatcherId,
DomainEventHandlers eventHandlers,
...) {
...
}
Handle the events using DomainEventHandlers
:
public class RestaurantOrderEventConsumer {
public DomainEventHandlers domainEventHandlers() {
return DomainEventHandlersBuilder
.forAggregateType("net.chrisrichardson.ftgo.restaurantservice.Restaurant")
.onEvent(RestaurantMenuRevised.class, this::reviseMenu)
.build();
}
public void reviseMenu(DomainEventEnvelope<RestaurantMenuRevised> de) {
See this example of transaction events.
Transaction commands are implemented using transactional messaging.
Send a command using a CommandProducer
:
public interface CommandProducer {
String send(String channel, Command command, String replyTo, Map<String, String> headers);
...
}
Subscribe to commands using a CommandDispatcher
:
public class CommandDispatcher {
public CommandDispatcher(String commandDispatcherId,
CommandHandlers commandHandlers) {
...
}
Handle commands and send a reply using CommandHandlers
:
public class OrderCommandHandlers {
public CommandHandlers commandHandlers() {
return CommandHandlersBuilder
.fromChannel("orderService")
.onMessage(ApproveOrderCommand.class, this::approveOrder)
...
.build();
}
public Message approveOrder(CommandMessage<ApproveOrderCommand> cm) {
ApproveOrderCommand command = cm.getCommand();
...
}
See this example of transactional commands.