echo 是 http 框架,ddd 是领域建模方法,职责不重叠;强行套用 ddd 目录会导致依赖倒置,http handler 直接操作领域对象;应让 echo 仅解析请求、校验输入、调用应用服务,所有领域逻辑由 appservice 封装协调。

为什么 Echo 项目里直接套 DDD 目录结构会出问题
因为 Echo 是 HTTP 层框架,而 DDD 是领域建模方法,两者职责不重叠——强行把 aggregate/ 或 domain/customer/ 塞进 routers/ 或 controllers/ 下,会导致依赖倒置:HTTP handler 直接 new Customer、调用 customer.ChangeName(),领域逻辑被路由层污染。
真正该做的是让 Echo 只负责三件事:解析请求、校验输入、调用应用服务;其余全部交给 domain 层处理。
- 所有 handler 函数里禁止出现
new Customer、&Customer{}、customer.Save()这类代码 - handler 只能调用
appservice.CreateCustomer()或appservice.UpdateCustomerName()这类封装好的应用服务函数 - 应用服务再协调聚合、仓储、工厂等 domain 组件,Echo 完全不知道
Customer长什么样
如何让 Echo 的 Request Body 映射到 DDD 值对象而不破坏封装
DDD 要求值对象不可变,但 json.Unmarshal 默认会反射赋值字段——如果值对象字段是小写(如 amount float64),反序列化失败;如果改成大写(Amount float64),又暴露了内部结构,违背封装原则。
正确做法是用独立的 DTO(Data Transfer Object)接收请求,再由工厂或应用服务转换为领域值对象。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
- 定义
type CreateCustomerRequest struct { Name string `json:"name"` Age int `json:"age"` }放在dto/目录下 - 在应用服务中用工厂构造:
customer, err := customerfactory.NewCustomer(req.Name, req.Age) - 工厂内部做校验(如
if req.Age ),保证聚合初始状态合法 - 绝不给领域值对象加
json:tag,也不让它出现在 HTTP 层代码里
仓储接口定义在 domain 层,但实现放在 infra 层时怎么让 Echo handler 找到它
Echo 的 handler 层不能 import infra/,否则 domain 层就依赖了基础设施——必须通过构造函数注入仓储实现,靠依赖注入容器或手动传参解耦。
典型错误是 handler 里写 repo := postgres.NewCustomerRepository(),这会让 domain 逻辑和 PostgreSQL 绑死。
- 先在
domain/customer/repository.go定义接口:type CustomerRepository interface { Save(c *Customer) error; FindByID(id string) (*Customer, error) } - 在
infra/postgres/customer_repo.go实现该接口,importgorm和具体数据库驱动 - 启动时(
main.go)创建 repo 实例,传给应用服务:svc := appservice.NewCustomerService(postgresRepo) - 再把 svc 注入 handler:
router.POST("/customers", handler.CreateCustomer(svc))
日志、事务、中间件这些横切关注点怎么不侵入 domain 层
Echo 的 middleware.LoggerWithConfig 和 middleware.Recover() 天然属于 transport 层,但事务控制容易误放错位置——比如在 handler 里手动开事务、commit/rollback,这就把数据一致性逻辑泄露到了 HTTP 层。
事务边界必须由应用服务控制,且只对写操作生效;读操作不应开启事务。
- 应用服务方法签名明确体现是否需要事务:
func (s *CustomerService) UpdateName(...) error内部用s.repo.WithTx(...)包裹 - Echo 中间件只记录请求耗时、状态码、URI,不碰业务数据
- 日志内容避免打印敏感字段(如
customer.ID可打,customer.Email应脱敏) - 不要在 domain 层 import
zap或ilogger,日志上下文由 handler 或应用服务注入
Customer.ChangeName() 永远不会被 echo.Context 直接调用,以及确保每次 Save() 都发生在事务内——这两点没守住,目录再标准也没用。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










