跳到主要内容

整洁架构示例

Github StackBlitz

本示例展示了一个遵循整洁架构原则的 Go Fiber 应用。

描述

本项目提供了一个使用整洁架构构建 Web 应用的起点。它利用 Fiber 作为 Web 框架,MongoDB 作为数据库,并遵循整洁架构原则来分离关注点并提高可维护性。

要求

项目结构

  • api/: 包含 HTTP 处理器、路由和展示器。
  • pkg/: 包含核心业务逻辑和实体。
  • cmd/: 包含主应用入口点。

设置

  1. 克隆仓库

    git clone https://github.com/gofiber/recipes.git
    cd recipes/clean-architecture
  2. .env 文件中设置环境变量

    DB_URI=mongodb://localhost:27017
    DB_NAME=example_db
  3. 安装依赖

    go mod download
  4. 运行应用

    go run cmd/main.go

API 现在应该正在 http://localhost:3000 上运行。

API 端点

API 中提供了以下端点

  • GET /books: 列出所有书籍。
  • POST /books: 添加一本新书。
  • PUT /books: 更新一本现有书籍。
  • DELETE /books: 删除一本书籍。

使用示例

  1. 添加一本新书

    curl -X POST http://localhost:3000/books -d '{"title":"Book Title", "author":"Author Name"}' -H "Content-Type: application/json"
  2. 列出所有书籍

    curl http://localhost:3000/books
  3. 更新一本书籍

    curl -X PUT http://localhost:3000/books -d '{"id":"<book_id>", "title":"Updated Title", "author":"Updated Author"}' -H "Content-Type: application/json"
  4. 删除一本书籍

    curl -X DELETE http://localhost:3000/books -d '{"id":"<book_id>"}' -H "Content-Type: application/json"

<book_id> 替换为书籍的实际 ID。

整洁架构原则

整洁架构是一种软件设计哲学,强调关注点分离,使代码库更易于维护、测试和扩展。在本示例中,Go Fiber 应用通过将代码组织成不同的层,每层承担自己的责任,从而遵循了整洁架构原则。

整洁架构中的层

  1. 实体 (核心业务逻辑)
  • 位于 pkg/entities 目录。
  • 包含核心业务逻辑和领域模型,它们独立于任何外部框架或技术。
  1. 用例 (应用逻辑)
  • 位于 pkg/book 目录。
  • 包含特定于应用的业务规则和用例。该层协调数据在实体之间的流动。
  1. 接口适配器 (适配器和展示器)
  • 位于 api 目录。
  • 包含 HTTP 处理器、路由和展示器。该层负责将用例中的数据转换为适合 Web 框架(此处为 Fiber)的格式。
  1. 框架和驱动 (外部接口)
  • 位于 cmd 目录。
  • 包含主应用入口点以及任何外部依赖,例如 Web 服务器设置。

示例分解

  • 实体: entities.Book 结构体代表书籍的核心业务模型。
  • 用例: book.Service 接口定义了与书籍交互的方法,例如 InsertBook, UpdateBook, RemoveBook, 和 FetchBooks
  • 接口适配器: handlers 包包含与 book.Service 交互的 HTTP 处理器,用于处理 HTTP 请求和响应。
  • 框架和驱动: cmd/main.go 文件初始化 Fiber 应用并使用 routes.BookRouter 函数设置路由。

代码示例

entities/book.go

package entities

import "go.mongodb.org/mongo-driver/bson/primitive"

type Book struct {
ID primitive.ObjectID `json:"id" bson:"_id,omitempty"`
Title string `json:"title"`
Author string `json:"author"`
}

pkg/book/service.go

package book

import "clean-architecture/pkg/entities"

type Service interface {
InsertBook(book *entities.Book) (*entities.Book, error)
UpdateBook(book *entities.Book) (*entities.Book, error)
RemoveBook(id primitive.ObjectID) error
FetchBooks() ([]*entities.Book, error)
}

api/handlers/book_handler.go

package handlers

import (
"clean-architecture/pkg/book"
"clean-architecture/pkg/entities"
"clean-architecture/api/presenter"
"github.com/gofiber/fiber/v2"
"net/http"
"errors"
)

func AddBook(service book.Service) fiber.Handler {
return func(c *fiber.Ctx) error {
var requestBody entities.Book
err := c.BodyParser(&requestBody)
if err != nil {
c.Status(http.StatusBadRequest)
return c.JSON(presenter.BookErrorResponse(err))
}
if requestBody.Author == "" || requestBody.Title == "" {
c.Status(http.StatusInternalServerError)
return c.JSON(presenter.BookErrorResponse(errors.New("Please specify title and author")))
}
result, err := service.InsertBook(&requestBody)
if err != nil {
c.Status(http.StatusInternalServerError)
return c.JSON(presenter.BookErrorResponse(err))
}
return c.JSON(presenter.BookSuccessResponse(result))
}
}

cmd/main.go

package main

import (
"clean-architecture/api/routes"
"clean-architecture/pkg/book"
"github.com/gofiber/fiber/v2"
)

func main() {
app := fiber.New()
bookService := book.NewService() // Assume NewService is a constructor for the book service
routes.BookRouter(app, bookService)
app.Listen(":3000")
}

通过遵循整洁架构原则,本示例确保每一层都是独立的,可以修改或替换而不会影响其他层,从而实现更易于维护和扩展的应用。

结论

本示例提供了一个遵循整洁架构原则的 Go Fiber 应用的基本设置。它可以进一步扩展和定制,以满足更复杂应用的需求。

参考资料