08 / 73

Ish maydonlari va ko'p modulli ishlab chiqish

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 maydoni aslida nimani hal qiladi

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:

matn
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 (.gitignore qilinadigan) alohida go.work fayli bo'lib, u Go kompilyatoriga va asboblariga quyidagicha yo'l-yo'riq beradi: go.mod fayllarini o'zgartirmasdan, paketlarni chaqirishda avvalo ko'rsatilgan lokal papkalardan foydalan.

Ish maydonini sozlash

bash
go work init

minimal go.work faylini yaratadi:

matn
go 1.22

Modullarni unga go work use buyrug'i orqali bittalab qo'shishingiz yoki faylni to'g'ridan-to'g'ri tahrirlashingiz mumkin:

bash
go work use ./api
go work use ./shared
matn
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.

papkalar
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.

Monorepolar va ko'p repozitoriyli ishlab chiqish

Ish maydonlari o'zining eng katta afzalligini monorepolarda — bir nechta mustaqil modullarni birlashtirgan yagona katta repozitoriyda namoyon qiladi:

papkalar
monorepo/
├── go.work
├── services/
│   ├── auth/
│   │   └── go.mod
│   └── api/
│       └── go.mod
└── libraries/
    ├── logger/
    │   └── go.mod
    └── database/
        └── go.mod
matn
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:

bash
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.

replace yoki ish maydoni: qaysi birini tanlash

Ikkala vosita ham modullarni qayta yo'naltirish vazifasini bajaradi, ammo ularning qo'llanish sohasi turlicha:

  • Almashtirish butun jamoa va loyiha bilan birga yashashi shart bo'lganda 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.
  • Almashtirish faqat sizning shaxsiy, vaqtinchalik ishlab chiqish muhitingizga tegishli bo'lsa, ish maydonidan (go.work) foydalaning — bu boshqalarning kodiga ta'sir qilmaydi va Git tarixiga kirmaydi.

Odatda, go.work fayli .gitignore ga 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.

Bir nechta modul bo'ylab versiyalash va CI

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?

  • Mustaqil versiyalash (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.
  • Yagona versiyalash (butun repozitoriyga yagona teg qo'yiladi) — boshqarish uchun ancha sodda, ammo modullardan birortasi o'zgarmagan taqdirda ham uning versiyasini majburan oshirishga olib keladi.

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:

bash
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.

Tavsiya etiladigan amaliyotlar

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.