Skip to content

Repositories

A Repository owns persistence behavior for a feature or aggregate.

Services call repositories. Controllers, commands, jobs, events, and schedules should call services instead of reaching into persistence directly.

Repository Shape

go
package users

type Repository struct {
	db *gorm.DB
}

func NewRepository(conns *database.Connections) (*Repository, error) {
	db, err := conns.Default()
	if err != nil {
		return nil, err
	}
	return &Repository{db: db}, nil
}

Use the database accessors generated for your App. Keep backend connection selection outside business logic.

Register repository constructors through app/wire/inject_repositories_app.go. Additional apps use the owning app's app/<name>/wire/inject_repositories_app.go.

Responsibilities

Repositories should own:

  • query construction
  • persistence-specific models
  • transaction participation
  • mapping between database rows and application types
  • database error interpretation

Repositories should not own HTTP behavior, CLI output, queue dispatch policy, event fan-out policy, or scheduler behavior.

Service Boundary

Services should call repositories through clear methods:

go
func (s *Service) Find(ctx context.Context, id string) (User, error) {
	return s.repo.Find(ctx, id)
}

Keep service inputs independent from database model structs unless that type is intentionally the application model.

Named Connections

Use named connections when a feature has a real persistence boundary:

go
analytics, err := conns.Analytics()

Do not create named connections just to organize code. Names should reflect operationally meaningful resources.

Testing

Repository tests can use local database drivers such as SQLite when behavior is driver-independent.

Use driver-specific integration tests when SQL behavior depends on MySQL or Postgres.

Run the focused package test before the full suite:

bash
go test ./internal/users
go test ./...

Success means the focused repository behavior and every package using its service contract pass. When SQL semantics depend on the production Driver, add the corresponding container-backed case described in Integration Tests rather than treating SQLite as equivalent.

Next Steps