Good Code
The good version gives the package a domain name, keeps dependencies explicit, and exports a small service boundary.
Lesson 01
Keep packages small, cohesive, and named by what callers need instead of by internal technical layers.
package reviews
type Service struct {
repo Repository
}
func NewService(repo Repository) *Service {
return &Service{repo: repo}
}
func (s *Service) Publish(ctx context.Context, id string) error {
review, err := s.repo.Find(ctx, id)
if err != nil {
return err
}
review.Publish()
return s.repo.Save(ctx, review)
}package utils
var ReviewDB *sql.DB
func PublishReview(id string) error {
row := ReviewDB.QueryRow("select title from reviews where id = ?", id)
var title string
if err := row.Scan(&title); err != nil {
return err
}
_, err := ReviewDB.Exec("update reviews set status = 'published' where id = ?", id)
return err
}The good version gives the package a domain name, keeps dependencies explicit, and exports a small service boundary.
The bad version hides review behavior in a generic utils package with global database state. Callers cannot tell what the package owns.