站长资讯网
最全最丰富的资讯网站

了解Go 扁平化项目结构

了解Go 扁平化项目结构

了解Go 扁平化项目结构

无需花时间试图弄清楚如何将代码分解为软件包,而是采用扁平结构的应用程序会将所有.go文件放置在一个软件包中。

myapp/   main.go   server.go   user.go   lesson.go   course.go

进入Go时,几乎每个人都从一个平面应用程序结构开始。 Go tour中的每个程序,Gophercises中的大多数练习以及许多其他早期的Go程序都没有被分解成任何包装。取而代之的是,我们只创建几个.go文件,然后将所有代码放入相同的(通常是main)包中。

起初,这听起来很糟糕。代码会很快变得笨拙吗?如何将业务逻辑与UI渲染代码分开?我如何找到正确的源文件?毕竟,我们使用软件包的很大一部分原因是要分离关注点,同时使更容易快速地导航到正确的源文件。

相关学习推荐:Go语言教程

有效使用平面结构

使用平面结构时,您仍应尝试遵守编码最佳实践。您将需要使用不同的.go文件分隔应用程序的不同部分:

myapp /   main.go#阅读配置并在此处启动您的应用   server.go#总体HTTP处理逻辑在这里   user_handler.go#用户http处理程序逻辑在这里   user_store.go#用户数据库逻辑在这里   # 等等...

全局变量仍然可能成为问题,因此您应考虑将类型与方法配合使用,以使它们脱离代码:

type Server struct {   apiClient *someapi.Client   router *some.Router }  func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {   s.router.ServeHTTP(w, r) }

而且您的main()函数可能仍应在设置应用程序之外删除大多数逻辑:

//警告:此示例非常人为设计,甚至可能无法编译。  type Config struct {   SomeAPIKey     string   Port           string   EnableTheThing bool }  func main() {   var config Config   config.SomeAPIKey = os.Getenv("SOMEAPI_KEY")   config.Port = os.Getenv("MYAPP_PORT")   if config.Port == "" {     config.Port = "3000"   }   config.EnableTheThing = true    if err := run(config); err != nil {     log.Fatal(err)   } }  func run(config Config) error {   server := myapp.Server{     APIClient: someapi.NewClient(config.SomeAPIKey),   }   return http.ListenAndServe(":" + config.Port, server) }

实际上,您实际上可以将基本上是平面结构的代码全部使用在一个软件包中,并在单独的main软件包中定义命令。这将允许您使用常见的cmd子目录模式:

myapp/   cmd/     web/       # package main       main.go     cli/       # package main       main.go   # package myapp   server.go   user_handler.go   user_store.go   ...

在此示例中,您的应用程序基本上仍然是平坦的,但是您拔出了main软件包是因为您有需要-例如可能需要使用同一核心应用程序来支持两个命令。

为什么要使用扁平结构?

扁平结构的主要好处不是将所有代码都保存在一个目录中,也不是那样愚蠢的东西。这种结构的核心好处是您可以不必担心如何组织事物,而可以继续解决您打算通过应用程序解决的问题。

我绝对喜欢这个应用程序结构让我回想起PHP的日子。当我第一次学习编码时,我开始使用随机PHP文件,其逻辑与各种HTML混合在一起,这真是一团糟。我并不是在建议我们以大型应用程序的方式构建-那样会很糟糕-但是我并不担心一切都应该放在哪里,而是更加关注学习如何编写代码和解决我的特定问题。无论您是要了解应用程序的需求,您的域还是一般的编码方式,使用扁平结构都可以使您更轻松地专注于学习和构建。

这是正确的,因为我们可以不再担心诸如“这种逻辑应该去哪里?”之类的问题。因为如果我们犯了一个错误,很容易解决。如果它是一个函数,我们可以将其移动到包中的任何新源文件中。如果它是错误类型的方法,我们可以创建两个新类型并将逻辑与原始类型分开。有了这些,我们就不必担心会遇到奇怪的周期性依赖问题,因为我们只有一个软件包。

考虑平面结构的另一个重要原因是,随着应用程序复杂性的提高,结构的发展变得容易得多。当您明显可以从将代码拆分到一个单独的程序包中受益时,您通常需要做的就是将一些源文件移到一个子目录中,更改其程序包,并更新任何引用以使用新的程序包前缀。例如,如果我们有SqlUser并决定从一个单独的sql包中处理所有与数据库相关的逻辑中受益,我们将更新所有引用以现在使用sql.User将类型移动到新软件包后。我发现,像MVC这样的结构在重构方面更具挑战性,尽管并非没有其他编程语言那样困难或困难。

扁平结构对于通常太快无法创建包的初学者特别有用。我真的不能说为什么会发生这种现象,但是Go的新手喜欢创建大量的软件包,这几乎总是导致口吃(user.User),周期性依赖关系或其他一些问题。

在下一篇有关MVC的文章中,我们将探讨这种创建过多包的现象如何使MVC在Go中显得不可能的方法,尽管事实并非如此。

通过推迟创建新程序包的决定,直到我们的应用程序增长一点并更好地了解它,发芽的Gophers犯此错误的可能性就大大降低了。

这也是为什么很多人会鼓励开发人员避免过早将其代码分解到微服务中的原因-您通常没有足够的知识来真正知道应该和不应该将哪些内容分解为微服务以及抢先式微服务( I kinda希望能成为一句俗语)只会在将来带来

赞(0)
分享到: 更多 (0)