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@EventListenerListen 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@TransactionalEventListenerListen for an event

  • TransactionPhase.BEFORE_COMMITBefore transaction commit
  • TransactionPhase.AFTER_COMMITAfter the transaction is committed
  • TransactionPhase.AFTER_ROLLBACKAfter transaction rollback
  • TransactionPhase.AFTER_COMPLETIONAfter transaction completion

AFTER_COMMIT uses transactionPhase.after_COMMIT to execute event listening methods after the transaction completes to ensure data consistency

3. TransactionPhase.AFTER_ROLLBACKRollback 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. @AsyncAsynchronous 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@EnableAsyncUsed 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…