Какие знаешь слои многослойной архитектуры

Ответ

В Go типичная многослойная архитектура включает:

  1. Transport (HTTP/gRPC) - обработка запросов/ответов
// HTTP handler
func (h *Handler) GetUser(c *gin.Context) {
    id := c.Param("id")
    user, err := h.service.GetUser(id)
    // ... обработка ошибок и отправка ответа
}
  1. Service/Business logic - ядро приложения, содержит бизнес-правила и координирует работу с репозиториями.
type UserService struct {
    repo Repository
}

func (s *UserService) GetUser(id string) (*User, error) {
    // Дополнительная бизнес-логика, валидация и т.д.
    return s.repo.GetByID(id)
}
  1. Repository - абстракция для работы с данными (базы данных, внешние API, файловая система).
type UserRepository struct {
    db *sql.DB
}

func (r *UserRepository) GetByID(id string) (*User, error) {
    // Выполнение запроса к БД и маппинг в модель
    row := r.db.QueryRow("SELECT id, name, email FROM users WHERE id = ?", id)
    user := &User{}
    err := row.Scan(&user.ID, &user.Name, &user.Email)
    if err != nil {
        return nil, err
    }
    return user, nil
}
  1. Models/Entities - структуры данных, представляющие сущности предметной области.
type User struct {
    ID    string
    Name  string
    Email string
}

Дополнительно могут быть:

  • Middleware: для обработки запросов до или после основных обработчиков (аутентификация, логирование).
  • DTO (Data Transfer Objects): структуры для передачи данных между слоями, часто отличаются от моделей.
  • Utils/Helpers: общие вспомогательные функции.

Ключевые принципы: слои изолированы, зависимости идут сверху вниз (Transport -> Service -> Repository -> Models). Каждый слой имеет свою ответственность, что упрощает тестирование и поддержку.

Ответ 18+ 🔞

А, ну это же классика, блядь! Сейчас объясню, как устроена эта ваша многослойная архитектура в Go, чтобы даже мартышлюшка поняла. Это не просто так, это чтобы мозги не ебать, когда проект разрастётся до овердохуища строк кода.

Представь, что ты строишь дом, но не сраный сарай, а нормальный такой особняк. И чтобы не получилась одна большая куча говна, ты делишь работу.

Первый этаж — Transport (HTTP/gRPC). Это типа приёмная, парадный вход. Тут всякие протоколы обитают. Сидит такой швейцар-обработчик, получает запросы из внешнего мира.

// HTTP handler
func (h *Handler) GetUser(c *gin.Context) {
    id := c.Param("id")
    user, err := h.service.GetUser(id)
    // ... обработка ошибок и отправка ответа
}

Видишь? Его работа — взять ID из запроса, крикнуть этажом выше: «Эй, сервис, дай-ка пользователя!», а потом красиво упаковать ответ или отправить ошибку обратно клиенту. Сам он нихуя не решает, он только передаёт. Пизда с ушами, короче.

Второй этаж — Service (Бизнес-логика). Вот это уже мозги центра, ёпта! Тут живут все эти умные правила, валидации, расчёты. Сервис — он как управляющий, который знает, что и как делать.

type UserService struct {
    repo Repository
}

func (s *UserService) GetUser(id string) (*User, error) {
    // Дополнительная бизнес-логика, валидация и т.д.
    return s.repo.GetByID(id)
}

Смотри: он получает задачу от транспорта. Может там проверить, не пришёл ли хуй с горы вместо ID. А потом говорит: «Ладно, я сам не пойду, я позову того, кто умеет с данными работать». И зовёт репозиторий.

Третий этаж — Repository (Хранилище). Это уже подвал, где живут данные. Тут обитают эти чудаки, которые умеют шептаться с базой данных, лазать во внешние API или ковыряться в файлах. Они знают все SQL-заклинания.

type UserRepository struct {
    db *sql.DB
}

func (r *UserRepository) GetByID(id string) (*User, error) {
    // Выполнение запроса к БД и маппинг в модель
    row := r.db.QueryRow("SELECT id, name, email FROM users WHERE id = ?", id)
    user := &User{}
    err := row.Scan(&user.ID, &user.Name, &user.Email)
    if err != nil {
        return nil, err
    }
    return user, nil
}

Его дело — выполнить запрос, вытащить сырые данные из БД и слепить из них нормальную структуру — модель. И отдать её наверх, сервису. Сам он в бизнес-правилах не шарит, он просто грузчик данных, блядь.

И наконец, Models/Entities. Это просто коробки, контейнеры для данных. Голые структуры, без мозгов.

type User struct {
    ID    string
    Name  string
    Email string
}

Они туда-сюда бегают между этажами. Ничего не делают, просто хранят инфу. Как мешки с картошкой, в рот меня чих-пых!

А ещё вокруг этого цирка могут плясать Middleware (типа охранники на входе, которые проверяют пропуска-аутентификацию), DTO (это когда тебе нужно отправить не весь мешок с картошкой, а только три отборные картофелины в красивой упаковке) и куча Helpers (всякие утилиты, как универсальный швейцарский нож).

Главный принцип, чтобы не было пиздеца — зависимости идут сверху вниз! Транспорт зависит от Сервиса, Сервис зависит от Репозитория. Никогда наоборот! Репозиторий не должен знать про HTTP, а Сервис — про то, как устроена база данных. Каждый сидит в своей комнате и делает своё дело. И тогда тестировать это всё — одно удовольствие, а не ебаться с монолитом, где всё перемешано в одну кучу. Вот и вся магия, хули.