# Transactional Outbox & Deduplication — Lab M11

## 01. Bài toán
Writing DB then publishing Kafka can lose events on crash; delivery can be repeated.

## 02. Vấn đề
Without durable state, crashes, duplicates or reorgs can silently corrupt payment records.

## 03. Giải pháp
Persist domain update plus outbox record atomically; retry publication and deduplicate by event ID.

## 04. Lý thuyết
- Distinguish blockchain consensus from your database transaction boundary.
- Make replay and crash recovery deterministic, using stable identifiers.
- For production use PostgreSQL transactions, unique constraints, Kafka delivery semantics, and RPC provider failover.

## 05. Lab từng bước
1. Open `solution/OutboxDemo.java`; identify persistent state and failure transition.
2. Run `cd solution && bash test.sh` (requires JDK 21).
3. Observe the printed PASS assertion and use `java -ea` to enable Java assertions.
4. Change the failure scenario and verify the invariant fails as expected.
5. Implement the same algorithm in `starter/` without looking at solution.

## 06. Expected output
See `expected-output.md`; a PASS line proves only the educational in-memory scenario.

## 07. Exercises
Model publisher crashes after sending but before marking published. Explain DB uniqueness and Kafka offset commit order.

## 08. Interview
- Why is at-least-once delivery insufficient without idempotency?
- What state must be persisted atomically to survive crashes?
- How does a blockchain reorganization affect this component?
- Which assumptions of the in-memory simulation fail in production?

## Production extension
Replace the in-memory store with PostgreSQL and test process restart, transaction rollback, concurrent workers, and a real Anvil RPC. This extension is **not implemented** in the reference demo.
