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

在基于 Go 的 DDD 分层架构中,包含多个server的项目目录结构应如何组织?

在基于 Go 的 DDD 分层架构中,项目目录结构应如何组织?

在Golang项目中,采用DDD(Domain-Driven Design,领域驱动设计)的分层架构时,没有严格的标准规范,因为DDD更注重设计原则而非固定结构。但根据社区实践和常见实现,通常分为四层:Interface层(或Presentation层,处理外部交互如API)、Application层(协调用例和业务流程)、Domain层(核心领域模型和逻辑)、Infrastructure层(基础设施如数据库、外部服务)。这些层通过接口解耦,确保领域逻辑独立。

以下是一个典型的目录结构示例,基于多个来源的总结(如Medium文章、DEV Community和Talent500博客)。实际项目可根据规模调整,例如小型项目可能将所有层放在单个包下,大型项目则细分子包。结构通常置于internal/目录下,以防止外部导入。

project-root/
├── cmd/                  # 应用入口点,包含main函数
│   └── main.go           # 启动服务器或CLI的入口
├── internal/             # 内部包,核心代码
│   ├── application/      # Application层:用例服务,协调领域逻辑(不含业务规则)
│   │   └── services/     # 子目录示例
│   │       └── order_service.go  # 示例:订单用例服务
│   ├── domain/           # Domain层:核心业务模型、实体、值对象、领域服务、仓库接口
│   │   ├── model/        # 或直接按领域细分,如order/
│   │   │   └── order.go  # 示例:订单实体
│   │   ├── repository/   # 仓库接口(抽象数据访问)
│   │   │   └── order_repository.go
│   │   └── service/      # 领域服务(复杂业务逻辑)
│   │       └── order_domain_service.go
│   ├── infrastructure/   # Infrastructure层:具体实现,如数据库、外部API
│   │   ├── persistence/  # 数据持久化实现
│   │   │   └── order_repository_impl.go  # 仓库接口的数据库实现
│   │   ├── datastore/    # 数据存储具体实现(如SQL、NoSQL)
│   │   └── transport/    # 外部通信(如HTTP客户端)
│   │       └── http/     # HTTP相关实现
│   └── interfaces/       # Interface/Presentation层:外部适配器,如HTTP handler、CLI
│       ├── handlers/     # 处理程序
│       │   └── order_handler.go  # 示例:订单API handler
│       ├── routes/       # 路由定义
│       │   └── order_routes.go
│       └── middleware/   # 中间件(如日志、认证)
│           └── logging.go
├── pkg/                  # 可复用公共包(可选,非DDD核心)
│   └── utils/            # 工具函数
└── go.mod                # 模块定义

说明:

  • Domain层 是DDD的核心,应保持纯净,只包含业务逻辑,不依赖其他层。仓库接口定义在这里,但实现放在Infrastructure层。
  • Application层 处理事务边界和用例(如创建订单流程),调用Domain层的服务和仓库接口。
  • Infrastructure层 提供具体技术实现,可替换(如切换数据库)。
  • Interfaces层 处理输入输出,如REST API、gRPC或命令行。
  • 对于单一领域的小项目,可将所有层合并到一个包(如tracking/)中,随着增长再拆分子包。
  • 建议使用依赖注入(如Wire)来组装层级,避免循环依赖。

这个结构受Clean Architecture影响,与DDD兼容。如果项目涉及多个领域,可在domain/下按领域细分(如domain/order/domain/user/)。


如果项目中既需要启动 HTTP Server 和 gRPC Server,同时还包含一个不对外提供服务的 Worker,那么这几类 Server 应当如何划分和归类?

在Golang项目采用DDD分层架构时,对于需要启动多个服务器(如HTTP server、gRPC server)和一个内部worker的场景,目录结构的设计重点在于保持共享的核心逻辑(Domain和Application层)独立,同时通过cmd/目录支持多个入口点(entry points)。这是一种常见实践,受Clean Architecture和Hexagonal Architecture影响,与DDD兼容,能避免代码重复并便于维护。以下基于社区最佳实践的建议结构和放置方式。

推荐目录结构调整

基于原始DDD四层结构(Interfaces、Application、Domain、Infrastructure),为支持多个服务器,我们在cmd/下添加子目录,每个子目录对应一个独立的可执行程序(main函数)。这允许每个服务器或worker有自己的启动逻辑,但共享internal/下的业务代码。结构示例如下:

project-root/
├── cmd/                  # 多个入口点,每个子目录是一个可执行程序
│   ├── http-server/      # HTTP服务器入口
│   │   └── main.go       # 启动HTTP服务器,注入依赖(如路由、handlers)
│   ├── grpc-server/      # gRPC服务器入口
│   │   └── main.go       # 启动gRPC服务器,注册服务实现
│   └── worker/           # 内部worker入口(不对外)
│       └── main.go       # 启动worker进程,如消费队列或定时任务
├── internal/             # 核心共享代码
│   ├── application/      # 用例服务,协调业务(如OrderService调用领域逻辑)
│   │   └── services/
│   │       └── order_service.go
│   ├── domain/           # 核心领域模型、实体、仓库接口
│   │   ├── model/
│   │   │   └── order.go
│   │   └── repository/
│   │       └── order_repository.go
│   ├── infrastructure/   # 基础设施实现(如DB、队列、外部服务)
│   │   ├── persistence/  # 数据持久化
│   │   │   └── order_repository_impl.go
│   │   └── queue/        # 如消息队列实现(worker可能依赖)
│   │       └── rabbitmq.go  # 示例:worker消费的队列
│   └── interfaces/       # 外部适配器,按协议细分
│       ├── http/         # HTTP相关:handlers、routes、中间件
│       │   ├── handlers/
│       │   │   └── order_handler.go
│       │   └── routes/
│       │       └── order_routes.go
│       └── grpc/         # gRPC相关:proto文件、服务实现
│           ├── proto/    # .proto文件和生成的代码
│           │   └── order.proto
│           └── server/   # gRPC服务实现
│               └── order_grpc.go  # 实现gRPC接口,调用Application服务
├── pkg/                  # 可复用工具(可选)
│   └── utils/
└── go.mod

放置说明

  • HTTP Server

    • 放置在cmd/http-server/main.go中。这里是启动HTTP服务器的入口,例如使用Gin或net/http库,注册interfaces/http/下的routes和handlers。
    • 为什么在这里?cmd/适合应用入口,避免污染核心层。main函数中通过依赖注入(DI,如使用Wire或手动)组装Application服务、仓库等,然后启动服务器(如http.ListenAndServe())。
    • 示例代码片段(main.go):
      package mainimport ("yourproject/internal/application/services""yourproject/internal/interfaces/http/handlers""yourproject/internal/interfaces/http/routes"// ... 其他导入
      )func main() {// 初始化依赖(如DB连接、仓库)orderService := services.NewOrderService(/* 注入仓库 */)router := routes.SetupRouter(orderService)  // 设置路由http.ListenAndServe(":8080", router)
      }
      
    • Handlers和routes放在interfaces/http/下,确保与领域逻辑解耦。
  • gRPC Server

    • 放置在cmd/grpc-server/main.go中。这里启动gRPC服务器,例如使用grpc-go库,注册interfaces/grpc/server/下的服务实现。
    • 为什么在这里?类似于HTTP,gRPC作为另一种外部接口,但独立入口允许单独部署或 scaling。proto文件和生成的代码放在interfaces/grpc/proto/,服务实现调用Application层用例。
    • 示例代码片段(main.go):
      package mainimport ("google.golang.org/grpc""yourproject/internal/application/services""yourproject/internal/interfaces/grpc/server"// ... proto导入
      )func main() {s := grpc.NewServer()orderService := services.NewOrderService(/* 注入 */)pb.RegisterOrderServer(s, server.NewOrderGRPCServer(orderService))s.Serve(/* 监听端口 */)
      }
      
    • 这保持gRPC独立于HTTP,但共享Domain和Application逻辑。
  • Worker(内部不对外服务)

    • 放置在cmd/worker/main.go中。这里启动后台进程,例如使用goroutines处理定时任务、消费消息队列(如RabbitMQ或Kafka),或运行批处理。
    • 为什么在这里?Worker不是外部接口,不需要handlers,而是直接调用Application服务或Infrastructure实现。放在cmd/下作为独立可执行,便于单独运行(如在Kubernetes中作为sidecar或job)。
    • 示例代码片段(main.go):
      package mainimport ("yourproject/internal/application/services""yourproject/internal/infrastructure/queue"// ... 
      )func main() {// 初始化依赖orderService := services.NewOrderService(/* 注入 */)consumer := queue.NewRabbitMQConsumer(orderService)consumer.Start()  // 无限循环消费队列
      }
      
    • 相关基础设施(如队列客户端)放在infrastructure/queue/下。

额外建议

  • 依赖注入:使用Google Wire或fx来组装层级依赖,确保main函数简洁。
  • 配置和日志:在每个main中处理特定配置(如端口、队列连接),但共享通用配置(如通过环境变量或config包)。
  • 部署考虑:每个cmd子目录编译成独立二进制,便于微服务化。如果项目规模小,可先合并到一个main中用flag切换模式,但多入口更灵活。
  • 为什么不放在其他层:DDD强调Domain独立,Interfaces只处理适配,Application协调用例。启动逻辑是"应用"级别,故放cmd/。
  • 如果项目涉及多个领域,可进一步在domain/下按领域细分(如domain/order/、domain/user/),服务器入口相应调整。

这个结构在实际项目中证明有效,便于测试和扩展。如果你的项目有特定框架(如Echo for HTTP、grpc-go for gRPC),可据此调整。

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

相关文章:

  • 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
  • 《Linux 网络编程六:数据存储与SQLite应用指南》
  • LabVIEW转速仪校准系统
  • uniapp跨平台开发---uni.request返回int数字过长精度丢失
  • uni-app + Vue3 开发H5 页面播放海康ws(Websocket协议)的视频流