diff --git a/db.go b/db.go
new file mode 100644
index 0000000000000000000000000000000000000000..338979e53897791004aacb52301ac2480190e9df
--- /dev/null
+++ b/db.go
@@ -0,0 +1,93 @@
+package main
+
+import (
+	"embed"
+	"errors"
+	"fmt"
+	"io/fs"
+	"log"
+	"sort"
+
+	"github.com/jmoiron/sqlx"
+)
+
+//go:embed migrations/*.sql
+var migrationFS embed.FS
+
+func initDB(db *sqlx.DB) error {
+	// Create table tracking migration state
+	const query = `CREATE TABLE IF NOT EXISTS migrations (name TEXT UNIQUE);`
+
+	_, err := db.Exec(query)
+	if err != nil {
+		return err
+	}
+
+	entries, err := migrationFS.ReadDir("migrations")
+	if err != nil {
+		return err
+	}
+
+	sort.Slice(entries, func(i, j int) bool {
+		return entries[i].Name() < entries[j].Name()
+	})
+
+	for _, e := range entries {
+		data, err := fs.ReadFile(migrationFS, "migrations/"+e.Name())
+		if err != nil {
+			return err
+		}
+
+		tx, err := db.Begin()
+		if err != nil {
+			return err
+		}
+
+		row := tx.QueryRow("SELECT count(*) FROM migrations WHERE name = ?", e.Name())
+
+		var howMany int
+		err = row.Scan(&howMany)
+		if err != nil {
+			tx.Rollback()
+			return fmt.Errorf("checking status for migration %s: %w", e.Name(), err)
+		}
+
+		switch howMany {
+		case 0:
+			// not yet applied
+		case 1:
+			// applied, no need to do anything
+			log.Printf("skipping migration %s: already applied", e.Name())
+			tx.Rollback()
+			continue
+		default:
+			// very weird
+			tx.Rollback()
+			return fmt.Errorf("unexpected migration count for %s: %d", e.Name(), howMany)
+		}
+
+		log.Println("applying migration", e.Name())
+
+		_, err = tx.Exec(string(data))
+		if err != nil {
+			tx.Rollback()
+			return fmt.Errorf("applying migration %s: %w", e.Name(), err)
+		}
+
+		// Record migration as applied
+		_, err = tx.Exec("INSERT INTO migrations (name) VALUES (?)", e.Name())
+		if err != nil {
+			tx.Rollback()
+			return fmt.Errorf("recording migration %s: %w", e.Name(), err)
+		}
+
+		err = tx.Commit()
+		if err != nil {
+			return err
+		}
+
+		log.Println("it has", len(data), "bytes")
+	}
+
+	return errors.New("here be dragons")
+}
diff --git a/main.go b/main.go
index e1601d591938948761e4a0ce1409b83d654dd8e9..293ab0892e0df087ef53ce7bd374df87d73ed24c 100644
--- a/main.go
+++ b/main.go
@@ -2,13 +2,9 @@ package main
 
 import (
 	"embed"
-	"errors"
-	"fmt"
 	"html/template"
-	"io/fs"
 	"log"
 	"net/http"
-	"sort"
 
 	"github.com/jmoiron/sqlx"
 	_ "modernc.org/sqlite" // Imported for side effects: registers DB driver
@@ -21,87 +17,6 @@ var templates = template.Must(template.ParseFS(templateFS, "templates/*.tpl"))
 //go:embed static/*
 var staticFS embed.FS
 
-//go:embed migrations/*.sql
-var migrationFS embed.FS
-
-func initDB(db *sqlx.DB) error {
-	// Create table tracking migration state
-	const query = `CREATE TABLE IF NOT EXISTS migrations (name TEXT UNIQUE);`
-
-	_, err := db.Exec(query)
-	if err != nil {
-		return err
-	}
-
-	entries, err := migrationFS.ReadDir("migrations")
-	if err != nil {
-		return err
-	}
-
-	sort.Slice(entries, func(i, j int) bool {
-		return entries[i].Name() < entries[j].Name()
-	})
-
-	for _, e := range entries {
-		data, err := fs.ReadFile(migrationFS, "migrations/"+e.Name())
-		if err != nil {
-			return err
-		}
-
-		tx, err := db.Begin()
-		if err != nil {
-			return err
-		}
-
-		row := tx.QueryRow("SELECT count(*) FROM migrations WHERE name = ?", e.Name())
-
-		var howMany int
-		err = row.Scan(&howMany)
-		if err != nil {
-			tx.Rollback()
-			return fmt.Errorf("checking status for migration %s: %w", e.Name(), err)
-		}
-
-		switch howMany {
-		case 0:
-			// not yet applied
-		case 1:
-			// applied, no need to do anything
-			log.Printf("skipping migration %s: already applied", e.Name())
-			tx.Rollback()
-			continue
-		default:
-			// very weird
-			tx.Rollback()
-			return fmt.Errorf("unexpected migration count for %s: %d", e.Name(), howMany)
-		}
-
-		log.Println("applying migration", e.Name())
-
-		_, err = tx.Exec(string(data))
-		if err != nil {
-			tx.Rollback()
-			return fmt.Errorf("applying migration %s: %w", e.Name(), err)
-		}
-
-		// Record migration as applied
-		_, err = tx.Exec("INSERT INTO migrations (name) VALUES (?)", e.Name())
-		if err != nil {
-			tx.Rollback()
-			return fmt.Errorf("recording migration %s: %w", e.Name(), err)
-		}
-
-		err = tx.Commit()
-		if err != nil {
-			return err
-		}
-
-		log.Println("it has", len(data), "bytes")
-	}
-
-	return errors.New("here be dragons")
-}
-
 func main() {
 	db, err := sqlx.Open("sqlite", "vino.sqlite")
 	if err != nil {
diff --git a/migrations/0001-initial.sql b/migrations/0001-initial.sql
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9e4127566ce56efc754f411d1ca0ed00b32f9773 100644
--- a/migrations/0001-initial.sql
+++ b/migrations/0001-initial.sql
@@ -0,0 +1,6 @@
+CREATE TABLE wines (
+	name TEXT,
+	rating INT, -- number of stars
+	picture BINARY, -- jpeg/png image of the label on the bottle
+	UNIQUE(name)
+);
\ No newline at end of file