Repozitoriy ildizidagi yagona go.mod ko'pchilik loyihalar uchun eng to'g'ri va yetarli yechim hisoblanadi. Ammo ertami-kechmi siz kutubxona va undan foydalanuvchi ilovani parallel ravishda bir vaqtning o'zida tahrirlayotganingizda yoki umumiy ichki paketlardan foydalanuvchi bir nechta xizmat ustida ishlayotganingizda, bitta modul doirasidan chiqib ketasiz. Go 1.18 versiyasida aynan shu holatlar uchun ish maydonlari (workspaces) — ya'ni go.work fayli va go work buyruqlari taqdim etildi. Bu ilgari dasturchilar majbur bo'lib ishlatgan replace direktivalariga nisbatan ancha qulay va yengil muqobil hisoblanadi.
Ish maydonlari paydo bo'lishidan oldin bir modulni boshqa modulning lokal, hali nashr qilinmagan nusxasidan foydalanishga yo'naltirish uchun go.mod fayliga replace direktivasini qo'shish kerak bo'lardi:
replace github.com/user/mylib => ../mylib
Bu usul ishlaydi, ammo u Git'ga commit qilinadigan go.mod faylini o'zgartirib qo'yadi. Natijada boshqa jamoa a'zolari ham aynan o'sha lokal fayl yo'llariga ega bo'lishi shart bo'lib qoladi yoki dasturchi har safar lokal sinovdan so'ng replace qatorini qo'lda o'chirib tashlashni esdan chiqarmasligi kerak bo'ladi.
Ish maydonlari (workspaces) bu muammoni oqlona hal qiladi: bu odatda Git'ga qo'shilmaydigan (
.gitignoreqilinadigan) alohidago.workfayli bo'lib, u Go kompilyatoriga va asboblariga quyidagicha yo'l-yo'riq beradi:go.modfayllarini o'zgartirmasdan, paketlarni chaqirishda avvalo ko'rsatilgan lokal papkalardan foydalan.
go work init
minimal go.work faylini yaratadi:
go 1.22
Modullarni unga go work use buyrug'i orqali bittalab qo'shishingiz yoki faylni to'g'ridan-to'g'ri tahrirlashingiz mumkin:
go work use ./api
go work use ./shared
go 1.22
use (
./api
./shared
)
Shundan keyin ish maydoni ichidan ishga tushirilgan har qanday build ./api va ./shared importlarini avval o'sha ko'rsatilgan lokal papkalardan qidiradi, qolgan hamma tashqi paketlar uchun esa odatdagidek modullar keshiga murojaat qiladi. Aniqroq aytganda: agar api/go.mod fayli github.com/user/project/shared modulini talab qilsa va shared ish maydonida ro'yxatga olingan bo'lsa, Go api/go.mod da yozilgan versiya o'rniga sizning kompyuteringizdagi, hali hatto commit ham qilinmagan eng so'nggi shared papkasidagi koddan foydalanadi. shared ichidagi biror funksiyani o'zgartirishingiz bilan, api ning navbatdagi build jarayoni ushbu o'zgarishni darhol aks ettiradi: hech qanday versiya ko'tarish, repozitoriyga push qilish yoki keyinchalik o'chirish kerak bo'lgan replace direktivalari talab qilinmaydi.
myproject/
├── go.work
├── api/
│ ├── go.mod
│ └── server.go
├── client/
│ ├── go.mod
│ └── client.go
└── shared/
├── go.mod
└── utils.go
go work sync buyrug'i ish maydoni bo'ylab haqiqatan ishlatilgan versiyalarni har bir a'zo modulning go.mod fayliga qaytarib sinxronlashtiradi va lokal ishni yakunlab, o'zgarishlarni commit qilishga tayyorlanayotganingizda ularning o'zaro mosligini ta'minlaydi. Shuningdek, go.work faylining o'zida ham replace direktivalarini belgilash mumkin bo'lib, ular barcha ichki modullarning qoidalaridan ustun turadi — bu butun ish maydoni bo'yicha biror tashqi paketni almashtirish zarur bo'lganda juda qulay.
Ish maydonlari o'zining eng katta afzalligini monorepolarda — bir nechta mustaqil modullarni birlashtirgan yagona katta repozitoriyda namoyon qiladi:
monorepo/
├── go.work
├── services/
│ ├── auth/
│ │ └── go.mod
│ └── api/
│ └── go.mod
└── libraries/
├── logger/
│ └── go.mod
└── database/
└── go.mod
go 1.22
use (
./services/auth
./services/api
./libraries/logger
./libraries/database
)
Ushbu sozlama bilan services/api xizmati libraries/logger paketini xuddi tashqi kutubxona kabi import qila oladi, Go esa uni avtomatik ravishda lokal ish maydonidan olib ulaydi. Repozitoriy ildizidan ishga tushirilgan go build ./... va go test ./... buyruqlari ish maydonidagi barcha modullar bo'ylab birvarakayiga ishlaydi.
Monoreponing eng asosiy maqsadi ham shunda: bitta buyruq butun tizimni tekshiradi, modullararo refaktoring o'zgarishlari esa keyinchalik ishlab chiqarishda (production) versiyalar nomutanosibligi sifatida emas, balki darhol build xatoligi ko'rinishida yuzaga chiqadi.
Xuddi shu mexanizm ishlab chiqish paytida diskda yonma-yon joylashgan alohida repozitoriylar uchun ham bemalol ishlaydi:
git clone https://github.com/user/app
git clone https://github.com/user/lib
cd app
go work init
go work use . ../lib
Endi app loyihasi ustida ishlayotganingizda u lib modulini bevosita lokal papkadan oladi. Ish maydoni fayli esa faqat sizning kompyuteringizda turadi va repozitoriyga commit qilinmagani sababli boshqa jamoa a'zolarining ishiga zarracha xalal bermaydi.
Ikkala vosita ham modullarni qayta yo'naltirish vazifasini bajaradi, ammo ularning qo'llanish sohasi turlicha:
go.mod dagi replace direktivasidan foydalaning — masalan, butun jamoa va CI/CD tizimi uchun fork qilingan repozitoriyga mahkamlash yoki doimiy lokal bog'liqlikni ko'rsatish kerak bo'lganda.go.work) foydalaning — bu boshqalarning kodiga ta'sir qilmaydi va Git tarixiga kirmaydi.Odatda,
go.workfayli.gitignorega qo'shiladi — faqat jamoangiz monorepoda yagona umumiy ish maydoni faylini birgalikda saqlashga qat'iy qaror qilgan holatdagina uni commit qiling.
Bu ikki usul bir-biriga xalal bermaydi: go.work fayli o'z ichida replace qoidalariga ega modullarni ham bemalol birlashtira oladi, go work sync esa yakunda barchasini tartibga soladi.
Monorepolar bitta modulli loyihalarda uchramaydigan savolni yuzaga keltiradi: har bir modul o'z mustaqil versiyasiga ega bo'ladimi yoki butun repozitoriy yagona umumiy versiya bilan belgilanadimi?
services/user-service uchun v1.2.3, libraries/auth uchun v2.1.0) — ancha aniq va moslashuvchan, ammo Git teglari aynan qaysi modulga tegishli ekanligini qat'iy nazorat qilishni talab etadi.Har ikkala yondashuv ham amaliyotda qo'llaniladi — muhimi, rejasiz aralash holatga tushib qolmaslik uchun jamoada ulardan birini ongli ravishda tanlab olish lozim.
Ko'p modulli loyihalarda CI/CD tizimi odatda repozitoriy ildizidan butun loyihani yig'adi va testlaydi (go build ./..., go test ./...). Go 1.25 dan boshlab maxsus work paketi patterni joriy qilingan: go build work yoki go test work buyrug'i bevosita "go.work dagi har bir moduldagi barcha paketlar" degan aniq ma'noni beradi va noaniqliklarni yo'qotadi.
Repozitoriy juda kattalashib ketganda, har bir commit'da hamma narsani qayta-qayta testlash vaqt oladi. Bunday holatlarda faqat o'zgargan fayllarga tegishli modullarni aniqlab, testlash maqsadga muvofiq:
git diff --name-only origin/main... | xargs -I{} dirname {} | sort -u
Shu tariqa qaysi papkalar o'zgarganini aniqlab, faqat o'sha modullarni qayta yig'ish va testlash mumkin. go vet ./... va golangci-lint run ./... kabi linterlar ham xuddi shunday integratsiya qilinadi.
Loyiha boshida har doim bitta moduldan boshlang. Alohida mustaqil versiyalanishi kerak bo'lgan kutubxona yoki alohida deploy qilinadigan mustaqil servislar kabi jiddiy ehtiyoj tug'ilmaguncha, bir nechta modulli strukturaga shoshilmang.
Ajratishga qaror qilganingizda esa modul import yo'llarini aniq va tartibli belgilang (masalan, github.com/company/monorepo/services/user-service), umumiy kodlarni xizmatlar orasida nusxalash o'rniga alohida umumiy kutubxona moduliga chiqaring hamda testlaringiz barcha modullarning birgalikdagi o'zaro uyg'unligini sinovdan o'tkazayotganiga ishonch hosil qiling.
Monoreponing eng kuchli jihati — modullararo ziddiyatlarni kompilyatsiya bosqichidayoq aniqlash — faqatgina CI tizimi butun loyihani yaxlit holda build qilib, testdan o'tkazgandagina to'laqonli samara beradi.
Shu tariqa biz Go loyihalarini qanday tashkil qilish, formatlash, tekshirish hamda modullar va bog'liqliklarni qanday boshqarishni to'liq ko'rib chiqdik. Bu Go asboblari va infratuzilmasi haqidagi qism edi. Endi esa bevosita dasturlash tili asoslariga sho'ng'iymiz: 9-bob Goning oldindan e'lon qilingan primitiv tiplari — har qanday murakkab dasturning asosiy g'ishtchalari haqida so'z yuritadi.