Skip to content

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 01 Oct 11:56
· 56 commits to main since this release

Reliable domain events: a transactional outbox on Spring Modulith's event publication registry, delivery to Kafka, RocketMQ, AMQP and JMS, event contracts, and idempotent consumers. The repository contract now reports optimistic-lock conflicts.

Added

  • @IntegrationEvent in com.ddk.core.domain marks domain events for delivery outside the process and names their target and message key, without framework annotations in the domain layer
  • ddk-event-starter hands @IntegrationEvent events to Spring Modulith's event externalization when it is on the classpath, so they are recorded in the business transaction and delivered after commit; the BOM manages Spring Modulith 2.1
  • @IntegrationEvent(id = ...) puts the event's own identifier into the ddk-event-id message header, so it stays stable across resubmissions
  • @IntegrationEvent(type, version) and the ddk-event-type / ddk-event-version headers give each integration event a stable contract name and version
  • The event starter verifies @IntegrationEvent declarations at startup (accessors, version, duplicate contracts) instead of failing at delivery time
  • The event starter delivers @IntegrationEvent events to RocketMQ when rocketmq-client is on the classpath and a DefaultMQProducer exists or ddk.event.rocketmq.name-server is set: topic:tag targets, per-key queue ordering, contract headers as user properties; the BOM manages the client version
  • IdempotentConsumer (ddk.event.inbox.enabled=true) runs a handler once per consumer and message ID, registering the message in the handler's transaction behind a savepoint
  • ConcurrentUpdateException (CONCURRENT_UPDATE), which the web starter maps to 409 Conflict
  • IdentifierJacksonModule writes typed identifiers such as UserId as their raw value and reads them back through the subclass's of(...) factory; the web and event starters register it. Without it Jackson wrote identifiers as {}, so a serialized domain event lost its IDs
  • GenericRepositoryImpl accepts typed identifiers such as UserId and unwraps them to key values; the example and archetype skills use GenericRepository<User, UserId>

Fixed

  • GenericRepositoryImpl.update silently ignored an optimistic-lock conflict or a removed row and still published the aggregate's domain events; it now throws ConcurrentUpdateException and publishes nothing
  • updateAll checks each row's version instead of a batch update that cannot report per-row conflicts