Store the original domain event in the Namastack outbox
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 76/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- java, spring-boot
- Domain
- backend, distributed-systems
Research direction
Start with NamastackOutboxEventRecorder and trace the existing outbox handler that restores records for externalization. Verify that the recorder evaluates the key and schedules the original payload, while mapping occurs during delivery and the mapped payload reaches the configured broker without changing module-listener behavior.
Written by the indexing model from the issue text.
Description
Problem
NamastackOutboxEventRecorder applies the configured externalization mapping before it schedules an event in the outbox:
var target = configuration.determineTarget(payload);
var mapped = configuration.map(payload);
var routing = BrokerRouting.of(target, context);
var key = routing.getKey(mapped);
scheduleToOutbox(mapped, key);
This makes the Namastack outbox behave differently from the regular module-listener externalization flow. The outbox contains the mapped transport payload instead of the original domain event.
This causes several related problems:
- The mapped payload has to be serializable by the outbox serializer. A mapping to an Avro-generated type, for example, can fail although the original domain event is Jackson-friendly.
- Mapping and serialization failures occur while the business transaction is recorded instead of during outbox delivery. They therefore cannot be handled and reported as outbox delivery failures, such as through
failure_reason. - The outbox handler receives an already mapped object and passes it to the externalizer. Selection and mapping are then applied to that object again, which can skip the event or attempt to map it twice.
- The key stored with the outbox record is evaluated against the mapped payload rather than the original event.
This was originally reported in namastack/namastack-outbox#466.
Example
@Externalized("orders::#{getOrderId()}")
record OrderCompleted(UUID orderId) {
public UUID getOrderId() {
return orderId;
}
}
@Bean
EventExternalizationConfiguration externalization() {
return EventExternalizationConfiguration.externalizing()
.select(EventExternalizationConfiguration.annotatedAsExternalized())
.mapping(OrderCompleted.class, this::toAvro)
.build();
}
Publishing OrderCompleted currently causes toAvro(…) to be invoked by the recorder. The resulting Avro object is serialized into the Namastack outbox and later handed back to the externalizer.
Expected behavior
The recorder should:
- determine whether the original event is externalized;
- determine the outbox record key from the original event;
- schedule the original event in the Namastack outbox without applying the externalization mapping.
When Namastack delivers the record, the existing outbox handler should pass the restored domain event to the externalizer. The normal externalization pipeline can then select, map and publish it exactly once.
This gives module-listener and Namastack outbox mode the same mapping lifecycle while keeping mapping and transport serialization failures within outbox processing.
Suggested change
Change NamastackOutboxEventRecorder to evaluate the outbox key against and schedule the original payload:
var target = configuration.determineTarget(payload);
var routing = BrokerRouting.of(target, context);
var key = routing.getKey(payload);
scheduleToOutbox(payload, key);
No change to the common externalizer or broker transports is required.
Acceptance criteria
- The Namastack outbox stores the original domain event.
- The externalization mapper is not invoked while the outbox record is created.
- The outbox record key is evaluated against the original event.
- The mapper is invoked during delivery by the outbox handler.
- The mapped payload is sent to the configured broker.
- Existing module-listener behavior remains unchanged.
- Dominant language
- Java
- Stars
- 1.2k
- Forks
- 222
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from spring-projects/spring-modulith
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
spring-projects/spring-modulith#1896 ·
-
in: infrastructure type: dependency-upgrade
spring-projects/spring-modulith#1889 · 1 assignee ·
-
in: infrastructure type: dependency-upgrade
spring-projects/spring-modulith#1888 · 1 assignee ·
-
in: infrastructure type: dependency-upgrade
spring-projects/spring-modulith#1887 · 1 assignee ·
-
Release 2.2 M2 Openin: infrastructure type: task
spring-projects/spring-modulith#1882 · 1 assignee ·
All issues in spring-projects/spring-modulith
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
bug needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
Difficulty 1/5 Under an hour Newbie friendliness 94/100
objectionary/hone-maven-plugin#1061 ·
-
Fix before the next release
Difficulty 2/5 1-3 hours Newbie friendliness 78/100