Clean, Performant, and Testable: Mastering Data Access in Go with Repositories & sqlc (2)
Part 2: Implementing Effective Repositories in Go & Confronting the Critiques (Recap: In Part 1, we explored the challenges of data access in Go, defined the Repository Pattern, and compared it to raw SQL and ORMs, setting the stage for a more structured approach. If you missed it, you can catch up here.) Table of Contents (Part 2) The 3 AM Database Panic Code Deep Dive: Building a Manual Go Repository Defining the Domain Model Crafting the Repository Interface The pgx Implementation: Step-by-Step Observations on the Manual Approach Why This Changes Everything: The Concrete Advantages Advantage 1: Enhanced Separation of Concerns (SoC) Advantage 2: Dramatically Improved Testability Advantage 3: Increased Flexibility & Maintainability Advantage 4: Centralized Data Access Logic The Honest Truth: Acknowledging the Downsides Disadvantage 1: Boilerplate Code Disadvantage 2: Potential for Over-Abstraction Confronting the Critics: A Go Developer's Response Critique 1: Hiding Native ORM Features Critique 2: "Deep Abstractions Kill Flexibility" Critique 3: SOLID Violations Critique 4: Unit Testing Isn't That Much Easier Critique 5: Leaky Abstraction Critique 6: CQRS Doesn't Fit Well Critique 7: The "Swappability" Stories Critique 8: Performance Anti-Patterns The Verdict: Context Matters The Path Forward: Setting Up for Success What's Next: The sqlc Revolution The 3 AM Database Panic It's 3 AM. Your phone buzzes with that dreaded sound. Production is down. "The payment service is throwing database errors," your teammate types frantically in Slack. "I can't figure out where the bug is, there are SQL queries scattered across twelve different files." You've been here before. What started as a simple microservice has become a web of embedded SQL, making debugging feel like archaeological excavation. Each fix breaks something else. Each test requires spinning up a full database. Each new feature means copying and pasting similar queries across multiple handlers. This is the moment when clean architecture stops being academic and starts being survival. In Part 1, we explored the theoretical foundations of the Repository Pattern through our story of three teams tackling the same problem with different approaches. Now it's time to get our hands dirty. We're going to build a repository from scratch, understand exactly why it solves the 3 AM panic scenario, and address the skeptics who argue it's unnecessary complexity. By the end of this article, you'll never again wonder if repositories are worth the effort, you'll wonder how you survived without them. Code Deep Dive: Building a Manual Go Repository To truly appreciate what tools like sqlc (which we'll cover in Part 3) bring to the table, it's essential to first understand how to build a repository manually. This exercise highlights both the structural benefits of the pattern and the boilerplate that sqlc aims to eliminate. We'll use pgx/v5 with a PostgreSQL database. Let's build this step by step, just like you would in a real project. Defining the Domain Model First, let's define our application's representation of a user. This struct lives in your domain layer and represents your business entity, not your database table. // models/user.go package models import "time" // User represents a user in our application domain. type User struct { ID int64 `json:"id"` Name string `json:"name"` Email string `json:"email" binding:"required,email"` // Example with validation tags CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` }

Part 2: Implementing Effective Repositories in Go & Confronting the Critiques
(Recap: In Part 1, we explored the challenges of data access in Go, defined the Repository Pattern, and compared it to raw SQL and ORMs, setting the stage for a more structured approach. If you missed it, you can catch up here.)
Table of Contents (Part 2)
- The 3 AM Database Panic
-
Code Deep Dive: Building a Manual Go Repository
- Defining the Domain Model
- Crafting the Repository Interface
- The
pgx
Implementation: Step-by-Step - Observations on the Manual Approach
-
Why This Changes Everything: The Concrete Advantages
- Advantage 1: Enhanced Separation of Concerns (SoC)
- Advantage 2: Dramatically Improved Testability
- Advantage 3: Increased Flexibility & Maintainability
- Advantage 4: Centralized Data Access Logic
-
The Honest Truth: Acknowledging the Downsides
- Disadvantage 1: Boilerplate Code
- Disadvantage 2: Potential for Over-Abstraction
-
Confronting the Critics: A Go Developer's Response
- Critique 1: Hiding Native ORM Features
- Critique 2: "Deep Abstractions Kill Flexibility"
- Critique 3: SOLID Violations
- Critique 4: Unit Testing Isn't That Much Easier
- Critique 5: Leaky Abstraction
- Critique 6: CQRS Doesn't Fit Well
- Critique 7: The "Swappability" Stories
- Critique 8: Performance Anti-Patterns
- The Verdict: Context Matters
- The Path Forward: Setting Up for Success
- What's Next: The
sqlc
Revolution
The 3 AM Database Panic
It's 3 AM. Your phone buzzes with that dreaded sound. Production is down.
"The payment service is throwing database errors," your teammate types frantically in Slack. "I can't figure out where the bug is, there are SQL queries scattered across twelve different files."
You've been here before. What started as a simple microservice has become a web of embedded SQL, making debugging feel like archaeological excavation. Each fix breaks something else. Each test requires spinning up a full database. Each new feature means copying and pasting similar queries across multiple handlers.
This is the moment when clean architecture stops being academic and starts being survival.
In Part 1, we explored the theoretical foundations of the Repository Pattern through our story of three teams tackling the same problem with different approaches. Now it's time to get our hands dirty. We're going to build a repository from scratch, understand exactly why it solves the 3 AM panic scenario, and address the skeptics who argue it's unnecessary complexity.
By the end of this article, you'll never again wonder if repositories are worth the effort, you'll wonder how you survived without them.
Code Deep Dive: Building a Manual Go Repository
To truly appreciate what tools like sqlc
(which we'll cover in Part 3) bring to the table, it's essential to first understand how to build a repository manually. This exercise highlights both the structural benefits of the pattern and the boilerplate that sqlc
aims to eliminate. We'll use pgx/v5
with a PostgreSQL database.
Let's build this step by step, just like you would in a real project.
Defining the Domain Model
First, let's define our application's representation of a user. This struct lives in your domain layer and represents your business entity, not your database table.
// models/user.go
package models
import "time"
// User represents a user in our application domain.
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
Email string `json:"email" binding:"required,email"` // Example with validation tags
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}