Lesson 1 · Core NATS & the JetStream model

The NATS map

Two systems in one name — fast fire-and-forget pub/sub, and a durable persistence layer on top — plus where each sits next to Kafka in this repo.

Your win: distinguish core NATS from JetStream, place NATS against Kafka in this repo, and identify the pieces (golib, streams, subscribers) so every later lesson has a home.

What NATS is

NATS is a lightweight, very fast messaging system — a single Go binary that moves messages in memory with sub-millisecond latency, built for microservice eventing.1 In this repo it's one of two event buses (Kafka is the other), and it comes in two flavours that people constantly conflate:

⚡ Core NATS

Plain publish/subscribe, at-most-once. If no subscriber is listening the instant a message is published, it's gone — no storage, no replay. Fast, simple, fire-and-forget fan-out.

💾 JetStream

A persistence layer on top of core NATS. It captures messages into streams and replays them to consumers — adding at-least-once delivery, replay, and dedup. This course is JetStream.

The distinction that matters Core NATS is at-most-once: a message with no listener vanishes. JetStream is at-least-once: the server stores the message and keeps redelivering until a consumer acknowledges it.2 Every durable event in this repo — a user created, a notification requested — rides JetStream, precisely because it must not be lost if the subscriber is momentarily down.

NATS vs Kafka — this repo runs both

You've done the Kafka course and the CDC course. So why also NATS? They divide the work:

NATS JetStreamKafka
Feellightweight, ultra-low-latency, in-memory-fastheavier, disk-durable, very high throughput
Used here forinternal service events & fan-out (user/staff/usergroup upserts, push-notification requests, the activity-log firehose)durable pipelines: CDC, the outbox, email, bulk jobs, DWH sync
Subjects/topicsdotted PascalCase (Staff.Upserted){team}.{topic}
A live migration to know about Some flows are moving from NATS to Kafka (gated by an Unleash flag). You'll find NATS subscribers commented out with a note pointing to their Kafka replacement — and the same business handler deliberately reused by both transports. So "which bus?" sometimes means "which bus today." We'll cover the trade-off properly in Lesson 12.

The flow, and where the pieces live

publisher ──Subject.Name──▶ STREAM ──▶ CONSUMER ──▶ subscriber (usermgmt) e.g. User.Created (durable store) (cursor) (conversationmgmt / notification) the golib: internal/golibs/nats/jetstream.go ← the JetStreamManagement client (publish/subscribe) streams: cmd/server/fink/streams/*.go ← provisioned by the "fink" job names: internal/golibs/constants/common.go ← Subject*/Stream*/Queue*/Durable*/Deliver* YOUR subs: internal/{conversationmgmt,notification}/.../nats/* ← the anchor

Four stops: a service publishes on a subject; JetStream captures it in a stream; a consumer is your service's cursor into that stream; your subscriber handles the message. Parts 1–3 walk this left to right, ending in your own code.

Read this next

What JetStream is (and how it compares)

The clearest overview of core NATS vs JetStream, and where NATS fits against other systems.

docs.nats.io — JetStream
Compare NATS · Synadia — NATS & Kafka compared

Check yourself (from memory)

Q1. What's the core delivery difference between core NATS and JetStream?

Core NATS drops a message with no live listener (at-most-once); JetStream stores it and redelivers until acked (at-least-once).

Q2. In this repo, what does NATS carry that Kafka typically doesn't?

NATS = fast internal domain events (user/staff upserts, push-notif, activity log). Kafka = durable pipelines (CDC, outbox, DWH).

Q3. What are the four stops in the flow?

A service publishes on a subject → JetStream captures it in a stream → a consumer is the cursor → your subscriber handles it.
Recall: core NATS vs JetStream, NATS vs Kafka, the flow.
two flavours + the split + the flow, then reveal
NATS = lightweight, in-memory-fast messaging (a single Go binary). Core NATS = pub/sub, at-most-once (no listener → gone, no persistence). JetStream = a persistence layer — captures into streams, replays to consumers, at-least-once + replay + dedup (this course). NATS vs Kafka (both run here): NATS = low-latency internal service events / fan-out + the activity-log firehose (dotted PascalCase subjects); Kafka = durable high-throughput pipelines (CDC, outbox, DWH; {team}.{topic}). Active NATS→Kafka migration (Unleash-flagged; commented-out subs). Flow: publisher → Subject.Name → STREAM → CONSUMER → subscriber. Pieces: golib internal/golibs/nats; streams via fink; names in constants/common.go; your subs in conversationmgmt/notification.
🎯 Interview one-liner "You use NATS and Kafka — why both?" → "NATS JetStream for low-latency internal service events and fan-out — user/staff upserts, notification requests, the audit-log firehose — where we want simple, fast, at-least-once eventing. Kafka for durable, high-throughput pipelines like CDC and the outbox. Core NATS is at-most-once; JetStream adds the persistence that makes it safe for events that can't be lost."
Next lesson: the addressing scheme — subjects and pub/sub, wildcards, and the queue groups that load-balance your subscribers. Unsure whether something is NATS or Kafka? Ask me — that's what I'm here for.

1. Compare NATS, NATS & Kafka compared.

2. docs.nats.io — JetStream. In-repo map: docs/nats/reference/repo-nats-map.md.