【读书笔记】《Go With The Domain》1. 准备项目案例
📔【读书笔记】《Go With The Domain》1. 准备项目案例
2023-10-4
| 2024-3-24
0  |  0 分钟
type
status
date
slug
summary
tags
category
icon
password
Sub-item
Last edited time
Mar 24, 2024 03:12 AM
Parent item
领域
该电子书通过实际案例的方式,介绍了在项目中如何使用DDD和CQRS实践整洁架构,并对一些常见反模式的设计提出了改进方法,最终完成一个用 Golang 构建的面向业务的应用程序框架。
本书的想法是不要过多关注基础设施和实现细节,在介绍具体的方法之前,需要先设计一个便于举例的模拟项目案例,本文就是介绍该模拟项目的框架和背景。

基础设施:使用Google Cloud托管K8S集群

考虑到使用自建K8S集群部署需要一个专门的DevOps团队,成本较高,因此采用托管K8S集群到云上的方式更加灵活方便, 你只需要提供容器镜像即可。在此容器内,您可以运行用任何语言编写的应用程序,该应用程序可以使用 HTTP 或 gRPC API 公开端口。也可以在此容器内处理 Pub/Sub 消息。这就是您在基础设施方面所需要的一切。本书创建了一个功能齐全、真实的应用程序,使用 Terraform 通过一条命令将此应用程序部署到 Google Cloud。
当然,也可以通过docker-compose将应用部署到本地。

基础设施:本地运行

对于由数百个微服务构建的项目来说,实现这一目标要困难得多。幸运的是,我们的项目 Wild Workouts 只有 5 个服务。在 Wild Workouts 中,我们创建了 Docker Compose,并支持热加载。
  • 对于前端,我们使用带有 vue-cli-service 服务工具的容器。
  • 对于后端来说,情况稍微复杂一些。在所有容器中,我们运行reflex工具。 reflex 侦听任何触发服务重新编译的代码更改。

运行步骤

  1. 安装dockerdocker-compose
  1. 下载代码:git clone https://github.com/ThreeDotsLabs/wild-workouts-go-ddd-example.git && cd wild-workouts-go-ddd-example
  1. 运行:docker-compose up
  1. 访问: http://localhost:8080/。 这里官方也部署了一个可以公开访问的示例可以直接访问: https://threedotslabs-wildworkouts.web.app

示例项目Wild Workouts是做什么的

很多Go教程中的举得例子在实际项目中几乎没什么用。为了避免这个问题,我们创建了 Wild Workouts 应用程序作为一个功能齐全的项目。只有一开始花足够的时间,才能在后期的实施过程中节省更多的时间。
闲话少说,进入正题。Wild Workouts 是一款面向私人健身教练和学员的应用程序。
  • 教练可以在他们有空的时间段里制定训练时间表
  • 学员可以在安排的指定日期内训练
  • 其他功能包括:
    • “积分”管理(学员可以安排多少次培训)
    • 取消 – 如果训练在开始前 24 小时内取消,学员将不会收到返还的积分,如果教练取消,那么额外赠送一节课作为补偿
    • 培训重新安排
      • 如果有人想要在培训开始前 24 小时内重新安排培训,则需要得到第二位参与者(教练或学员)的批准
    • 日历视图
教练设置时间表
教练设置时间表
学员选择健身的时间
学员选择健身的时间

前端技术

前端和项目设计关系不大,不需要特别关注,这里用到的前端技术有:
  • OpenAPI (Swagger) client。生成后端接口的说明文档的工具,方便JavaScript访问
  • Bootstrap。前端资源打包工具,创建HTML等
  • Vue.js。前端开发框架

后端服务

  1. trainer服务。管理教练的时间表,提供公开的HTTP接口,和内部gRPC接口
  1. trainings服务。管理学员的培训。提供公开的HTTP接口
  1. users服务。管理积分和用户信息

公开HTTP接口

应用程序执行的大多数操作都是由 HTTP API 触发的。我多次听到 Go 新手问他们应该使用什么框架来创建 HTTP 服务。我始终建议不要在 Go 中使用任何类型的 HTTP 框架。一个简单的路由器,比如 chi 就足够了。 chi 只为我们提供了轻量级的粘合剂来定义我们的 API 支持哪些 URL 和方法。在底层,它使用 Go 标准库 http 包,因此所有相关工具(如中间件)都是 100% 兼容的。在 Go 中使用任何类型的框架都会增加不必要的复杂性,并将您的项目与该框架耦合在一起,这里需要遵循KISS原则(Keep It Simple And Stupid)。所有服务都以相同的方式运行 HTTP 服务器。
译注:这里我们使用gin框架也是没有问题的。
然后在trainings服务中启动:
RunHTTPServer的入参是createHandler函数,createHandler需要返回http.Handler。在我们的例子中,它是由 oapi-codegen 生成的 HandlerFromMux (在openapi_api.gen.go)。它为我们提供了 OpenAPI 规范中的所有路径和查询参数
接口定义在trainings.yml:
如果需要修改接口,需要重新生成GO和JavaScript客户端的代码,已经封装在make命令中: make openapi
工具不仅生成路由,还生成了业务功能接口:
下面是实现该接口的例子:
HTTP 路径并不是 OpenAPI 规范生成的唯一内容。更重要的是,它还为我们提供了响应和请求的模型。在大多数情况下,模型比 API 路径和方法复杂得多。生成它们可以在任何 API 约定更改期间节省时间。
举例,定义的数据(trainings.yml)如下:
生成的结构体如下:

云数据库Firestore

如果我们想以最现代、可扩展且真正无服务器的方式构建应用程序,Firestore 是一个自然的选择。我们将开箱即用。他的限制是1 update / second / one document. 但对于独立的文档是并行处理的,实际使用时问题不大。

在本地运行 Firestore

不幸的是 Firestore 模拟器并不完美。我发现有些情况下模拟器与真实版本不100%兼容。我也遇到过一些情况,当我在事务中更新和读取同一个文档时,它导致了死锁。在我看来,这个功能对于本地开发来说已经足够了。另一种选择可能是拥有一个单独的 Google Cloud 项目来进行本地开发。我在这里的偏好是拥有一个真正本地的本地环境,并且不依赖于任何外部服务。它也更容易设置,并且可以在以后的持续集成中使用。自 5 月底以来,Firestore 模拟器提供了 UI。它已添加到 Docker Compose 中,并可从 http://localhost:4000/ 获取。

使用Firestore

除了 Firestore 实现之外,代码在本地和生产环境中的工作方式相同。当我们在本地使用模拟器时,我们需要在.env文件中将 env FIRESTORE_EMULATOR_HOST 设置为模拟器(firestore:8787)来运行我们的应用程序
使用firestore:
读出来的数据都是map[string]interface,再转换为需要的数据格式

生产环境部署

再输入一些项目信息和账户信息就可以部署了。
 
《Go With The Domain》电子书下载:
 
架构设计
  • 读书笔记
  • 技术架构
  • 【读书笔记】《Go With The Domain》9. 基础CQRS【读书笔记】《Go With The Domain》10. 结合 DDD、CQRS 和整洁架构
    目录