go-mapper

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Go Mapper

Go Mapper

Generate pure mapper functions for GO modular architecture.
为Go模块化架构生成纯mapper函数。

When to Use

适用场景

  • Map HTTP request DTOs to use case inputs
  • Map persistence models to HTTP response DTOs
  • Map between any two struct representations across layers
  • Convert a slice of structs to a slice of another struct
  • Any struct-to-struct transformation that lives in a module
  • 将HTTP请求DTO映射为用例输入
  • 将持久化模型映射为HTTP响应DTO
  • 跨层的任意两种struct表示之间的映射
  • 将struct切片转换为另一种struct的切片
  • 模块内的任意struct到struct的转换

Location

文件位置

All mapper files live in
internal/modules/<module>/mapper/
.
One file per domain concept:
<name>_mapper.go
(e.g.,
user_mapper.go
).
所有mapper文件都存放在
internal/modules/<module>/mapper/
路径下。
每个领域概念对应一个文件:
<name>_mapper.go
(例如
user_mapper.go
)。

Function Signature Pattern

函数签名规范

Every mapper function follows the
To
prefix convention. It accepts one or more inputs and returns exactly one output, optionally with an error.
func ToXxx(input InputType) OutputType
func ToXxx(input InputType) (OutputType, error)
func ToXxx(a TypeA, b TypeB) OutputType
Single output only. Error is the only allowed second return value.
每个mapper函数都遵循
To
前缀的约定。它接收一个或多个输入,仅返回一个输出,可选择额外返回error。
func ToXxx(input InputType) OutputType
func ToXxx(input InputType) (OutputType, error)
func ToXxx(a TypeA, b TypeB) OutputType
仅允许单个返回值,error是唯一允许的第二个返回值。

File Structure

文件结构

  1. Package declaration and imports
  2. Public mapper functions
  3. Private helper functions (shared logic between public functions)
  1. 包声明与导入
  2. 公共mapper函数
  3. 私有辅助函数(公共函数之间的共享逻辑)

Examples

示例

Basic mapper

基础mapper

go
package mapper

import (
	"github.com/cristiano-pacheco/pingo/internal/modules/user/dto"
	"github.com/cristiano-pacheco/pingo/internal/modules/user/model"
	"github.com/cristiano-pacheco/pingo/internal/modules/user/usecase"
)

func ToCreateUserInput(req dto.CreateUserRequest) usecase.CreateUserInput {
	return usecase.CreateUserInput{
		Name:  req.Name,
		Email: req.Email,
	}
}

func ToUserResponse(u model.UserModel) dto.UserResponse {
	return dto.UserResponse{
		ID:        u.ID,
		Name:      u.Name,
		Email:     u.Email,
		CreatedAt: u.CreatedAt,
	}
}

func ToUserListResponse(models []model.UserModel) []dto.UserResponse {
	responses := make([]dto.UserResponse, len(models))
	for i, u := range models {
		responses[i] = ToUserResponse(u)
	}
	return responses
}
go
package mapper

import (
	"github.com/cristiano-pacheco/pingo/internal/modules/user/dto"
	"github.com/cristiano-pacheco/pingo/internal/modules/user/model"
	"github.com/cristiano-pacheco/pingo/internal/modules/user/usecase"
)

func ToCreateUserInput(req dto.CreateUserRequest) usecase.CreateUserInput {
	return usecase.CreateUserInput{
		Name:  req.Name,
		Email: req.Email,
	}
}

func ToUserResponse(u model.UserModel) dto.UserResponse {
	return dto.UserResponse{
		ID:        u.ID,
		Name:      u.Name,
		Email:     u.Email,
		CreatedAt: u.CreatedAt,
	}
}

func ToUserListResponse(models []model.UserModel) []dto.UserResponse {
	responses := make([]dto.UserResponse, len(models))
	for i, u := range models {
		responses[i] = ToUserResponse(u)
	}
	return responses
}

Mapper with multiple inputs

多输入mapper

go
package mapper

import (
	"github.com/cristiano-pacheco/pingo/internal/modules/order/dto"
	"github.com/cristiano-pacheco/pingo/internal/modules/order/model"
)

func ToOrderResponse(order model.OrderModel, items []model.OrderItemModel) dto.OrderResponse {
	return dto.OrderResponse{
		ID:    order.ID,
		Total: order.Total,
		Items: toOrderItemResponses(items),
	}
}

func toOrderItemResponses(items []model.OrderItemModel) []dto.OrderItemResponse {
	responses := make([]dto.OrderItemResponse, len(items))
	for i, item := range items {
		responses[i] = dto.OrderItemResponse{
			ID:       item.ID,
			Name:     item.ProductName,
			Quantity: item.Quantity,
			Price:    item.Price,
		}
	}
	return responses
}
go
package mapper

import (
	"github.com/cristiano-pacheco/pingo/internal/modules/order/dto"
	"github.com/cristiano-pacheco/pingo/internal/modules/order/model"
)

func ToOrderResponse(order model.OrderModel, items []model.OrderItemModel) dto.OrderResponse {
	return dto.OrderResponse{
		ID:    order.ID,
		Total: order.Total,
		Items: toOrderItemResponses(items),
	}
}

func toOrderItemResponses(items []model.OrderItemModel) []dto.OrderItemResponse {
	responses := make([]dto.OrderItemResponse, len(items))
	for i, item := range items {
		responses[i] = dto.OrderItemResponse{
			ID:       item.ID,
			Name:     item.ProductName,
			Quantity: item.Quantity,
			Price:    item.Price,
		}
	}
	return responses
}

Mapper with error return

带error返回的mapper

Use when transformation can fail (e.g., parsing, validation during mapping).
go
package mapper

import (
	"github.com/cristiano-pacheco/pingo/internal/modules/product/dto"
	"github.com/cristiano-pacheco/pingo/internal/modules/product/errs"
	"github.com/cristiano-pacheco/pingo/internal/modules/product/model"
)

func ToProductModel(req dto.CreateProductRequest) (model.ProductModel, error) {
	price, err := parsePrice(req.Price)
	if err != nil {
		return model.ProductModel{}, errs.ErrInvalidPrice
	}
	return model.ProductModel{
		Name:  req.Name,
		Price: price,
	}, nil
}
当转换可能失败时使用(例如映射过程中的解析、校验场景)。
go
package mapper

import (
	"github.com/cristiano-pacheco/pingo/internal/modules/product/dto"
	"github.com/cristiano-pacheco/pingo/internal/modules/product/errs"
	"github.com/cristiano-pacheco/pingo/internal/modules/product/model"
)

func ToProductModel(req dto.CreateProductRequest) (model.ProductModel, error) {
	price, err := parsePrice(req.Price)
	if err != nil {
		return model.ProductModel{}, errs.ErrInvalidPrice
	}
	return model.ProductModel{
		Name:  req.Name,
		Price: price,
	}, nil
}

Slice mapper pattern

切片mapper模式

When mapping a single item, add a corresponding list function if the type appears in collections.
go
func ToArticleResponse(a model.ArticleModel) dto.ArticleResponse {
	return dto.ArticleResponse{
		ID:     a.ID,
		Title:  a.Title,
		Author: toAuthorResponse(a),
	}
}

func ToArticleListResponse(models []model.ArticleModel) []dto.ArticleResponse {
	responses := make([]dto.ArticleResponse, len(models))
	for i, a := range models {
		responses[i] = ToArticleResponse(a)
	}
	return responses
}

func toAuthorResponse(a model.ArticleModel) dto.AuthorResponse {
	return dto.AuthorResponse{
		ID:   a.AuthorID,
		Name: a.AuthorName,
	}
}
映射单个对象时,如果该类型会以集合形式出现,需要新增对应的列表转换函数。
go
func ToArticleResponse(a model.ArticleModel) dto.ArticleResponse {
	return dto.ArticleResponse{
		ID:     a.ID,
		Title:  a.Title,
		Author: toAuthorResponse(a),
	}
}

func ToArticleListResponse(models []model.ArticleModel) []dto.ArticleResponse {
	responses := make([]dto.ArticleResponse, len(models))
	for i, a := range models {
		responses[i] = ToArticleResponse(a)
	}
	return responses
}

func toAuthorResponse(a model.ArticleModel) dto.AuthorResponse {
	return dto.AuthorResponse{
		ID:   a.AuthorID,
		Name: a.AuthorName,
	}
}

Naming

命名规范

  • File:
    <name>_mapper.go
    in
    mapper/
    package
  • Public functions:
    ToXxx
    — where
    Xxx
    is the output type name (e.g.,
    ToUserResponse
    ,
    ToCreateUserInput
    )
  • Private helpers:
    toXxx
    — lowercase prefix for shared sub-mapping logic
  • Slice variants:
    ToXxxList
    or
    ToXxxListResponse
  • 文件:
    mapper/
    包下的
    <name>_mapper.go
  • 公共函数:
    ToXxx
    —— 其中
    Xxx
    是输出类型的名称(例如
    ToUserResponse
    ToCreateUserInput
  • 私有辅助函数:
    toXxx
    —— 共享的子映射逻辑使用小写前缀
  • 切片变种:
    ToXxxList
    ToXxxListResponse

Rules

规则

  1. Functions only — no structs, no interfaces, no constructors, no port files
  2. To
    prefix
    — every public function starts with
    To
  3. Single output — one return value, or one return value + error
  4. No
    context.Context
    — mappers are pure transformations, no I/O
  5. No side effects — no logging, no database calls, no external dependencies
  6. Private helpers — extract shared sub-mapping logic into private functions
  7. No comments on functions — function names are self-documenting via the
    To
    convention
  8. Slice helpers — add a list variant when the mapped type appears in collections
  9. Error return only when transformation can fail — prefer no-error signatures; use error only for parsing or conversion failures
  1. 仅包含函数 —— 不允许struct、接口、构造函数、端口文件
  2. To
    前缀
    —— 所有公共函数都以
    To
    开头
  3. 单输出 —— 仅一个返回值,或一个返回值+error
  4. 不允许传入
    context.Context
    —— mapper是纯转换逻辑,无I/O操作
  5. 无副作用 —— 不允许日志、数据库调用、外部依赖
  6. 私有辅助函数 —— 将共享的子映射逻辑提取到私有函数中
  7. 无需给函数加注释 —— 遵循
    To
    命名约定的函数名本身就具备自说明性
  8. 切片辅助函数 —— 当被映射的类型会以集合形式出现时,新增对应的列表转换变种
  9. 仅当转换可能失败时才返回error —— 优先使用无error的签名;仅在解析或转换失败的场景下使用error返回

Workflow

工作流程

  1. Create mapper file in
    mapper/<name>_mapper.go
  2. Run
    make lint
    to verify code quality
  3. Run
    make nilaway
    for static analysis
  1. mapper/<name>_mapper.go
    路径下创建mapper文件
  2. 运行
    make lint
    验证代码质量
  3. 运行
    make nilaway
    执行静态分析