Interview question
Save an order and outbox message together
Persists domain state and its outbox message in one SaveChanges transaction without publishing inside the request transaction.
TL;DR
Persists domain state and its outbox message in one SaveChanges transaction without publishing inside the request transaction.
Outbox persistence, transaction boundaries, serialized events, idempotency, and separation from external publication.
Practice the problem like a real interview: restate, reason, implement, and test.
Create a new order and a corresponding OutboxMessage in the same AppDbContext. One SaveChangesAsync should persist both atomically. A separate worker will publish pending outbox rows; the request must not call the message broker directly.
If the outbox insert fails, the order insert rolls back. If the database commit succeeds but the process stops immediately afterward, the pending outbox row remains for the worker.
I create the aggregate and a small integration-event payload, add both rows to the same context, and save once. EF wraps one SaveChanges operation in a transaction when multiple statements are required. The request returns after durable state exists; a worker owns publication, retries, and marking the outbox row processed.
public static async Task<PlaceOrderResult> PlaceOrderAsync(
AppDbContext db,
PlaceOrder command,
CancellationToken cancellationToken)
{
var order = Order.Place(
command.CustomerId,
command.Lines,
command.IdempotencyKey);
var integrationEvent = new OrderPlacedV1(
order.Id,
order.CustomerId,
order.Total,
order.PlacedAtUtc);
var outbox = OutboxMessage.Create(
Guid.NewGuid(),
"orders.placed.v1",
JsonSerializer.Serialize(integrationEvent),
order.PlacedAtUtc);
db.Orders.Add(order);
db.OutboxMessages.Add(outbox);
await db.SaveChangesAsync(cancellationToken);
return new PlaceOrderResult(order.Id);
}
One SaveChanges call creates a local database transaction around order, line, and outbox inserts. A unique idempotency key protects repeated requests, while a unique outbox id and worker-side publish strategy handle delivery retries. Exactly-once external delivery is not assumed.
Move to the linked follow-up, next path step, prerequisite, or deeper variant.
Practice the next layer of the same subject.