当前位置: 首页 > news >正文

在 Go 项目的 DDD 分层架构中,Echo Web 框架及其 middleware 应该归属到哪一层?

在 Go 项目的 DDD 分层架构中,Echo Web 框架及其 middleware 应该归属到哪一层?main 方法应如何组织和放置?在 main 中启动 Echo Server 的最佳实践是什么?


📂 典型的 DDD + Echo 项目目录结构

.
├── cmd/
│   └── myapp/
│       └── main.go        // 程序入口,启动 echo server
├── internal/
│   ├── domain/            // 领域层,实体 (Entity)、值对象 (Value Object)、领域服务
│   ├── application/       // 应用层,用例逻辑,调用 domain
│   ├── infrastructure/    // 基础设施,数据库、第三方 API、日志等
│   └── interfaces/        // 接口层,HTTP handler,Echo router,middleware
└── pkg/                   // 公共库

各层职责

  • domain
    只包含业务核心模型和规则,不依赖外部框架。

  • application
    编排用例逻辑(比如「下单」调用库存和支付),依赖 domain。

  • infrastructure
    具体的实现(DB、缓存、消息队列、第三方服务),为 domain/application 提供支持。

  • interfaces

    • Echo 的 routerhandler
    • Echo 的 middleware(如日志、请求ID、恢复 panic)
    • handler 里调用 application 层的用例

middleware 放哪里?

放在 interfaces 层,因为 middleware 主要是 Web 层的适配逻辑。
例如:

internal/interfaces/http/middleware/logger.go
internal/interfaces/http/middleware/recover.go

main.go 里该做什么?

main.go 是程序入口,一般只做:

  1. 初始化配置(config/env)
  2. 初始化 logger
  3. 初始化基础设施(DB、缓存等)
  4. 组装依赖注入(application -> interfaces)
  5. 创建 Echo 实例并注册 middleware、路由
  6. 启动 server

示例:cmd/myapp/main.go

package mainimport ("log""myapp/internal/interfaces/http" // 你的 http router"myapp/internal/infrastructure/db"
)func main() {// 1. 初始化基础设施database, err := db.NewPostgres()if err != nil {log.Fatal(err)}// 2. 初始化 Echoe := http.NewServer(database) // 在 interfaces 层组装 Echo// 3. 启动服务if err := e.Start(":8080"); err != nil {log.Fatal(err)}
}

示例:internal/interfaces/http/server.go

package httpimport ("github.com/labstack/echo/v4""github.com/labstack/echo/v4/middleware""myapp/internal/interfaces/http/handler"
)func NewServer(db any) *echo.Echo {e := echo.New()// 注册全局中间件e.Use(middleware.Logger())e.Use(middleware.Recover())// 注册路由和 handlerh := handler.NewUserHandler(db) e.GET("/users/:id", h.GetUser)return e
}

✅ 总结:

  • echomiddleware 放在 interfaces 层(因为它们是 I/O 适配)。
  • main.go 放在 cmd/{app}/main.go,只负责组装依赖和启动 Echo server。
  • middleware 和路由初始化逻辑写在 internal/interfaces/http 下,不要放到 domainapplication

不过,有人倾向于将 Echo 归类为 Web 框架,并封装在基础设施层,而不是直接暴露给其他层。这样可以降低对具体框架的依赖,从而在未来需要替换为其他框架(如 Gin)时,能够更灵活地进行替换,而不会影响整体架构。

DDD 里面其实有两种常见做法:

  1. 把 web 框架(Echo/Gin/Fiber)放到 infrastructure 层,对外提供一个抽象接口(比如 HTTPServer 接口),application/handler 只依赖接口,不依赖具体实现。这样就能做到将来替换 web 框架时对上层透明。
  2. 把 web 框架直接放在 interfaces 层,但这时 interfaces 层就跟具体框架耦合了。如果未来要换框架,interfaces 层会有比较大的改动。

这两种其实各有权衡:

  • infrastructure 层更“纯粹”,因为它就是实现技术细节的地方。
  • interfaces 层更直观,因为很多人认为 HTTP handler 本身就是“接口适配”。

✅ 如果你坚持把 Echo 放到基础设施层,可以这样组织:

.
├── cmd/
│   └── myapp/
│       └── main.go
├── internal/
│   ├── domain/           
│   ├── application/      
│   ├── infrastructure/   // Echo server 在这里
│   │   └── web/
│   │       ├── echo.go   // Echo 实现
│   │       └── gin.go    // 将来可换 Gin
│   └── interfaces/       // 定义 HTTP handler(只暴露接口,不依赖 echo)
└── pkg/

定义一个抽象接口

internal/interfaces/server.go

package interfacestype HTTPServer interface {Start(addr string) errorShutdown() error
}

Echo 实现(基础设施层)

internal/infrastructure/web/echo.go

package webimport ("github.com/labstack/echo/v4""github.com/labstack/echo/v4/middleware""myapp/internal/interfaces"
)type EchoServer struct {e *echo.Echo
}func NewEchoServer() *EchoServer {e := echo.New()e.Use(middleware.Logger())e.Use(middleware.Recover())return &EchoServer{e: e}
}func (s *EchoServer) RegisterRoutes(register func(e *echo.Echo)) {register(s.e)
}func (s *EchoServer) Start(addr string) error {return s.e.Start(addr)
}func (s *EchoServer) Shutdown() error {return s.e.Close()
}

main.go 组装

cmd/myapp/main.go

package mainimport ("log""myapp/internal/infrastructure/web""myapp/internal/interfaces/http"
)func main() {// 基础设施层:echo 实现server := web.NewEchoServer()// 接口层:注册 handler 到 echoserver.RegisterRoutes(http.RegisterRoutes)// 启动if err := server.Start(":8080"); err != nil {log.Fatal(err)}
}

handler 不依赖 Echo

internal/interfaces/http/routes.go

package httpimport ("net/http""github.com/labstack/echo/v4"
)func RegisterRoutes(e *echo.Echo) {e.GET("/health", func(c echo.Context) error {return c.String(http.StatusOK, "ok")})
}

将来要换 Gin,只需要:

  • infrastructure/web/gin.go 实现 HTTPServer 接口
  • main.go 替换 web.NewEchoServer()web.NewGinServer()

👉 所以严格来说:Echo 属于基础设施层,只是大部分 Go 项目为了简单,直接把它放在 interfaces 层用了。

http://www.dtcms.com/a/356457.html

相关文章:

  • mysql安全运维之安全模型与原则-构建坚不可摧的数据库防护体系
  • 自然语言提取PDF表格数据
  • 电影交流|基于SprinBoot+vue的电影交流平台小程序系统(源码+数据库+文档)
  • 在基于 Go 的 DDD 分层架构中,包含多个server的项目目录结构应如何组织?
  • LoraConfig target modules加入embed_tokens(64)
  • 设计模式-行为型模式-命令模式
  • Spring 微服务架构下的单元测试优化实践:从本地连接到真实开发数据库的集成测试
  • Qt节点编辑器设计与实现:动态编辑与任务流可视化(一)
  • WebStorm-在WebStorm中使用Git管理项目
  • 【WPF】WPF 自定义控件实战:从零打造一个可复用的 StatusIconTextButton (含避坑指南)
  • 循环高级(2)
  • 面试八股文之——JVM与并发编程/多线程
  • Azure、RDP、NTLM 均现高危漏洞,微软发布2025年8月安全更新
  • 【物联网】什么是 DHT11(数字温湿度传感器)?
  • C++ 编译和运行 LibCurl 动态库和静态库
  • SyncBack 备份同步软件: 使用 FTPS、SFTP 和 HTTPS 安全加密传输文件
  • 【2025 完美解决】Failed connect to github.com:443; Connection timed out
  • 网络编程(2)—多客户端交互
  • 跨境物流新引擎:亚马逊AGL空运服务赋能卖家全链路升级
  • Pycharm 登录 Github 失败
  • idea2023.3遇到了Lombok失效问题,注释optional和annotationProcessorPaths即可恢复正常
  • “FAQ + AI”智能助手全栈实现方案
  • 极飞科技AI智慧农业实践:3000亩棉田2人管理+产量提15%,精准灌溉与老农操作门槛引讨论
  • autojs RSA加密(使用public.pem、private.pem)
  • 【拍摄学习记录】03-曝光
  • Lora与QLora
  • 创维E910V10C_晶晨S905L2和S905L3芯片_线刷固件包
  • SpringMVC相关梳理
  • 第三方软件测试:【深度解析SQL注入攻击原理和防御原理】
  • [Mysql数据库] 知识点总结6