Lesson 1 · Hexagonal — the shape of a v2 module
The v2 map
Before any pattern: where "v2" lives, what the layers are called, and the one table that tells you which of the three patterns you'll actually find where.
Your win: open any module in your services and name its layers, and say out loud which of hexagonal / CQRS / sqlc you should expect to find there — and which you won't.
Two generations of code live side by side
Every service here has old code and new code in the same tree. The team calls the new shape "v2". You add new features to v2 and leave v1 alone:
| Generation | Looks like | Rule |
|---|---|---|
| v1 (legacy) | entities/ repositories/ services/ subscriptions/ — a flat layer per kind | don't add new code here |
| v2 (active) | modules/{name}/ — a self-contained bounded context per domain | all new features go here |
In eureka the new tree is literally internal/eureka/v2/; in spike and notification
it's the modules/ folder. Same idea, two homes.1
The layers of a v2 module
A v2 module is layered so that the business logic sits in the middle and everything external — the database, gRPC, Kafka — plugs in at the edges. The names differ slightly by service, but the shape is identical:
The whole course is really about that middle arrow — the port (an interface the core owns) and the adapter (a concrete implementation at the edge). Lesson 2 is entirely about it. For now, just learn to see the four bands.
The map: which pattern lives where
Here's the fact that saves you an hour of confusion. The three patterns in the course title are not evenly spread across the repo:
| Pattern | Lives in | NOT in |
|---|---|---|
| Hexagonal (ports & adapters) | spike, notification, conversationmgmt, eureka v2 | — (but adoption is uneven) |
| CQRS (commands / queries) | spike, notification (system_notification) | eureka (uses usecase/ + Reader/Writer split) |
| sqlc (typed SQL) | eureka only | spike, notification, conversationmgmt — they hand-roll SQL |
assessment module) for
the one pattern your services don't use. If you go looking for a querier in spike,
you won't find one — spike builds its SQL strings by hand, and that's by design.
One more thing: the vocabulary is split
The same architecture is described with two different dialects, and knowing this stops a lot of "wait, are these the same thing?" moments:
| eureka | spike / notification | |
|---|---|---|
| calls it | "v2" / "clean architecture + DDD" | "the modules/ layer" / "hexagonal + CQRS" |
| layers | transport / usecase / domain / repository | controller / application / core (or domain) / infrastructure |
This course uses hexagonal + CQRS + sqlc as the umbrella terms. When eureka's docs say "clean architecture," read "hexagonal"; they're the same family (Lesson 2).
.claude/rules/*.md
(e.g. eureka-v2-conventions.md), which are auto-loaded and prescriptive: storage.go
purity, querier injection, error translation. When a readme and a rule disagree, the rule wins.
Combining DDD, CQRS & Clean Architecture in Go
Three Dots Labs — the single best article on exactly this stack in idiomatic Go. It's the public analogue of what your v2 modules are reaching for.
→ threedots.tech — DDD, CQRS & Clean Architecture combined
→ in-repo internal/spike/CLAUDE.md, internal/eureka/CLAUDE.md
Check yourself (from memory)
Q1. Which service uses sqlc?
assessment module for sqlc.
Q2. In a v2 module, where does the business logic sit?
application/ (spike) or usecase/ (eureka), between transport and ports.
Q3. eureka says "clean architecture"; spike says "hexagonal." What gives?
entities/repositories/services/,
don't touch) vs v2 active (modules/{name}/, all new code). eureka's v2 = literal
internal/eureka/v2/. Four layers: controller/transport (thin) →
application/usecase (logic) → port (interface) ← infrastructure/repository
adapter; domain (pure) at the centre. The matrix: hexagonal = everywhere;
CQRS = spike + notification; sqlc = eureka ONLY (your services hand-roll SQL).
Naming split: eureka "v2 / clean arch / DDD"; spike+notification "modules /
hexagonal / CQRS" — same shape. Real spec = .claude/rules/*.md.1. In-repo: internal/eureka/CLAUDE.md ("v2 Architecture"),
internal/spike/CLAUDE.md ("Module Architecture"),
internal/notification/CLAUDE.md. Full map with file:line in
repo-v2-map.