Documentation
¶
Overview ¶
Package example демонстрирует полную структуру пакета в Clean Architecture.
Этот пакет — эталонный пример для команд, использующих portsmith. Каждый файл отвечает за строго один слой. Зависимости направлены только внутрь:
Handler → ServicePort ← Service → RepositoryPort ← Repository
Файлы в порядке от ядра наружу:
domain.go — доменные типы (ядро, ноль зависимостей) errors.go — доменные ошибки ports.go — интерфейсы-порты (обычно генерируется portsmith gen) service.go — бизнес-логика repository.go — реализация хранилища handler.go — HTTP-адаптер dto.go — структуры запросов и ответов mappers.go — преобразование domain ↔ DTO
Index ¶
- Variables
- type CreateParams
- type CreateUserRequest
- type Handler
- type ListFilter
- type ListUsersResponse
- type Repository
- func (r *Repository) Create(ctx context.Context, user *User) error
- func (r *Repository) Delete(ctx context.Context, id uint) error
- func (r *Repository) FindByEmail(ctx context.Context, email string) (*User, error)
- func (r *Repository) FindByID(ctx context.Context, id uint) (*User, error)
- func (r *Repository) List(ctx context.Context, filter ListFilter, page pagination.OffsetPage) ([]*User, int64, error)
- func (r *Repository) Update(ctx context.Context, user *User) error
- type Service
- func (s *Service) Create(ctx context.Context, params CreateParams) (*User, error)
- func (s *Service) Delete(ctx context.Context, id uint) error
- func (s *Service) GetByID(ctx context.Context, id uint) (*User, error)
- func (s *Service) List(ctx context.Context, filter ListFilter, page pagination.OffsetPage) ([]*User, int64, error)
- func (s *Service) Update(ctx context.Context, id uint, params UpdateParams, callerID uint) (*User, error)
- type UpdateParams
- type UpdateUserRequest
- type User
- type UserRepository
- type UserResponse
- type UserRole
- type UserService
Constants ¶
This section is empty.
Variables ¶
var ( // ErrUserNotFound возвращается когда пользователь не найден по ID или email. ErrUserNotFound = apperrors.NotFound("user not found") // ErrEmailTaken возвращается при попытке создать пользователя // с email, который уже занят. ErrEmailTaken = apperrors.Conflict("email already taken") // ErrCannotDeactivateSelf возвращается когда администратор // пытается деактивировать собственный аккаунт. ErrCannotDeactivateSelf = apperrors.BadRequest("cannot deactivate your own account") // ErrInsufficientPermissions возвращается при попытке выполнить // операцию, требующую прав администратора. ErrInsufficientPermissions = apperrors.Forbidden("insufficient permissions") )
Functions ¶
This section is empty.
Types ¶
type CreateParams ¶
CreateParams — входные данные для создания пользователя. Используется сервисом: handler преобразует DTO → CreateParams, сервис принимает CreateParams → вызывает репозиторий.
Почему не передавать DTO прямо в сервис? Сервис не должен знать про HTTP-специфику (json-теги, validator-теги). CreateParams — это чистый "язык" бизнес-операции.
type CreateUserRequest ¶
type CreateUserRequest struct {
Email string `json:"email" binding:"required,email"`
Name string `json:"name" binding:"required,min=2,max=100"`
Role UserRole `json:"role" binding:"omitempty,oneof=user admin"`
}
CreateUserRequest — тело POST /users.
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler реализует HTTP-обработчики для пользователей.
func (*Handler) Routes ¶
func (h *Handler) Routes(rg *gin.RouterGroup)
Routes регистрирует маршруты в переданной группе Gin. Вызывается из main.go или при настройке сервера.
Пример:
v1 := srv.Router().Group("/api/v1")
userHandler.Routes(v1)
type ListFilter ¶
ListFilter — параметры фильтрации для запросов списка.
type ListUsersResponse ¶
type ListUsersResponse struct {
Items []*UserResponse `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
Limit int `json:"limit"`
}
ListUsersResponse — ответ на GET /users со списком и метаданными пагинации.
type Repository ¶
type Repository struct {
// contains filtered or unexported fields
}
Repository реализует UserRepository поверх GORM.
func NewRepository ¶
func NewRepository(db *gorm.DB) *Repository
NewRepository создаёт новый Repository.
func (*Repository) Create ¶
func (r *Repository) Create(ctx context.Context, user *User) error
Create сохраняет нового пользователя в базе.
func (*Repository) Delete ¶
func (r *Repository) Delete(ctx context.Context, id uint) error
Delete удаляет пользователя по ID (soft delete если у модели есть DeletedAt).
func (*Repository) FindByEmail ¶
FindByEmail ищет пользователя по email.
func (*Repository) FindByID ¶
FindByID возвращает пользователя по первичному ключу. Возвращает ErrUserNotFound если запись не найдена — не gorm.ErrRecordNotFound.
func (*Repository) List ¶
func (r *Repository) List(ctx context.Context, filter ListFilter, page pagination.OffsetPage) ([]*User, int64, error)
List возвращает страницу пользователей с опциональной фильтрацией.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service реализует бизнес-логику управления пользователями.
func NewService ¶
func NewService(repo UserRepository) *Service
NewService создаёт новый Service. Принимает интерфейс — легко тестируется.
func (*Service) Create ¶
Create создаёт нового пользователя.
Бизнес-правила:
- Email должен быть уникальным (проверяем через репозиторий).
- Если роль не указана — назначаем RoleUser по умолчанию.
func (*Service) List ¶
func (s *Service) List(ctx context.Context, filter ListFilter, page pagination.OffsetPage) ([]*User, int64, error)
List возвращает страницу пользователей с фильтрацией и пагинацией.
func (*Service) Update ¶
func (s *Service) Update(ctx context.Context, id uint, params UpdateParams, callerID uint) (*User, error)
Update обновляет поля пользователя.
Бизнес-правила:
- Обычный пользователь не может менять свою роль.
- Нельзя деактивировать самого себя (callerID == id && Active = false).
type UpdateParams ¶
UpdateParams — входные данные для частичного обновления. Указатели означают "поле передано" / "поле не передано" (partial update).
type UpdateUserRequest ¶
type UpdateUserRequest struct {
Name *string `json:"name" binding:"omitempty,min=2,max=100"`
Role *UserRole `json:"role" binding:"omitempty,oneof=user admin"`
Active *bool `json:"active"`
}
UpdateUserRequest — тело PATCH /users/:id. Указатели позволяют отличить "поле не передано" от "передано пустое значение".
type User ¶
type User struct {
ID uint `gorm:"primaryKey"`
Email string `gorm:"uniqueIndex;not null"`
Name string `gorm:"not null"`
Role UserRole `gorm:"default:'user'"`
Active bool `gorm:"default:true"`
CreatedAt time.Time
UpdatedAt time.Time
}
User — центральная доменная сущность пакета.
Правило: domain.go не импортирует ни database/sql, ни net/http, ни gorm. Это чистые Go-типы. Их можно безопасно передавать между слоями.
GORM-теги (`gorm:"..."`) — компромисс ради AutoMigrate. В строго чистой архитектуре DB-модель была бы отдельным типом в repository.go, а здесь лежали бы только бизнес-поля. Для большинства проектов тегов на домене достаточно — разделяй при необходимости.
type UserRepository ¶
type UserRepository interface {
Create(ctx context.Context, user *User) error
FindByID(ctx context.Context, id uint) (*User, error)
FindByEmail(ctx context.Context, email string) (*User, error)
Update(ctx context.Context, user *User) error
Delete(ctx context.Context, id uint) error
List(ctx context.Context, filter ListFilter, page pagination.OffsetPage) ([]*User, int64, error)
}
UserRepository — порт хранилища, используемый сервисом.
Service зависит от этого интерфейса, а не от конкретного *Repository. Это позволяет тестировать сервис без базы данных — достаточно мок-репозитория.
Compile-time проверка: var _ UserRepository = (*Repository)(nil) Если Repository не реализует интерфейс — ошибка компиляции, а не runtime-паника.
type UserResponse ¶
type UserResponse struct {
ID uint `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
Role UserRole `json:"role"`
Active bool `json:"active"`
}
UserResponse — представление пользователя в API-ответе. Чувствительные данные (пароли, токены) намеренно исключены.
type UserService ¶
type UserService interface {
Create(ctx context.Context, params CreateParams) (*User, error)
GetByID(ctx context.Context, id uint) (*User, error)
Update(ctx context.Context, id uint, params UpdateParams, callerID uint) (*User, error)
Delete(ctx context.Context, id uint) error
List(ctx context.Context, filter ListFilter, page pagination.OffsetPage) ([]*User, int64, error)
}
UserService — порт бизнес-логики, используемый хендлером.
Handler зависит от этого интерфейса, а не от конкретного *Service. Позволяет тестировать хендлер без сервиса и без базы данных.