Tek bir dosyayla başlayın
Bir Go programı tamamen tek bir main.go içinde yaşayabilir ve küçük araçlar öyle yaşamalıdır. Bu eksiksiz program girdiyi ayrıştırır, işini yapar ve bir rapor yazdırır; hepsi tek bir pakette:
Büyüdüğünde onu aynı dizinde ve aynı pakette daha fazla dosyaya bölün (parse.go, report.go, main.go). Bir paketteki dosyalar her tanımlayıcıyı paylaşır, bu yüzden hiçbir şeyin dışa açılması ya da import edilmesi gerekmez. Birkaç bin satırlık bir projenin ihtiyaç duyduğu yapı çoğu zaman yalnızca budur.
Çok dosyalı bir package main'in bir paket olarak çalıştırılması gerektiğini unutmayın: go run main.go değil, go run .. Aksi halde dosyalar tek başına derlenir ve diğer dosyalarda tanımlanan adlar için undefined hataları alırsınız.
Zorunlu bir düzen yok
Go, "dizin başına bir paket" dışında hiçbir dizin yapısı gerektirmez. Resmi rehber go.dev'deki Organizing a Go module sayfasıdır ve kurallar değil, birkaç biçim anlatır.
GitHub deposu golang-standards/project-layout çok kopyalanır ve çoğu zaman bir standart sanılır. Büyük projelerden gelen geleneklerin bir topluluk derlemesidir ve o dönem Go'nun teknik lideri olan Russ Cox orada bunun bir Go standardı olmadığını belirten bir issue açmıştır. Dizinlerinin çoğu (pkg/, api/, build/, deployments/) yalnızca büyük kod tabanlarında anlamlıdır. Tüm iskeleti yeni bir projeye kopyalamak hiçbir fayda sağlamadan boş klasörler ve derin import yolları yaratır.
Aracın zorladığı kurallar kısadır:
- Bir dizin bir pakettir. İçindeki tüm
.godosyaları (_testsonekini kullanan_test.godosyaları hariç) bir paket adını paylaşır. - Import yolu modül yolu artı dizin yoludur.
internaladlı bir dizin, içindekini kimin import edebileceğini kısıtlar.testdataadlı dizinler ve.ya da_ile başlayanlar go aracı tarafından yok sayılır.
Yaygın biçimler
Tek bir komut
todo/
├── go.mod module github.com/you/todo
├── main.go
├── parse.go
├── report.go
└── parse_test.go
Düz, her şey package main içinde. go install github.com/you/todo@latest çalışır ve binary'nin adı todo olur.
Bir kütüphane
slug/
├── go.mod module github.com/you/slug
├── slug.go package slug
├── slug_test.go
├── example_test.go
└── internal/
└── table/ helpers the public package uses, hidden from users
Paket modül kökünde durur, bu yüzden kullanıcılar github.com/you/slug'ı import eder ve slug.Make(...)'i çağırır. Genel API olarak desteklemek istemediğiniz her şey internal/ altına gider.
Bir servis ya da birkaç binary
shop/
├── go.mod module github.com/you/shop
├── cmd/
│ ├── shop-api/
│ │ └── main.go package main: flags, config, wiring
│ └── shop-worker/
│ └── main.go
├── internal/
│ ├── order/ package order: domain types and logic
│ │ ├── order.go
│ │ └── order_test.go
│ ├── store/ package store: database access
│ └── httpapi/ package httpapi: handlers, routing
├── migrations/
└── README.md
cmd/<name>/main.go her binary için bir dizin tutar ve dizin adı binary adı olur (go build ./cmd/shop-api, shop-api üretir). Her main ince kalır: yapılandırmayı okur, bağımlılıkları oluşturur, sunucuyu başlatır. Asıl kod internal/ altındaki paketlerde yaşar; orada iki binary de onu kullanabilir, başka hiçbir modül kullanamaz.
Çoğu Go servisinin ulaştığı düzen budur. Buna ilk gün değil, ikinci bir binary'niz ya da paketleri bölmek için gerçek bir nedeniniz olduğunda başvurun.
internal/ go komutu tarafından zorlanır
internal/ altındaki bir paket yalnızca internal'ın ebeveyninde kök salan ağaçtaki kod tarafından import edilebilir. Başka bir modülden derleme başarısız olur:
package example.com/b
main.go:6:2: use of internal package example.com/a/internal/secret not allowed
Bu, internal/'ı API yüzeyinizi küçük tutmanın aracı yapar. Oradaki kod serbestçe değişebilir, çünkü her çağıranın kendi deponuzda olduğunu bilirsiniz. Bir uygulama için neredeyse her şeyi internal/'a koymak makuldür. Bir kütüphane için ise kararlı tutmaya söz verdiğinizi vermediğinizden ayırır.
internal her derinlikte çalışır: shop/internal/order tüm shop/ için görünürken, shop/internal/order/internal/pricing yalnızca shop/internal/order/ içinde görünür.
Paketleri adlandırmak
Paket adı her çağrı noktasının bir parçasıdır, bu yüzden dizin ağacından daha önemlidir.
- Kısa, küçük harfli, tek kelime:
order,store,httpapi. Alt çizgi yok, mixedCaps yok. - Onu içerdiğiyle değil, sağladığıyla adlandırın.
util,common,helpers,miscvemodelshiçbir şey söylemez ve çöplüğe dönüşür; Go ekibinin stil rehberi bunları açıkça önermez. Bir yardımcıyı onu kullanan pakete ya da amacıyla adlandırılmış bir pakete (slug,retry) koyun. - Tekrardan (stutter) kaçının. Çağıranlar
order.Orderveorder.Newyazar, bu yüzden şeylereorder.OrderServiceya daorder.NewOrderadını vermeyin. Standart kütüphane bunu tutarlı biçimde yapar:http.HTTPServerdeğil,http.Server. - Dizin adı ile paket adı eşleşmelidir, komut dizinlerindeki
package maindışında. Farklı olduklarında okuyanlar import'un hangi tanımlayıcıyı getirdiğini öğrenmek için bir dosya açmak zorunda kalır.
Paketleri türe göre değil sorumluluğa göre bölün. Bir models/, controllers/, services/ bölmesi (başka ekosistemlerde yaygın) her özelliği üç pakete dokunmaya zorlar ve Go'nun yasakladığı import döngüleri yaratma eğilimindedir. Bir alan kavramı etrafında düzenlenen paketler (order, payment, user) her biri kendi tiplerini ve mantığını bir arada tutar.
Testler nereye gider
Testler kodun yanında, aynı dizinde, *_test.go adlı dosyalarda yaşar. tests/ ağacı yoktur.
order_test.goiçindepackage order: dışa kapalı tanımlayıcıları kullanabilen bir iç test.- Aynı dizinde
package order_test: bir çağıran gibi yalnızca dışa açık API'yi gören bir dış test. Örnekler ve testlerde import döngülerinden kaçınmak için kullanışlıdır. - Fixture dosyaları testlerin yanındaki bir
testdata/dizinine gider. Go aracı onu yok sayar ve testler çalışma dizini olarak paket diziniyle çalışır, bu yüzdentestdata/big.jsongibi göreli yollar çalışır.
Kökteki diğer dosyalar
| Dosya ya da dizin | Amaç |
|---|---|
go.mod, go.sum | modül tanımı ve bağımlılık checksum'ları, her zaman kökte |
README.md, LICENSE | her projede olduğu gibi |
Makefile ya da Taskfile.yml | isteğe bağlı build kısayolları |
Dockerfile | servisler için genellikle kökte |
migrations/, web/, docs/ | tuttuklarıyla adlandırılmış Go dışı varlıklar |
tools.go | araç bağımlılıklarını sabitlemenin eski yolu; Go 1.24 onun yerine go.mod içinde tool satırlarını getirir (go get -tool) |
go.work | birkaç modülü yerel olarak birlikte geliştirmek için bir workspace; tek modüllü projelerde genellikle commit edilmez |
Sık yapılan hatalar
- Küçük bir proje için büyük bir şablon kopyalamak. Düz başlayın ve ikinci bir binary ya da gerçek bir sınır ortaya çıktığında dizin ekleyin.
utilsya dacommonadlı paketler. Paketleri yaptıkları işle adlandırın.- Dosya ya da tip başına bir paket. Go paketleri Java sınıflarından daha büyük birimler olarak tasarlanmıştır. On dosyalı bir paket normaldir.
- Import döngüleri. İki paket birbirini import edemez. Bu genellikle birbirlerine ait olduklarını, paylaşılan bir tipin daha alt seviyeli bir pakete taşınması gerektiğini ya da bir tarafın diğer paket yerine küçük bir interface'e bağımlı olması gerektiğini gösterir.
- Refleks olarak
pkg/. Anlam eklemeden her import yoluna bir parça ekler. - Ayrı bir dizinde testler. Orada dışa kapalı kodu göremezler ve araçlar onları orada beklemez.
Sıkça Sorulan Sorular
Resmi bir Go proje düzeni var mı?
Zorunlu bir tane yok. Go ekibi go.dev/doc/modules/layout adresinde birkaç yaygın biçimi (tek bir paket, bir komut, internal/ ile birkaç komut) anlatan bir rehber yayımlar. Popüler golang-standards/project-layout deposu bir Go standardı değil, bir topluluk projesidir ve Go ekibi bunu açıkça söylemiştir.
Go'da internal dizini nedir?
Import yolu bir internal elemanı içeren bir paket yalnızca o internal dizininin ebeveyninde kök salan kod tarafından import edilebilir. example.com/app/internal/store, example.com/app/ altındaki her yerden import edilebilir ve go komutu diğer herhangi bir modülden gelen import'ları use of internal package ... not allowed ile reddeder.
Go'da pkg dizini kullanmalı mıyım?
İsteğe bağlıdır ve anlam eklemeden bir yol parçası ekler. Bazı büyük projeler genel kütüphane kodunu geri kalanından ayırmak için pkg/ kullanır, ama Go standart kütüphanesi ve çoğu modern proje kullanmaz. Import edilebilir paketleri modül köküne ya da adlandırılmış dizinlere, özel olan her şeyi internal/'a koyun.
Bir Go projesinde test dosyaları nereye konur?
Test ettikleri kodla aynı dizine, *_test.go adıyla. Go'da ayrı bir test ağacı yoktur. Fixture dosyaları testlerin yanındaki bir testdata dizinine gider; go aracı paket ararken onu yok sayar.