Home > Article > Backend Development > Understand Go flat project structure
Instead of spending time trying to figure out how to break your code into packages, a flat-structured application will have it all .go
files are placed in a package.
myapp/ main.go server.go user.go lesson.go course.go
When entering Understand Go flat project structure, almost everyone starts with a flat application structure. Every program in the Understand Go flat project structure tour, most exercises in Understand Go flat project structurephercises, and many other early Understand Go flat project structure programs are not broken into any packages. Instead, we just create a few .go
files and put all the code into the same (usually main
) package.
At first, this sounds bad. Will the code become unwieldy quickly? How to separate business logic from UI rendering code? How do I find the correct source file? After all, a big part of the reason we use packages is to separate concerns while making it easier to quickly navigate to the correct source files.
Related learning recommendations: Understand Go flat project structure language tutorial
When using flat structures, you should still try Follow coding best practices. You will need to use different .go
files to separate different parts of your application:
myapp / main.go#阅读配置并在此处启动您的应用 server.go#总体HTTP处理逻辑在这里 user_handler.go#用户http处理程序逻辑在这里 user_store.go#用户数据库逻辑在这里 # 等等...
Global variables can still be a problem, so you should consider using types with methods so that they Out of code:
type Server struct { apiClient *someapi.Client router *some.Router } func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.router.ServeHTTP(w, r) }
And your main()
function should probably still remove most of the logic outside of the setup application:
//警告:此示例非常人为设计,甚至可能无法编译。 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) }
Actually, you actually It's possible to use essentially flat-structured code all in one package and define commands in a separate main
package. This will allow you to use the common cmd
subdirectory pattern:
myapp/ cmd/ web/ # package main main.go cli/ # package main main.go # package myapp server.go user_handler.go user_store.go ...
In this example, your application is still basically flat, but you pulled out main
Packages are there because you have a need - for example you might need to support two commands using the same core application.
The main benefit of a flat structure is not keeping all your code in one directory, or something stupid like that. The core benefit of this structure is that you can stop worrying about how to organize things and get on with solving the problem you intend to solve with your application.
I absolutely love how this application structure reminds me of my PHP days. When I first learned to code, I started using random PHP files with their logic mixed in with all kinds of HTML, and it was a real mess. I'm not suggesting we build it like a large application - that would be terrible - but I'm less worried about where everything should be and more focused on learning how to code and solve my specific problem. Whether you're trying to understand the needs of your application, your domain, or how to code in general, using a flat structure makes it easier for you to focus on learning and building.
This is true because we can stop worrying about questions like "Where should this logic go?" Because if we make a mistake, it's easy to fix. If it's a function, we can move it to any new source file in the package. If it's the wrong type of method, we can create two new types and separate the logic from the original type. With these, we don't have to worry about running into weird cyclic dependency issues since we only have one package.
Another important reason to consider a flat structure is that as the complexity of the application increases, the structure becomes much easier to evolve. When it's obvious that you could benefit from splitting your code into a separate package, all you usually need to do is move some source files into a subdirectory, change its package, and update any references to use the new program package prefix. For example, if we had SqlUser
and decided to benefit from handling all database-related logic in a separate sql
package, we would update all references to now use sql. User
after moving types to new package. I find that structures like MVC are more challenging to refactor, although not as difficult or difficult as other programming languages.
Flat structures are especially useful for beginners who are often too quick to create packages. I can't really say why this happens, but newbies to Understand Go flat project structure like to create tons of packages, which almost always leads to stuttering (user.User
), cyclical dependencies, or some other problem.
In the next article about MVC, we will explore the ways in which this phenomenon of creating too many packages can make MVC appear impossible in Understand Go flat project structure, even though it is not.
通过推迟创建新程序包的决定,直到我们的应用程序增长一点并更好地了解它,发芽的Understand Go flat project structurephers犯此错误的可能性就大大降低了。
这也是为什么很多人会鼓励开发人员避免过早将其代码分解到微服务中的原因-您通常没有足够的知识来真正知道应该和不应该将哪些内容分解为微服务以及抢先式微服务( I kinda希望能成为一句俗语)只会在将来带来更多工作。
假装使用扁平结构没有任何不利之处,这对我来说是不诚实的,所以我们也应该讨论这些。
对于初学者来说,扁平的结构只能使您受益匪浅。它会工作一段时间(可能比您想象的更长),但是到某个时候,您的应用程序将变得足够复杂,您需要开始分解它。使用平面结构的好处是您可以推迟使用它,并且在分解时可能会更好地理解您的代码。缺点是,您将需要花一些时间进行重构,并且您可能(也许-但这很麻烦)发现自己已经重构为您想从任何地方开始的结构。
使用平面结构时,命名冲突有时也会很尴尬。例如,假设您想要在应用程序中使用Course
类型,但是在数据库中表示课程的方式与在JSON中呈现课程的方式不同。一个快速的解决方案是创建两种类型,但是由于它们都在同一个包中,因此每种类型都需要使用不同的名称,并且可能最终以类似以下内容的形式出现:SqlCourse
和JsonCourse
。这确实没什么大不了的,但是有点令人遗憾的是我们最终得到了零类型,简单地称为Course
。
将代码重构为新程序包也不总是那么简单。是的,这通常很容易,但是由于所有代码都在一个包中,因此您有时可能会遇到天生具有周期性的代码。例如,假设我们的课程是否具有在JSON响应中始终以crs_
开头的ID,并且我们想以各种货币返回价格。我们可以创建一个JsonCourse
来处理:
输入JsonCourse struct { ID字符串`json:“ id”` 价格结构{ USD字符串`json:“ usd”` }`json:“价格”` }
同时,SqlCourse
仅需要存储一个整数ID和一个以美分为单位的单一价格,我们可以使用各种货币对其进行格式化。
type SqlCourse struct { ID int Price int }
现在我们需要一种将SqlCourse
转换为JsonCourse
的方法,因此我们可以将其作为SqlCourse
类型的方法:
func (sc SqlCourse) ToJson() (JsonCourse, error) { jsonCourse := JsonCourse{ ID: fmt.Sprintf("crs_%v", sc.ID), } jsonCourse.Price.USD = Price: fmt.Sprintf("%d.%2d", sc.Price/100, sc.Price%100) return jsonCourse, nil }
然后稍后我们可能需要一种方法来解析传入的JSON并将其转换为SQL等效项,因此我们将其添加到JsonCourse
类型中作为另一种方法:
func (jc JsonCourse) ToSql() (SqlCourse, error) { var sqlCourse SqlCourse // JSON ID is "crs_123" and we convert to "123" // for SQL IDs id, err := strconv.Atoi(strings.TrimPrefix(jc.ID, "crs_")) if err != nil { // Note: %w is a Understand Go flat project structure 1.13 thing that I haven't really // tested out, so let me know if I'm using it wrong
相关学习推荐:编程视频
The above is the detailed content of Understand Go flat project structure. For more information, please follow other related articles on the PHP Chinese website!