Instead of writing to an outbox table, a service publishes an OrderPlaced event to Kafka first, and a consumer inside that same service subscribes to its own topic to then update its local 'orders' read table. What is this approach called, and how does it avoid the dual-write problem?
answer
- publish is the source of truth, DB update is a reaction
- service consumes its own topic
- no separate direct-DB-write branch
- adds latency/eventual consistency even for the originator
- depends entirely on broker durability, no local TX fallback
basics
~20 sThis is called 'listen to yourself' - the service treats publishing the event as the single source of truth, and only updates its own database in reaction to consuming that event back, so there's just one write path instead of two independent ones.
solid answer
~40 sIn the 'listen to yourself' pattern, the service that originates a state change publishes the event to the broker as the primary, authoritative action, then subscribes to its own event stream and applies the resulting database update inside its consumer handler - the same code path any other downstream consumer would use. Because the DB write only ever happens as a reaction to a successfully published event, there's no separate 'also call the DB directly' branch that could race or diverge from publishing. The trade-off is that the service's own read model now lags behind the request by at least one round trip through the broker, and the pattern is fully dependent on the broker's durability and ordering guarantees since there's no local transactional fallback if the publish never lands.
go deeper
Not expected to know this pattern by name; credit for recognizing 'publish first, then update the database in response' as one coherent approach if described.
Should be able to describe the mechanism (publish, then self-consume to write DB) once prompted, even if the term 'listen to yourself' isn't recalled.
Should name the pattern, explain why it avoids dual writes, and articulate the latency/eventual-consistency trade-off versus outbox.
Should weigh listen-to-yourself against outbox for a concrete scenario (e.g., a service with strict read-your-writes requirements on its own API) and explain why outbox is usually preferred when the originating service needs a synchronous, consistent response.
## What the pattern is The 'listen to yourself' pattern is a second, structurally different way to achieve the same goal as the transactional outbox: atomically tying a state change to an event about that state change, without a distributed transaction across a database and a broker. - **Outbox** achieves atomicity by making the event durable in the same local transaction as the state (and delivering it out-of-band afterward). - **Listen-to-yourself** achieves it by removing the second write path entirely - there is only ever one authoritative action, publishing the event, and every effect of that action, including the originating service's own database update, is derived from consuming it back. ## The mechanism, step by step Concretely: when a client asks the order service to place an order, 1. the service does **not** call `db.save(order)` at all as part of handling that request; 2. instead, it publishes an `OrderPlaced` event to the broker as essentially the entire synchronous response to the request (the response to the client may be a 202 Accepted, or the service may wait for its own consumer to finish before responding); 3. separately, the same service runs a consumer subscribed to its own `OrderPlaced` topic, and it is that consumer handler - not the original request handler - that actually performs `db.save(order)`. Any other service (shipping, notifications) subscribes to the identical topic and does the analogous thing for its own concerns. From the broker's point of view, the originating service is just another consumer of its own event. ## Why this avoids dual writes Why does this avoid dual writes? Because dual-write risk specifically comes from two independent operations that can each fail on their own, leaving the system in a state where one happened and the other didn't. Listen-to-yourself has only one operation with independent failure risk - the publish - and the database write is not an independent action at all; it's a deterministic reaction to consuming a message that, once durably in the broker, will keep being redelivered until it's successfully processed. There's no window where 'the event went out but the DB never got updated,' because the DB update literally cannot happen except by consuming that event. ## The trade-offs The trade-offs are real and different in character from outbox's trade-offs. - **Latency and consistency shape.** First, with outbox, the primary request path is synchronous and consistent (the order is genuinely committed to the DB by the time the client gets a response; publishing merely trails behind asynchronously). With listen-to-yourself, even the originating service's own database write is now asynchronous - a client that calls place-order and then immediately calls get-order on the very same service is not guaranteed to see it, because the read model is only populated once the self-consumption completes. This **read-your-own-writes** gap on the originating service is often the deciding factor against this pattern for user-facing APIs with strict consistency expectations. - **No local transactional fallback.** Second, if the broker loses the message (misconfigured acks, an under-replicated partition losing its leader before replication completed) before this service's own consumer ever sees it, the state change never happens anywhere, including in the service's own database - the system's only source of truth was the broker, so the whole design leans entirely on the broker's durability guarantees, with no independent record to reconcile from. With outbox, by contrast, the outbox row's existence in a durable relational database is itself a fallback record, independent of the broker having received anything yet. ## Where it actually shows up Because of these trade-offs, listen-to-yourself is typically chosen as an alternative implementation strategy to outbox, not layered on top of it, and is comparatively less common in mainstream microservice practice than outbox specifically because most services do want a synchronous, consistent primary write path and can't tolerate their own reads lagging their own writes by a broker round-trip. It tends to show up in designs that are already fundamentally **event-sourced or CQRS-oriented**, where the write side is naturally 'append event, derive read model asynchronously' regardless of which service is doing the deriving - in those architectures the pattern isn't adding new asynchrony, it's just reusing the same event stream the design already treats as the source of truth.
- How does listen-to-yourself compare operationally to the transactional outbox pattern?Outbox keeps the primary state change synchronous and transactionally safe, with publishing happening asynchronously afterward via a relay; listen-to-yourself makes the state change itself asynchronous, since even the originating service's own database write waits on consuming the event back. Outbox needs an extra table plus relay/CDC infrastructure; listen-to-yourself needs no outbox table but pushes every write - including the originator's own - through the broker's latency and durability characteristics.
- What happens if the broker loses the event before this service's own consumer sees it?The service's own local state never gets updated either, since there's no independent DB write path - so a client that just made the request may see it as accepted (e.g., a 202) but the order never actually appears anywhere, including in the originating service's own store. This makes the pattern fully dependent on the broker's durability guarantees (e.g., Kafka's replication and acks=all) with no local transactional safety net.
- Is listen-to-yourself commonly used as a total replacement for outbox, or alongside it?It's typically an alternative implementation strategy rather than something layered on top of outbox - teams pick one mechanism to achieve atomicity between state and event, not both, since combining them would just add a redundant path. Outbox is far more common in practice because it keeps the primary request path synchronous and doesn't tie the caller's response latency to broker round-trips.
It's like a company announcing a policy change over the company-wide intercom before updating the employee handbook, and the person who wrote the policy updates their own copy of the handbook only after hearing the announcement over the intercom, same as everyone else - there's one authoritative broadcast, not a separate quiet update to their private copy.
saying these in an interview costs you the question
- Confuses this with 'a service consuming its own events for caching' as an incidental optimization rather than the core write path
- Doesn't recognize that this makes even the originating service's write asynchronous
- Assumes it still needs an outbox table
- Can't explain why there's no dual-write risk once this is in place