Ответ
В 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
}
Ключевая цель такого разделения — изоляция бизнес-логики от деталей реализации хранилища. Это упрощает тестирование (можно подменить репозиторий моком), сопровождение и возможную замену СУБД в будущем.
Ответ 18+ 🔞
А, ну вот, опять про эту вашу модульную архитектуру в Go! Слушай, а ты знаешь, что в Go нет единого, блядь, строгого фреймворка, который бы тебе всё на блюдечке принёс? Нет, сука! Поэтому структуру проекта ты определяешь сам, по соглашениям, как умный мальчик.
Вот этот слой доступа к данным, который часто делают через паттерн «Репозиторий» (Repository), его, блядь, принято пихать в определённые директории. Смотри сюда, я тебе сейчас всё разложу, как на духу.
-
internal/repository(илиinternal/storage) — это, блядь, самый ходовой вариант! Весь код вinternal— это святая святых, его другие проекты импортировать не могут. Полная инкапсуляция, ебать! Вся твоя логика доступа к данным сидит внутри сервиса, как в бункере. -
pkg/repository— это уже для распиздяев, которые библиотеки пишут. Если твой код должен переиспользоваться в других проектах — вот тебеpkg. Но для обычного бизнес-приложения это, блядь, редкость, честно говоря. -
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
}
И главная цель всей этой хуйни — изоляция бизнес-логики от деталей реализации хранилища! Это же, блядь, гениально просто: упрощает тестирование (можно репозиторий моком подменить), сопровождение и, если вдруг надумаешь СУБД менять, то не придётся весь проект переписывать. В рот меня чих-пых, какая красота!