Generics allow creating a universal repository pattern for different entities.
1// Base interface2type Repository[T any, ID comparable] interface {3 FindByID(ctx context.Context, id ID) (*T, error)4 FindAll(ctx context.Context) ([]*T, error)5 Create(ctx context.Context, entity *T) error6 Update(ctx context.Context, entity *T) error7 Delete(ctx context.Context, id ID) error8}910// PostgreSQL implementation11type PostgresRepository[T any, ID comparable] struct {12 db *sql.DB13 table string14}1516func (r *PostgresRepository[T, ID]) FindByID(ctx context.Context, id ID) (*T, error) {17 query := fmt.Sprintf("SELECT * FROM %s WHERE id = $1", r.table)18 var entity T19 err := r.db.QueryRowContext(ctx, query, id).Scan(&entity)20 if err != nil {21 return nil, err22 }23 return &entity, nil24}2526func (r *PostgresRepository[T, ID]) Create(ctx context.Context, entity *T) error {27 query := fmt.Sprintf("INSERT INTO %s ...", r.table)28 _, err := r.db.ExecContext(ctx, query, ...)29 return err30}3132// Usage33type User struct {34 ID int35 Name string36 Email string37}3839func main() {40 userRepo := &PostgresRepository[User, int]{41 db: db,42 table: "users",43 }4445 user, err := userRepo.FindByID(ctx, 1)46 if err != nil {47 log.Fatal(err)48 }49 fmt.Println(user.Name)5051 // Same code for Product52 productRepo := &PostgresRepository[Product, int]{53 db: db,54 table: "products",55 }56}
Advantages: Less code, type safety, consistency. Limitations: Complex serialization, generics constraints.