golang模块化微服务项目布局的Todo后端示例插件go-todo-backend的使用
Golang模块化微服务项目布局的Todo后端示例插件go-todo-backend使用
项目简介
go-todo-backend是一个使用模块化项目布局的Go语言Todo后端示例,适用于产品级微服务开发。它适合作为中型到大型项目的起点。
这个示例使用:
主要特性:
- 模块化项目结构
- 包含完整测试的示例
- Docker部署支持
- 兼容todobackend规范
安装与运行
前提条件
运行步骤
- 准备.env文件
cp .env.sample .env
- 启动PostgreSQL并创建数据库
docker-compose up -d
- 准备数据库schema
rel migrate
- 构建并运行
make
项目结构
.
├── api
│ ├── handler
│ │ ├── todos.go
│ │ └── [其他handler].go
│ └── middleware
│ └── [其他中间件].go
├── bin
│ ├── api
│ └── [其他可执行文件]
├── cmd
│ ├── api
│ │ └── main.go
│ └── [其他cmd]
│ └── main.go
├── db
│ ├── schema.sql
│ └── migrations
│ └── [迁移文件]
├── todos
│ ├── todo.go
│ ├── create.go
│ ├── update.go
│ ├── delete.go
│ ├── service.go
│ └── todostest
│ ├── todo.go
│ └── service.go
├── [其他领域]
│ ├── [实体a].go
│ ├── [业务逻辑].go
│ ├── [其他领域]test
│ │ └── service.go
│ └── service.go
└── [其他客户端]
├── [实体b].go
├── client.go
└── [其他客户端]test
└── client.go
代码示例
Todo实体定义 (todos/todo.go)
package todos
import (
"time"
"github.com/go-rel/rel"
)
// Todo represents todo model.
type Todo struct {
ID int
Title string
Completed bool
CreatedAt time.Time
UpdatedAt time.Time
}
// TableName for todos.
func (Todo) TableName() string {
return "todos"
}
// TodoRepository interface for todo repository.
type TodoRepository interface {
FindAll(todos *[]Todo) error
Find(todo *Todo, id int) error
Insert(todo *Todo) error
Update(todo *Todo) error
Delete(todo *Todo) error
}
Todo服务实现 (todos/service.go)
package todos
import (
"context"
"github.com/go-rel/rel"
)
type service struct {
repository rel.Repository
}
// NewService creates new todo service.
func NewService(repository rel.Repository) *service {
return &service{
repository: repository,
}
}
// List returns all todos.
func (s *service) List(ctx context.Context, todos *[]Todo) error {
return s.repository.FindAll(ctx, todos)
}
// Get returns single todo by id.
func (s *service) Get(ctx context.Context, todo *Todo, id int) error {
return s.repository.Find(ctx, todo, rel.Eq("id", id))
}
// Create creates new todo.
func (s *service) Create(ctx context.Context, todo *Todo) error {
return s.repository.Insert(ctx, todo)
}
// Update updates existing todo.
func (s *service) Update(ctx context.Context, todo *Todo) error {
return s.repository.Update(ctx, todo)
}
// Delete deletes existing todo.
func (s *service) Delete(ctx context.Context, todo *Todo) error {
return s.repository.Delete(ctx, todo)
}
HTTP处理器 (api/handler/todos.go)
package handler
import (
"net/http"
"strconv"
"github.com/Fs02/go-todo-backend/todos"
"github.com/go-chi/chi"
"github.com/go-chi/render"
)
// TodoHandler handles todo http requests.
type TodoHandler struct {
service *todos.Service
}
// NewTodoHandler returns new todo handler.
func NewTodoHandler(service *todos.Service) *TodoHandler {
return &TodoHandler{
service: service,
}
}
// List handles GET /todos
func (h *TodoHandler) List(w http.ResponseWriter, r *http.Request) {
var todos []todos.Todo
if err := h.service.List(r.Context(), &todos); err != nil {
render.Render(w, r, ErrInternalServer(err))
return
}
render.JSON(w, r, todos)
}
// Get handles GET /todos/{id}
func (h *TodoHandler) Get(w http.ResponseWriter, r *http.Request) {
var todo todos.Todo
id, _ := strconv.Atoi(chi.URLParam(r, "id"))
if err := h.service.Get(r.Context(), &todo, id); err != nil {
render.Render(w, r, ErrNotFound)
return
}
render.JSON(w, r, todo)
}
// Create handles POST /todos
func (h *TodoHandler) Create(w http.ResponseWriter, r *http.Request) {
var todo todos.Todo
if err := render.DecodeJSON(r.Body, &todo); err != nil {
render.Render(w, r, ErrBadRequest(err))
return
}
if err := h.service.Create(r.Context(), &todo); err != nil {
render.Render(w, r, ErrInternalServer(err))
return
}
render.Status(r, http.StatusCreated)
render.JSON(w, r, todo)
}
领域(domain)与客户端(client)的区别
领域和客户端文件夹非常相似,区别在于客户端文件夹实际上不实现任何业务逻辑(服务),而是调用任何内部/外部API来处理领域实体的客户端。
更多关于golang模块化微服务项目布局的Todo后端示例插件go-todo-backend的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang模块化微服务项目布局的Todo后端示例插件go-todo-backend的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Go-Todo-Backend: 模块化微服务Todo后端示例
下面我将介绍一个基于Go语言的模块化微服务Todo后端项目布局示例,并提供关键代码实现。
项目结构
go-todo-backend/
├── cmd/
│ └── main.go # 应用入口
├── internal/
│ ├── config/ # 配置管理
│ ├── controller/ # HTTP控制器
│ ├── domain/ # 核心业务模型
│ ├── repository/ # 数据访问层
│ ├── service/ # 业务逻辑层
│ └── server/ # HTTP服务器配置
├── pkg/
│ ├── database/ # 数据库连接
│ └── middleware/ # 中间件
├── api/ # API文档/OpenAPI规范
├── migrations/ # 数据库迁移文件
├── go.mod
└── go.sum
核心代码实现
1. 领域模型 (internal/domain/todo.go)
package domain
import "time"
type Todo struct {
ID uint `json:"id"`
Title string `json:"title" validate:"required"`
Description string `json:"description"`
Completed bool `json:"completed"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type TodoRepository interface {
GetAll() ([]Todo, error)
GetByID(id uint) (*Todo, error)
Create(todo *Todo) error
Update(id uint, todo *Todo) error
Delete(id uint) error
}
2. 服务层 (internal/service/todo_service.go)
package service
import (
"go-todo-backend/internal/domain"
"go-todo-backend/internal/repository"
)
type TodoService struct {
repo repository.TodoRepository
}
func NewTodoService(repo repository.TodoRepository) *TodoService {
return &TodoService{repo: repo}
}
func (s *TodoService) GetAllTodos() ([]domain.Todo, error) {
return s.repo.GetAll()
}
func (s *TodoService) GetTodoByID(id uint) (*domain.Todo, error) {
return s.repo.GetByID(id)
}
func (s *TodoService) CreateTodo(todo *domain.Todo) error {
return s.repo.Create(todo)
}
func (s *TodoService) UpdateTodo(id uint, todo *domain.Todo) error {
return s.repo.Update(id, todo)
}
func (s *TodoService) DeleteTodo(id uint) error {
return s.repo.Delete(id)
}
3. HTTP控制器 (internal/controller/todo_controller.go)
package controller
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"go-todo-backend/internal/domain"
"go-todo-backend/internal/service"
)
type TodoController struct {
service *service.TodoService
}
func NewTodoController(service *service.TodoService) *TodoController {
return &TodoController{service: service}
}
func (c *TodoController) GetTodos(ctx *gin.Context) {
todos, err := c.service.GetAllTodos()
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
ctx.JSON(http.StatusOK, todos)
}
func (c *TodoController) GetTodo(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 32)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
return
}
todo, err := c.service.GetTodoByID(uint(id))
if err != nil {
ctx.JSON(http.StatusNotFound, gin.H{"error": "Todo not found"})
return
}
ctx.JSON(http.StatusOK, todo)
}
// 其他CRUD方法类似...
4. 主程序入口 (cmd/main.go)
package main
import (
"log"
"github.com/gin-gonic/gin"
"go-todo-backend/internal/config"
"go-todo-backend/internal/controller"
"go-todo-backend/internal/repository"
"go-todo-backend/internal/server"
"go-todo-backend/internal/service"
"go-todo-backend/pkg/database"
)
func main() {
// 加载配置
cfg, err := config.Load()
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
// 初始化数据库
db, err := database.NewPostgresConnection(cfg.DB)
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
// 自动迁移
if err := database.Migrate(db); err != nil {
log.Fatalf("Failed to migrate database: %v", err)
}
// 初始化依赖
todoRepo := repository.NewTodoRepository(db)
todoService := service.NewTodoService(todoRepo)
todoController := controller.NewTodoController(todoService)
// 创建Gin路由
router := gin.Default()
// 注册路由
api := router.Group("/api")
{
api.GET("/todos", todoController.GetTodos)
api.GET("/todos/:id", todoController.GetTodo)
api.POST("/todos", todoController.CreateTodo)
api.PUT("/todos/:id", todoController.UpdateTodo)
api.DELETE("/todos/:id", todoController.DeleteTodo)
}
// 启动服务器
srv := server.NewServer(cfg.Server, router)
if err := srv.Run(); err != nil {
log.Fatalf("Failed to start server: %v", err)
}
}
使用说明
- 克隆项目后,安装依赖:
go mod tidy
- 配置数据库连接信息:
cp config.example.yaml config.yaml
# 然后编辑config.yaml文件
- 运行项目:
go run cmd/main.go
API端点
GET /api/todos
- 获取所有Todo项GET /api/todos/:id
- 获取特定Todo项POST /api/todos
- 创建新的Todo项PUT /api/todos/:id
- 更新Todo项DELETE /api/todos/:id
- 删除Todo项
扩展建议
- 添加JWT认证中间件
- 实现分页和过滤功能
- 添加Swagger文档
- 集成测试套件
- 添加Docker支持
这个示例展示了如何构建一个模块化的Go微服务后端,遵循清晰的层次结构,便于维护和扩展。