Start here. This is the direct spoken answer to practice first.
Why this question matters
A notification system separates a business event from channel delivery. Preferences, templates, retries, deduplication, provider failure, and user trust determine whether email and in-app delivery remain reliable.
I would publish a notification request when a business event happens, then process delivery asynchronously. The system would store notification records, user preferences, channel choices, template data, and delivery status. Email sending would happen in a worker with retry and idempotency, while in-app notifications would be stored for the user to read later. The API would expose notification lists, unread counts, and mark-as-read operations.