TIL

03. Working with Application Events

https://docs.spring.io/spring-modulith/reference/events.html

@Service
class OrderManagement(val inventory: InventoryManagement) {

  @Transactional
  fun complete(order: Order) {
    inventory.updateStockFor(order)
  }
}
@Service
class OrderManagement(val events: ApplicationEventPublisher, val dependency: OrderInternal) {

  @Transactional
  fun complete(order: Order) {
    events.publishEvent(OrderCompleted(order.id))
  }
}
@Component
class InventoryManagement {

  @Async
  @TransactionalEventListener
  fun on(event: OrderCompleted) { /* … */ }
}

Application Module Listener

@Component
class InventoryManagement {

  @Async
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  @TransactionalEventListener
  fun on(event: OrderCompleted) { /* … */ }
}
@Component
class InventoryManagement {

  @ApplicationModuleListener
  fun on(event: OrderCompleted) { /* … */ }
}

The Event Publication Registry

image

image

Spring Boot Event Registry Starters

Managing Event Publications

Event Publication Repositories

Event Serializer

Customizing the Event Publication Date

Externalizing Events

Supported Infrastructure

Fundamentals of Event Externalization

  1. 이벤트의 외부화 여부 결정
  2. @Externalized 어노테이션을 통해 외부화 이벤트 선택
  3. 이벤트 매핑 (선택 사항)
  4. 기본적으로 이벤트는 Jackson을 사용하여 JSON으로 직렬화되어 발행된다.
  5. 매핑 단계를 통해 개발자는 표현을 커스터마이징하거나 외부 브로커에 적합한 표현으로 대체할 수도 있다.
  6. 매핑 단계는 발행할 객체의 실제 직렬화보다 앞서 진행된다.
  7. 라우팅 대상 결정
  8. 메시지 브로커의 클라이언트는 메시지를 발행할 논리적 대상을 필요로 한다.
  9. 이 대상은 일반적으로 물리적 인프라(토픽 또는 대기열)를 식별하며 이벤트 타입에서 정적으로 파생되는 경우가 많다.
  10. @Externalized 어노테이션에 특별히 정의하지 않는한 기본적으로 appllication-local 타입 이름을 사용한다. 1. 즉 com.acme.app 기본 패키지가 있는 애플리케이션에서 com.acme.app.sample.SampleEventsample.SampleEvent에 게시된다.
    • 일부 브로커는 실제 대상 내에서 다른 용도로 사용되는 다소 동적인 라우팅 키를 정의할 수도 있다.
      • 기본적으로는 라우팅 키는 사용되지 않는다.

Annotation-based Event Externalization Configuration

@Externalized("customer-created::#{#this.getLastname()}")
class CustomerCreated {
  fun getLastname(): String {
    // …
  }
}
@Externalized("…::#{@beanName.someMethod(#this)}")

Programmatic Event Externalization Configuration

@Configuration
class ExternalizationConfiguration {

  @Bean
  fun eventExternalizationConfiguration(): EventExternalizationConfiguration {

    EventExternalizationConfiguration.externalizing()
      .select(EventExternalizationConfiguration.annotatedAsExternalized())
      .mapping(SomeEvent::class, it -> )
      .routeKey(WithKeyProperty::class, WithKeyProperty::getKey)
      .build()
  }
}

Testing published events

@ApplicationModuleTest
class OrderIntegrationTests {
  @Test
  fun someTestMethod(events: PublishedEvents) {
    // …
    var matchingMapped = events.ofType(OrderCompleted::class)
      .matching(OrderCompleted::getOrderId, reference.getId())

    assertThat(matchingMapped).hasSize(1)
  }
}