Start here. This is the direct spoken answer to practice first.
Why this question matters
A local database transaction cannot atomically commit an independent broker publish. The outbox makes the intent to publish part of the same durable state as the business change, then crosses the broker boundary asynchronously.
I write the business change and an outbox row in the same database transaction. After commit, a dispatcher reads pending rows and publishes them. If the process crashes before publishing, the row remains for retry; if the database transaction rolls back, there is no message to publish. This closes the gap between saving data and remembering that an integration event still needs to be sent.