这是一个实用的电子商务微服务,它使用CQRS、事件源、垂直切片架构和事件驱动架构构建。点击标题
一个实际的电子商务示例,使用Golang和不同的软件架构和技术构建,如微服务架构,垂直切片架构,CQRS模式,域驱动设计(DDD),事件源,事件驱动架构和依赖注入。对于独立服务之间的通信,我们使用RabbitMQ进行异步消息传递,有时我们使用REST和gRPC调用进行实时通信。
您可以使用此项目作为模板,在此项目之上使用Go语言构建后端项目。
这个应用程序不是“面向业务”的,我的重点主要是技术部分,我只是想使用不同的技术,软件架构设计,原则和创建微服务应用程序所需的所有内容来实现一个示例。
特色功能
- 使用Vertical Slice Architecture作为高级架构
- 在RabbitMQ Message Broker之上使用Event Driven Architecture和自定义事件总线
- 在目录读取服务中使用基于CRUD的Data Centeric Architecture
- 在Event Sourcing服务中使用Audit Based,如订单服务
- 在Go-MediatR库上使用CQRS Pattern和Mediator Pattern
- 在uber-go/fx库上使用Dependency Injection和Inversion of Control
- 使用RESTFul API Echo 框架和使用swagger与 swaggo/swag 图书馆
- 使用gRpc进行内部服务通信
- 使用go-playground/validator验证REST和gRpc中的输入数据
- 使用Postgres和EventStoreDB用于完全支持事务的写数据库(ACID)
- 使用MongoDB和Elastic Search读取数据库(NOSQL)
- 使用OpenTelemetry用于集合Distributed Tracing,使用Jaeger和Zipkin
- 将OpenTelemetry用于集合Metrics,使用Prometheus和Grafana
- 使用Unit Test测试带有mocking依赖类的小单元,使用Mockery测试mocking依赖项
- 使用End2End Test和Integration Test来测试功能,并使用Docker容器(清理测试)和testcontainers-go库来测试它们的所有真实的依赖项
- 使用Zap和结构化日志
- 使用Viper进行配置管理
- 使用docker和docker-compose进行部署
- 在一些服务中使用Domain Driven Design,如目录写入服务和订单服务
- 使用Helm和Kubernetes进行部署
- 对所有微服务使用Outbox Pattern以保证交付或至少一次交付
- 使用Inbox Pattern处理接收方的幂等性和精确一次交付
技术 - 库
- ✔️ labstack/echo- 高性能、简约的 Go Web 框架
- ✔️ uber-go/zap- Go 中的快速、结构化、分级日志记录。
- ✔️ emperror/errors- 标准库错误包和 github.com/pkg/errors 的直接替换
- ✔️ open-telemetry/opentelemetry-go- OpenTelemetry Go API 和 SDK
- ✔️ open-telemetry/opentelemetry-go-contrib- OpenTelemetry-Go 扩展集合。
- ✔️ rabbitmq/amqp091-go- 由 RabbitMQ 团队维护的 AMQP 0-9-1 Go 客户端。最初由@streadway 发布:streadway/amqp
- ✔️ stretchr/testify- 具有常见断言和模拟的工具包,可以与标准库很好地配合
- ✔️ mehdihadeli/go-mediatr- Golang 中的中介模式实现,有助于创建基于 CQRS 的应用程序。
- ✔️ grpc-ecosystem/go-grpc-middleware- Golang gRPC 中间件:拦截器链接、身份验证、日志记录、重试等
- ✔️ grpc/grpc-go- gRPC 的 Go 语言实现。基于 HTTP/2 的 RPC
- ✔️ elastic/go-elasticsearch- Elasticsearch 的官方 Go 客户端
- ✔️ avast/retry-go- 用于重试机制的简单 golang 库
- ✔️ ahmetb/go-linq- Go 中的 .NET LINQ 功能
- ✔️ EventStore/EventStore-Client-Go- Event Store 版本 20 及更高版本的 Go 客户端。
- ✔️ olivere/elastic/v7- 已弃用:使用 Go 的官方 Elasticsearch 客户端,网址为
- ✔️ swaggo/swag- 使用 Swagger 2.0 for Go 自动生成 RESTful API 文档。
- ✔️ prometheus/client_golang- 用于 Go 应用程序的 Prometheus 仪器库
- ✔️ mongodb/mongo-go-driver- MongoDB 的 Go 驱动程序
- ✔️ go-redis/redis- Golang 的类型安全 Redis 客户端
- ✔️ go-gorm/gorm- Golang 的出色 ORM 库,旨在对开发人员友好
- ✔️ go-playground/validator- Go 结构和字段验证,包括跨字段、跨结构、地图、切片和数组潜水
- ✔️ spf13/viper- 带尖牙的 Go 配置
- ✔️ caarlos0/env- 一个简单的零依赖库,用于将环境变量解析为结构。
- ✔️ joho/godotenv- Ruby 的 dotenv 库的 Go 端口(从 .env 文件加载环境变量)
- ✔️ mcuadros/go-defaults- 使用标签设置默认值的 Go 结构
- ✔️ uber-go/fx- 基于依赖注入的 Go 应用程序框架。
- ✔️ testcontainers/testcontainers-go- Testcontainers for Go 是一个 Go 包,可以轻松创建和清理基于容器的依赖项以进行自动化集成/冒烟测试。
应用程序结构
在本项目中,我使用了垂直切片架构或重组为垂直切片架构,还在本项目中使用了功能文件夹结构。
- 我们将每个请求视为一个不同的用例或切片,封装并分组了从前端到后端的所有关注点。
- 当我们在 n-tire 架构中添加或更改应用程序中的某个功能时,我们通常会接触到应用程序中的许多不同 "层"。我们不是跨层耦合,而是沿切片垂直耦合,每次更改只影响一个切片。
- 我们将切片之间的耦合最小化,将切片内的耦合最大化。
- 通过这种方法,我们的每个垂直切片都能自行决定如何以最佳方式满足请求。新功能只会增加代码,我们不会改变共享代码,也不用担心副作用。使用 cqrs 模式来实现垂直切片架构是一个很好的选择。
在这里,我还使用 CQRS 将我的功能分解成非常小的部分,从而使我们的应用程序变得更小:
- 最大限度地提高性能、可扩展性和简易性。
- 在这种机制中添加新功能非常简单,无需对其他部分的代码进行任何破坏性修改。新功能只需添加代码,无需更改共享代码,也无需担心副作用。
- 易于维护,任何改动只影响一个命令或查询(或片段),避免对其他部分造成任何破坏性改动
- 在我们的代码中,它能更好地分离关注点和交叉关注点(在 MediatR 行为管道的帮助下),而不是用一个大的服务类来做很多事情。
使用 CQRS 后,我们的代码将更符合 SOLID 原则,尤其是在以下方面:
- 单一责任规则--因为负责特定操作的逻辑被封装在自己的类型中。
- 开放-封闭规则--因为要添加新的操作,不需要编辑任何现有类型,而是需要添加一个新文件,用一个新类型来代表该操作。
在这里,我们将每个业务功能切割成若干垂直切片,在每个切片内部,我们都有专门针对该功能的技术文件夹结构(命令、处理程序、基础架构、存储库、控制器、数据模型......),而不是一些技术拆分,例如为我们的服务、控制器和数据模型创建文件夹或层。
通常情况下,当我们开发某个功能时,我们需要一些技术方面的东西,例如
- 应用程序接口端点(控制器)
- 请求输入(Dto)
- 请求输出(Dto)
- 处理请求的类,例如命令和命令处理程序或查询和查询处理程序
- 数据模型
现在,我们可以把所有这些东西放在一起,这样就可以减少一些层或文件夹之间的跳转和依赖。
在 CQRS 中保持这种分割非常有效。它隔离了我们的操作,并将应用程序代码垂直切分,而不是水平切分。在我们的 CQRS 模式中,每个命令/查询处理程序都是一个单独的片段。这样可以减少层与层之间的耦合。每个处理程序都可以是一个独立的代码单元,甚至可以复制/粘贴。因此,我们可以调整具体方法,使其不遵循一般惯例(例如,使用自定义 SQL 查询或甚至不同的存储)。在传统的分层架构中,当我们改变一层的核心通用机制时,会影响到所有方法。
详细点击标题