Domain event publishing is a notification given by a domain object to let other objects know that it has completed an operation. Event publishing seeks to decouple its own objects from external objects at the code level and reduce technical code intrusion.
First, manually publish events
// Entity definition
@Entity
public class Department implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer departmentId;
@Enumerated(EnumType.STRING)
private State state;
}
// Event definition
public class DepartmentEvent {
private Department department;
private State state;
public DepartmentEvent(Department department) {
this.department = department; state = department.getState(); }}// Domain services
@Service
public class ApplicationService {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Autowired
private DepartmentRepository departmentRepository;
@Transactional(rollbackFor = Exception.class)
public void departmentAdd(Department department) {
departmentRepository.save(department);
// Event release
applicationEventPublisher.publishEvent(newDepartmentEvent(department)); }}Copy the code
Using applicationEventPublisher. PublishEvent events in the field of field service processing after the completion of the release, this method requires explicit publishing events in the business code, and in the field of service introduced applicationEventPublisher class, However, the domain service itself has a certain degree of invasion, but high flexibility.
2. Automatically publish events
// Entity definition
@Entity
public class SaleOrder implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer orderId;
@Enumerated(EnumType.STRING)
private State state;
// Return type definition
@DomainEvents
public List<Object> domainEvents(a){
return Stream.of(new SaleOrderEvent(this)).collect(Collectors.toList());
}
// Callback after the event is published
@AfterDomainEventPublication
void callback(a) {
System.err.println("ok"); }}// Event definition
public class SaleOrderEvent {
private SaleOrder saleOrder;
private State state;
public SaleOrderEvent(SaleOrder saleOrder) {
this.saleOrder = saleOrder; state = saleOrder.getState(); }}// Domain services
@Service
public class ApplicationService {
@Autowired
private OrderRepository orderRepository;
@Transactional(rollbackFor = Exception.class)
public void saleOrderAdd(SaleOrder saleOrder) { orderRepository.save(saleOrder); }}Copy the code
Using the @ DomainEvents defines the type of event to return, must be a collection, using the @ AfterDomainEventPublication define events after the release of the callback. This approach is completely decoupled from domain services by the fact that the object type is defined in the entity, with no intrusion. The system automatically calls event publishing after orderRepository.save(saleOrder), and the delete method does not call event publishing.
3. Event monitoring
@Component
public class ApplicationEventProcessor {
@EventListener(condition = "#departmentEvent.getState().toString() == 'SUCCEED'")
public void departmentCreated(DepartmentEvent departmentEvent) {
System.err.println("dept-event1:" + departmentEvent);
}
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, condition = "#saleOrderEvent.getState().toString() == 'SUCCEED'")
public void saleOrderCreated(SaleOrderEvent saleOrderEvent) {
System.err.println("sale-event succeed1:" + saleOrderEvent);
}
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT, condition = "#saleOrderEvent.getState().toString() == 'SUCCEED'")
public void saleOrderCreatedBefore(SaleOrderEvent saleOrderEvent) {
System.err.println("sale-event succeed2:" + saleOrderEvent);
}
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void saleOrderCreatedFailed(SaleOrderEvent saleOrderEvent) {
System.out.println("sale-event failed:"+ saleOrderEvent); }}Copy the code
1. Use@EventListener
Listen for an event
@EventListener has no transaction support and can be monitored as soon as an event is emitted
@Transactional(rollbackFor = Exception.class)
public void departmentAdd(Department department) {
departmentRepository.save(department);
applicationEventPublisher.publishEvent(new DepartmentEvent(department));
throw new RuntimeException("failed");
}
Copy the code
The previous situation will cause the transaction to fail and roll back, but the event monitoring end has been running, which may cause data inconsistency
2. Use@TransactionalEventListener
Listen for an event
TransactionPhase.BEFORE_COMMIT
Before transaction commitTransactionPhase.AFTER_COMMIT
After the transaction is committedTransactionPhase.AFTER_ROLLBACK
After transaction rollbackTransactionPhase.AFTER_COMPLETION
After transaction completion
AFTER_COMMIT uses transactionPhase.after_COMMIT to execute event listening methods after the transaction completes to ensure data consistency
3. TransactionPhase.AFTER_ROLLBACK
Rollback transaction problems
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK, condition = "#departmentEvent.getState().toString() == 'SUCCEED'")
public void departmentCreatedFailed(DepartmentEvent departmentEvent) {
System.err.println("dept-event3:" + departmentEvent);
}
Copy the code
The AFTER_ROLLBACK method will only be executed if any other statement in the same transaction fails or if an explicit rollback is performed. If the save method fails, the rollback event will not be listened for.
4. @Async
Asynchronous event listening
- No such annotation event listens to the method in the same transaction as the main method.
- Using this annotation will leave the original transaction, and BEFORE_COMMIT cannot intercept the moment before the transaction commits
- This annotation requires coordination
@EnableAsync
Used together
Four,
Through the use of @ DomainEvents, @ TransactionalEventListener, event publishing in the field of effective solving cases, reduced the invasion of the business code, and as a step to solve the problem of data consistency. In the distributed architecture, event notifications are sent to other services through MQ. To solve the consistency problem and prevent the processing failure of other services, you can save the event to the database and try again.
Five, the source
Gitee.com/hypier/barr…