Repository navigation
feat(notification): tell passengers what happened to their journey, closing US-009, US-010, US-011 and EPIC-05 - #30
Merged
Merged
Conversation
Adds notification-service, which listens to booking.created.v1, payment.succeeded.v1 and checkin.completed.v1, composes the message and hands it to a channel. Nobody is actually told anything: no service here holds an email address, a phone number or a name, so the channel is a port whose adapter writes a log line, and every send is recorded with the moment the channel took it (ADR-017). booking-service gains an outbox and publishes booking.created.v1. ADR-001 said it would never need one, because everything it could announce another service already knew; a booking having been made is the case that broke that, since only booking-service can say it. The service had no transaction machinery at all, and a BEFORE_COMMIT listener published outside a transaction does not run and does not say so. payment.succeeded.v1 and payment.failed.v1 now carry the passenger. The alternatives were a synchronous call from inside the consumer, or remembering the booking from another topic with no ordering between them, and both are argued in ADR-018. Payment itself stores nothing new. Fixes docker-compose: checkin-service was never given the broker address, so its outbox could not deliver outside the end-to-end stack. The check-in smoke script found it, which the e2e suite cannot: it builds its own stack and wires each service itself. The smoke scripts now refuse a switch they do not declare, wait for the relay before reporting, print the reason behind a refusal, and stop when the stack is down instead of announcing a flight they never created. scripts/README.md records what they are for.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A passenger is now told when their booking is made, when their payment goes through, and when they have checked in. The new notification-service listens to
booking.created.v1,payment.succeeded.v1andcheckin.completed.v1, composes the message, records it and hands it to a channel. Nothing calls it and it calls nobody. Two other services had to change to make that possible: booking-service gained an outbox, because a booking having been made is something only it can say, and the payment events gained apassengerId, because a payment message could not otherwise be addressed to anyone. Nobody is actually told anything, and the code says so rather than pretending: no service here holds an email address, a phone number or a name, so the channel is a port whose adapter writes a log line and every send is recorded with the moment the channel took it.Changes
notification-service (new)
Three Maven modules in the ADR-006 shape, on port 8085 with a database of its own. The domain holds
Notification,NotificationTypeandNotificationMessage, which is where the wording lives.One listener per topic, each mapping its event into a command.
NotifyPassengerServicecomposes the message,NotificationRecorderclaims the event id and writes the notification in one transaction, and only then is the channel called.Written, sent, marked, in that order. A crash between the write and the send leaves a notification nobody received, which the partial index over
sent_at IS NULLmakes findable. The other order would send one and forget it had.processed_eventsper ADR-014, which matters more here than anywhere: a booking confirmed twice is the same booking, and a passenger notified twice is a passenger who noticed.No read endpoint. This is the record of a send, not an inbox, and no story asks for one.
booking-service
An outbox, a relay, and
booking.created.v1carrying the passenger, the seats and the total.BookingRecorderholds the write and the announcement in one transaction, andCreateBookingServicedelegates to it. This service had no transaction machinery at all: its use cases relied on@Transactionalin the adapters, which is not enough, because a@TransactionalEventListener(BEFORE_COMMIT)published outside a transaction does not run and does not say so.Announced only when the write happened. A repeated request carrying a key that is already taken gets the booking that exists, and nothing new to tell anyone about.
payment-service
payment.succeeded.v1andpayment.failed.v1carrypassengerId.BookingToPaykeeps the field booking-service already returned and payment discarded,PayBookingServicehands it to the recorder, andPaymentSettledcarries it to the listener.Paymentdoes not store it. No column, no migration. A payment is money, and whose booking it settles is booking-service's business.Infrastructure and docs
Compose gains notification-db and notification-service, and gains the broker address checkin-service was missing (see below).
NotificationJourneyE2ETestjoins the e2e module, whose stack now starts eleven containers.scripts/smoke-notifications.ps1walks a whole journey and shows what the passenger was told at each step.scripts/README.mdrecords what the scripts are for.ARCHITECTURE.mdgains ADR-017 and ADR-018, and ADR-001 is corrected.Notes
ADR-001 was wrong and now says so. It claimed booking-service would never need an outbox, because everything it could announce another service already knew. Notifications are the case that does not fit: only booking-service can say a booking was made, and something finally needed to hear it. The record is amended rather than quietly rewritten.
Getting the passenger onto a payment notification had three candidates, argued in ADR-018. A synchronous read from inside the consumer was rejected: when booking-service is down the consumer either blocks its partition or drops the notification. Remembering the booking from
booking.created.v1was rejected for a subtler reason: they are different topics, Kafka orders within a partition and not across topics, and in production minutes pass between booking and paying while in the end-to-end test milliseconds do. A failure that only appears when things are fast is the kind that appears in a demonstration.The contract stayed at v1. Adding a field does not break a consumer that ignores what it does not know, and booking-service's copy of the payment event declares two fields and says exactly that in its own javadoc.
Two defects surfaced during this epic, and it is worth recording which found them:
docker-compose.yml, so its outbox could not deliver anything outside the end-to-end stackLeft out on purpose, and recorded in ADR-017 rather than papered over:
processed_eventsis never purged, the same gap booking-service already has.Testing
./mvnw -B clean verifyruns 402 tests with zero failures; the four skips are the e2e classes staying behind their flag. Per module against what was there before: booking-infrastructure 40 to 55, payment-infrastructure 41 to 43, and notification's two modules adding 15 and 20../mvnw -B verify -pl e2e-tests "-Dairline.e2e=true"runs 35 e2e tests against the real stack, up from 28.Against compose, all five smoke paths were run:
smoke-notifications.ps1ends with three notifications for one booking, each caused by a different service, all of them sent;smoke-checkin.ps1and its-TooEarlypath, the second now printing the refusal in full, title, reason and problem type; andsmoke-payment.ps1with-Decline, whose output also showspassengerIdpresent in a realpayment.failed.v1message.The tests that carry the new guarantees:
NotificationTestpins the wording, including that a booking message does not call an unpaid booking confirmed;NotifyPassengerSliceTestcovers all three notifications, that a redelivered message does not produce a second, and that a failing channel leaves the notification behind unsent;BookingOutboxSliceTestproves the announcement shares the transaction that wrote the booking and that a repeated request announces nothing;NotificationJourneyE2ETestis the only place where what three services publish is read by the thing that consumes it.Closes #14 (US-009)
Closes #15 (US-010)
Closes #16 (US-011)
Closes #13 (EPIC-05)