[{"data":1,"prerenderedAt":40},["ShallowReactive",2],{"profile-en":3,"article-outbox-pattern-na-pratica-en":27},{"brandName":4,"fullName":5,"headline":6,"manifesto":7,"contactIntro":11,"avatar":12,"manifestoImage":13,"social":14},"Danilo Fernando","Danilo Fernando - Senior Software Engineer","Well-built software, reliable integrations, and architecture designed to last.",[8,9,10],"My journey is guided by the belief that code is just a tool to solve complex business problems. With solid experience in the Java and Spring ecosystems, I focus on systems that work AND are easy to maintain and evolve.","Specialised in robust APIs and critical integrations, I believe technical maturity shows up in balanced decisions that weigh innovation against real business needs.","I chase real impact: Clean Code is not aesthetics, it is an economic necessity for long-term product sustainability.","I'm always open to new professional opportunities, technical exchange, or conversations about architecture and software engineering.","\u002Fimages\u002Fprofile\u002Fdanilo.webp","\u002Fimages\u002Fprofile\u002Fdanilo_manifesto.webp",[15,19,23],{"kind":16,"label":17,"href":18},"email","Email","mailto:danilo.bossanova@hotmail.com",{"kind":20,"label":21,"href":22},"linkedin","LinkedIn","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fdanilo-fernando-dev\u002F",{"kind":24,"label":25,"href":26},"github","GitHub","https:\u002F\u002Fgithub.com\u002Fdanilobossanova",{"slug":28,"title":29,"excerpt":30,"category":31,"tags":32,"readTimeMinutes":36,"publishedAt":37,"author":4,"coverImage":38,"body":39},"outbox-pattern-na-pratica","Outbox pattern in practice with Spring Boot","How to guarantee at-least-once delivery without distributed transactions.","Architecture",[33,34,35],"events","spring-boot","kafka",9,"2025-03-01","https:\u002F\u002Fpicsum.photos\u002Fseed\u002Foutbox\u002F1200\u002F600","## The problem\n\nReliably publishing events next to a database transaction is one of the classic\npitfalls in distributed systems. If you write to Postgres and then publish to\nKafka:\n\n```java\n@Transactional\npublic void confirm(Order order) {\n    orderRepository.save(order);       \u002F\u002F ✅ inside DB transaction\n    kafkaTemplate.send(\"orders\", order); \u002F\u002F ❌ outside the transaction\n}\n```\n\nA crash between the two calls is enough for the DB to say *\"confirmed\"* while\nthe rest of the world never hears about it.\n\n## Outbox pattern\n\nThe idea is to write the event in the **same transaction**, in an `outbox`\ntable. A relay reads the table and actually publishes it.\n\n### Minimal schema\n\n```sql\nCREATE TABLE outbox (\n    id           UUID PRIMARY KEY,\n    aggregate    VARCHAR(64)  NOT NULL,\n    event_type   VARCHAR(128) NOT NULL,\n    payload      JSONB        NOT NULL,\n    created_at   TIMESTAMPTZ  NOT NULL DEFAULT now(),\n    published_at TIMESTAMPTZ\n);\nCREATE INDEX outbox_unpublished_idx\n    ON outbox (created_at) WHERE published_at IS NULL;\n```\n\n### Relay\n\nA simple job reads unpublished events, ships them to the broker and marks them\nas published. What matters:\n\n1. **Consumer idempotency** — the relay will republish on failure.\n2. **Lag monitoring** — alert if `outbox` grows faster than it drains.\n3. **Controlled batches** — don't lock the DB pulling 10k rows at a time.\n\n### Gotchas\n\n- Never rely on strict broker ordering; partition by `aggregate_id`.\n- Don't dead-letter into the same table — create `outbox_failed`.\n- Always record the payload schema version.\n\n## When to avoid\n\nIf your system doesn't need at-least-once guarantees, outbox is overkill.\nSpring's `@TransactionalEventListener` covers 80% of cases.",1776457051916]