Eleven technical workouts that take one sentence of requirements and end with a running endpoint you would not be embarrassed to review. One clinic schema throughout — designed, mapped to Go, then organised into layers that survive a second developer.
01 · The Domain
Every page works on the same appointment-booking system. The tables you design in Session 01 are the structs you map in Session 05 and the repository you inject in Session 11.
"Patients book appointments with doctors. A doctor has specialities, a patient has one medical record, and the same slot must never be sold twice."
Nothing is thrown away between sessions. The double-booking problem is introduced as a constraint in Session 03, enforced in a transaction in Session 09, and returned as a clean 409 Conflict in Session 11.
02 · The Arc
PostgreSQL throughout. Where MySQL differs in a way that matters — identity columns, RETURNING, the ON CONFLICT spelling — it is called out on the page.
03 · The 11 Sessions
Turning requirements into entities · tables, fields, primary and foreign keys · choosing the right type for each column instead of defaulting to text.
One-to-one, one-to-many and many-to-many · which side holds the foreign key · why a join table is the only honest way to model many-to-many.
NOT NULL, UNIQUE, CHECK and foreign-key actions · normalizing a flat spreadsheet to 3NF · the constraint that makes double-booking impossible.
Reading and drawing an ER diagram · crow's-foot cardinality · and the cases where deliberately denormalizing is the right call, not a mistake.
Connecting to PostgreSQL · turning each table into a Go struct · the struct tags that actually matter, and the ones that quietly do nothing.
Create, read, update and delete with GORM · why Save is not Update · AutoMigrate for local work and versioned migrations for anything real.
Where, Joins, Group and Having · packaging repeated filters as reusable Scopes · and the moment to stop fighting the ORM and write Raw SQL.
has one, has many and many2many in GORM · loading related records with Preload · and watching the N+1 query problem appear in the SQL log.
Booking a slot atomically with db.Transaction · what rolls back and what does not · sentinel errors and errors.Is instead of string matching.
Handler, Service, Repository and Model · a folder tree you can defend · interfaces that keep the service from importing GORM at all.
Constructor injection and the composition root · one request traced through every layer and back · centralized error mapping · configuration from the environment.
Eleven sessions from now the booking rule lives in one function, the handler is nine lines, and swapping the database touches a single package.