Skip to content
Search lessons, topics, tests…
Esc

    ↑ ↓ moveEnter openEsc close

    Module 1 · Ownership, Ordering and Failure · Lesson 2 of 2

    Mediator: Make Booking Workflow Rules Explicit

    Watch

    Put the rule in one visible place

    A booking request must reserve capacity, authorize a payment, and only then confirm the booking. A mediator-style coordinator makes these collaboration rules explicit. Individual components expose focused operations; they do not need to call every other participant directly. The goal is clear ownership, not a mandatory mediator library.

    For this example, the invariant is: a confirmed booking has both a valid reservation and successful authorization. An email is a consequence of confirmation, not proof that the invariant holds.

    Walk the success and failure paths

    StepSuccessFailure response
    Reserve capacityKeep a reservation reference.Return unavailable; do not authorize payment.
    Authorize paymentKeep an authorization reference.On a definite decline, release the reservation and record failed cleanup for retry. On an ambiguous timeout, reconcile before deciding whether to release or continue.
    Confirm bookingStore the confirmed state.Reconcile the stored references; do not blindly repeat effects.
    Notify customerRecord notification progress.Retry notification without creating a second booking.

    These are illustrative business rules. A real product must decide reservation expiry, whether authorization or capture is intended, cancellation policy, and who owns reconciliation. A coordinator does not create a transaction across independent external systems by itself.

    Why a timeout is ambiguous

    Suppose the coordinator sends an authorization request and times out. The provider may have applied the request even though the response never arrived. Retrying with a new operation identity can create another effect. Use the same agreed idempotency identity for retries of the same logical operation and verify status where supported. Define the retention period, payload-mismatch handling and concurrency behavior of that identity.

    A local database transaction can protect local state changes, but cannot make an arbitrary network call atomic with that transaction. If you choose an outbox or durable workflow, specify which state is committed together, how dispatch is retried and how consumers handle duplicates. Pattern names do not remove these obligations.

    Keep the coordinator focused

    The coordinator owns sequencing and decisions that connect participants. A pricing component still owns its calculation, and a reservation component owns its capacity rule. Avoid a single coordinator accumulating every unrelated business rule in the application. If the whole operation is a small, fixed sequence, a clearly named application-service method may be sufficient.

    Exercise and worked answer

    Confirmation succeeds but the email provider is unavailable. Should the system cancel the booking and ask the user to pay again?

    Under the stated invariant, no: notification failure does not invalidate the successful reservation and authorization. Keep the booking confirmed, make its state visible to the user, and retry notification with duplicate-safe handling. If the business has a different rule, state it explicitly and test the compensation path.

    Compare with Observer

    Independent analytics or dashboard updates can observe a confirmed-booking event. Required reservation-before-authorization ordering belongs in the workflow. Use the workbook to practice distinguishing a notification from a dependency.

    Before accepting a design, test one unavailable dependency, one ambiguous timeout, one duplicate request and one cleanup failure.

    Trace a recovery record

    For each logical booking, retain its stable operation identity, requested payload, reservation reference and expiry, authorization reference or pending result, completed steps, and required cleanup. If the same identity arrives with a different payload, reject the conflict rather than quietly reusing an earlier result. Concurrent requests must claim the logical operation atomically; a read-then-insert pre-check can race.

    Example: reservation R is active; authorization request A times out; the coordinator records an unknown outcome. It queries or replays A using the provider's supported identity contract. If A succeeded, it checks that R is still valid before confirmation. If R expired, it follows the business-defined re-reservation or compensation policy instead of confirming an invalid booking. If a release or reversal fails, durable recovery work remains pending and observable.

    An outbox can commit local booking state and a pending notification together. A worker may send the notification again after crashing before recording completion, so the recipient still needs duplicate-safe handling. Compensation is a new business operation, not a time machine that rolls back arbitrary external effects. See Microsoft Compensating Transaction.

    This trace is a design exercise; no payment, reservation or database operation is executed here.

    Video companion

    Dev Leader: Mediator Design Pattern In Action!, by Nick Cosentino (2023), demonstrates an in-process C# chat mediator that routes messages between users. See the creator tutorial for supporting discussion.

    Use the demo to identify who owns communication rules, then apply that ownership to reserve, authorize and confirm. The chat example does not implement booking compensation, idempotency or durable recovery. It is not a current MediatR package setup guide. Observer and Mediator solve different relationship problems; neither is universally more loosely coupled or better.

    Analogy: a booking desk

    A booking desk asks the venue to hold capacity, asks the payment desk to authorize, and confirms only when the required evidence is present. Each specialist owns its own rule. The booking desk owns the order of calls and the recovery plan, so the venue and payment desk need not call every other participant.

    If the phone cuts out, the desk cannot assume the payment desk did nothing. It looks up the same request and records unfinished recovery. Sending a confirmation email is a separate consequence once the booking invariant holds.

    The analogy explains responsibility, not transaction guarantees. Real services can crash, disagree temporarily, expire reservations or apply duplicate requests. A mediator class alone does not supply durable state or exactly-once effects.

    Mediator cheat sheet

    DecisionUseful rule
    FitCentralize interaction rules when collaborators otherwise depend on one another
    InvariantConfirm only with valid reservation and successful authorization under this contract
    Local rulesPricing owns prices; reservation owns capacity; coordinator owns the sequence
    Unavailable capacityStop before payment authorization
    Definite declineRelease the reservation; track failed cleanup
    Ambiguous timeoutKeep the outcome unknown; reconcile the same logical operation
    Duplicate requestDefine identity scope, payload conflicts, retention and concurrent claims
    Crash recoveryPersist references, completed steps and pending compensation
    OutboxAtomically record local state plus intent; expect duplicate dispatch
    Notification failurePreserve a valid confirmed booking; retry notification separately
    Simpler alternativeA focused application-service method can be enough

    Interview test plan

    Explain the expected state after unavailable capacity, definite decline, ambiguous authorization, expired reservation, duplicate concurrent request, failed compensation and notification redelivery. For every case, name the component that owns the next action.

    Practice

    Sign in to mark lessons done and keep your place in the course.Sign in