Ответ
Транспортный слой отвечает за взаимодействие приложения с внешним миром (например, прием HTTP-запросов или gRPC-вызовов). Его принято изолировать от бизнес-логики (сервисного слоя).
Расположение в проекте
Чаще всего его размещают в директории internal
, чтобы избежать импорта из других проектов. Популярные названия:
internal/transport
(общее название)internal/handler
илиinternal/handlers
internal/api
Внутри этих папок создают поддиректории для каждого типа транспорта:
/my-project
└── internal/
├── service/ # Бизнес-логика
└── transport/ # Транспортный слой
├── http/
│ ├── handler.go # Обработчики
│ ├── middleware.go # Middleware
│ └── router.go # Регистрация роутов
└── grpc/
└── server.go
Роль и обязанности транспортного слоя:
- Прием и разбор (parsing) входящих запросов (HTTP, gRPC и т.д.).
- Валидация данных запроса (заголовки, тело, параметры).
- Преобразование (mapping) данных из формата запроса (например, JSON) во внутренние структуры (DTO или модели для сервисного слоя).
- Вызов соответствующего метода сервисного слоя с передачей ему валидированных данных.
- Обработка ошибок, возвращаемых сервисным слоем, и преобразование их в корректные ответы (например, HTTP-статусы 400, 404, 500).
- Сериализация данных ответа (например, в JSON) и отправка клиенту.
Пример HTTP-обработчика:
package http
import "net/http"
// service - интерфейс бизнес-логики, который мы будем вызывать.
type service interface {
GetUser(id int) (*User, error)
}
type Handler struct {
service service
}
func NewHandler(s service) *Handler {
return &Handler{service: s}
}
// GetUser обрабатывает запрос на получение пользователя.
func (h *Handler) GetUser(w http.ResponseWriter, r *http.Request) {
// 1. Парсинг и валидация ID из URL
id, err := parseID(r)
if err != nil {
http.Error(w, "Invalid ID", http.StatusBadRequest)
return
}
// 2. Вызов бизнес-логики
user, err := h.service.GetUser(id)
if err != nil {
// 3. Обработка ошибок от сервиса
http.Error(w, "User not found", http.StatusNotFound)
return
}
// 4. Сериализация и отправка ответа
respondJSON(w, http.StatusOK, user)
}