Ответ
В Go нет единого строгого фреймворка, поэтому структура проекта определяется соглашениями. Слой доступа к данным, часто реализуемый через паттерн "Репозиторий" (Repository), принято размещать в следующих директориях:
internal/repository
(илиinternal/storage
): Самый распространенный вариант. Код вinternal
не может быть импортирован другими проектами, что обеспечивает инкапсуляцию логики доступа к данным внутри вашего сервиса.pkg/repository
: Используется, если вы пишете библиотеку, и этот код предназначен для переиспользования в нескольких проектах. Для бизнес-приложений это менее частый случай.db/
илиstorage/
(в корне проекта): Иногда используется в небольших проектах для простоты.
Пример структуры:
/my-project
├── cmd/app/main.go
├── internal/
│ ├── service/ # Бизнес-логика
│ │ └── user_service.go
│ ├── repository/ # Слой доступа к данным
│ │ └── user_repository_postgres.go
│ └── models/ # Модели данных
│ └── user.go
└── pkg/
└── db/ # Логика подключения к БД
└── postgres.go
Пример кода репозитория:
package repository
import (
"database/sql"
"my-project/internal/models"
)
// UserRepository определяет интерфейс для работы с пользователями
// Это позволяет легко подменять реализацию (например, для тестов).
type UserRepository interface {
GetByID(id int) (*models.User, error)
}
// UserPostgresRepo - конкретная реализация для PostgreSQL.
type UserPostgresRepo struct {
db *sql.DB // Зависимость от подключения к БД
}
func NewUserPostgresRepo(db *sql.DB) *UserPostgresRepo {
return &UserPostgresRepo{db: db}
}
func (r *UserPostgresRepo) GetByID(id int) (*models.User, error) {
// ... логика запроса к БД ...
return nil, nil
}
Ключевая цель такого разделения — изоляция бизнес-логики от деталей реализации хранилища. Это упрощает тестирование (можно подменить репозиторий моком), сопровождение и возможную замену СУБД в будущем.