# Bazel 16: Loyiha #5: Monorepo — iOS ilova + macOS CLI + umumiy kutubxona, visibility, query va "faqat ta'sirlanganini test qil"

> **Holat:** 2026-yil oktabr. Ushbu darsda biz zamonaviy IT kompaniyalarining (Google, Meta, Uber, Twitter) eng sevimli quroli — **Apple Monorepo** tizimini quramiz. Bitta Git omborida (repository) iOS mobil ilovasi, macOS terminal buyruq vositasi (CLI) hamda ularga umumiy bo'lgan `SharedKit` biznes mantig'i yashaydi. Darsda atomik o'zgarishlar (atomic commits), platformalararo kod ulashish va eng asosiysi — monorepoda faqat ta'sirlangan platformani test qilish (Selective Testing) amaliyotini o'rganamiz. Kod misollari Bazel 9.3.0, rules_apple 5.3.0, rules_swift 4.2.1 va Xcode 27.1'da tekshirilgan.

> **Bu darsdan keyin siz:** bir nechta platforma (iOS, macOS, watchOS, Server-Side Swift) kodlarini alohida git repolarga sochib yubormasdan, bitta monorepoda tartibli saqlay olasiz, umumiy kutubxona o'zgarganda barcha platformalarni bitta buyruq bilan tekshirib, xavfsiz atomik refaktoring qilasiz.

- Multi-repo (har bir modul alohida git) ning versiyalash azoblari va Monorepo'ning yechimlarini tushunasiz.
- Bitta repoda `ios_application` va `swift_binary` (macOS) nishonlarini birgalikda sozlaysiz.
- `SharedKit` kutubxonasini har ikkala platforma (iOS va macOS) uchun universal kompilyatsiya qilasiz.
- **Atomik commit kuchi:** umumiy model o'zgarganda, iOS va macOS kodini bitta git commit orqali sinxron yangilaysiz (versiya kutish yo'q!).
- Monorepo'da selective testing'ni ishga tushirasiz: faqat iOS o'zgarsa, CLI umuman test qilinmaydi va aksincha.
- `bazel query` yordamida monorepo miqyosida o'zgarishlar xavfini tahlil qilasiz.

## Mundarija

1. [Multi-repo azobi vs Monorepo qulayligi](#multi-vs-mono)
2. [Monorepo papkalar va targetlar xaritasi](#tuzilish)
3. [SharedKit: Platformadan mustaqil umumiy mantiq](#shared-kit)
4. [macOS CLI nishoni: Admin vositasi](#cli-target)
5. [iOS ilova nishoni: Mobil interfeys](#ios-target)
6. [Atomik refaktoring: Ikkala platformani bir vaqtda yangilash](#atomic-refactor)
7. [Monorepo'da Selective Testing: Vaqtni tejash san'ati](#monorepo-testing)
8. [Monorepo uchun GitHub Actions pipeline](#monorepo-ci)
9. [Amaliy mashqlar](#mashqlar)
10. [O'zingizni tekshiring](#checklist)
11. [Xulosa](#xulosa)
12. [Manbalar](#manbalar)

---

## 1. Multi-repo azobi vs Monorepo qulayligi {#multi-vs-mono}

Dasturchilar ko'pincha loyiha kattalashganda kodni alohida git omborlariga bo'lib tashlashadi:
- `SharedModels` (alohida git repo)
- `iOS-App` (alohida git repo)
- `macOS-Admin-Tool` (alohida git repo)

Bu yo'l dastlab chiroyli ko'rinsa ham, 3 oydan keyin **"Dependency Hell"** (qaramliklar do'zaxi) boshlanadi:
1. `SharedModels`da bitta modelga yangi maydon qo'shasiz.
2. Unga git tag qo'yib (`v1.4.2`), push qilasiz.
3. So'ng `iOS-App` repoga o'tib, `Package.resolved`ni yangilaysiz va PR ochasiz.
4. Keyin `macOS-Admin-Tool` repoga o'tib, yana xuddi shu ishni qilasiz.
5. Agar birortasida xato chiqsa, yana 1-qadamga qaytasiz!

**Bazel Monorepo yechimi:**
Barcha loyihalar bitta omborda yashaydi. Siz `SharedKit`dagi modelni o'zgartirasiz va shu zahotiyoq iOS va macOS'dagi kodni to'g'rilab, **bitta git commit** bilan merge qilasiz. Versiya tag'lariga yoki kutishlarga mutlaqo hojat qolmaydi!

![Loyiha #5: Apple Monorepo Arxitekturasi: iOS, macOS CLI va SharedKit](/lessons/images/bazel/bazel_16_monorepo_layout.svg)

---

## 2. Monorepo papkalar va targetlar xaritasi {#tuzilish}

```text
apple-monorepo/
├── MODULE.bazel
├── .bazelrc
├── Shared/
│   ├── BUILD.bazel
│   ├── Sources/
│   │   ├── User.swift
│   │   └── SecurityValidator.swift
│   └── Tests/
│       └── SharedTests.swift
├── Tools/
│   └── AdminCLI/
│       ├── BUILD.bazel
│       └── main.swift
└── Apps/
    └── iOS/
        ├── BUILD.bazel
        ├── Info.plist
        └── Sources/
            └── MobileApp.swift
```

`MODULE.bazel`:

```python
# MODULE.bazel
module(
    name = "apple_monorepo",
    version = "1.0.0",
)

bazel_dep(name = "rules_apple", version = "5.3.0")
bazel_dep(name = "rules_swift", version = "4.2.1")
bazel_dep(name = "apple_support", version = "2.10.1")
```

---

## 3. SharedKit: Platformadan mustaqil umumiy mantiq {#shared-kit}

`Shared/Sources/User.swift`:

```swift
import Foundation

public struct User: Identifiable, Codable, Equatable, Sendable {
    public let id: UUID
    public var username: String
    public var email: String
    public var role: UserRole

    public init(id: UUID = UUID(), username: String, email: String, role: UserRole = .user) {
        self.id = id
        self.username = username
        self.email = email
        self.role = role
    }
}

public enum UserRole: String, Codable, Sendable {
    case admin = "ADMIN"
    case moderator = "MODERATOR"
    case user = "USER"
}
```

`Shared/Sources/SecurityValidator.swift`:

```swift
import Foundation

public struct SecurityValidator: Sendable {
    public static func isValidEmail(_ email: String) -> Bool {
        let pattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
        return email.range(of: pattern, options: .regularExpression) != nil
    }
}
```

`Shared/BUILD.bazel`:

```python
load("@rules_swift//swift:swift.bzl", "swift_library", "swift_test")

swift_library(
    name = "SharedKit",
    srcs = glob(["Sources/**/*.swift"]),
    module_name = "SharedKit",
    visibility = ["//visibility:public"],
)

swift_test(
    name = "SharedTests",
    srcs = glob(["Tests/**/*.swift"]),
    deps = [":SharedKit"],
)
```

---

## 4. macOS CLI nishoni: Admin vositasi {#cli-target}

Server administratorlari yoki muhandislar terminaldan foydalanuvchilarni tekshirishi uchun macOS CLI vositasi:

`Tools/AdminCLI/main.swift`:

```swift
import Foundation
import SharedKit

print("🛠️ Apple Monorepo Admin Tool (macOS)")
print("------------------------------------")

let sampleUser = User(username: "azimjon", email: "azimjondevuz@gmail.com", role: .admin)

print("Tekshirilayotgan foydalanuvchi: \(sampleUser.username)")
print("Rol: \(sampleUser.role.rawValue)")

let isEmailValid = SecurityValidator.isValidEmail(sampleUser.email)
print("Email holati: \(isEmailValid ? "To'g'ri ✅" : "Noto'g'ri ❌")")
```

`Tools/AdminCLI/BUILD.bazel`:

```python
load("@rules_swift//swift:swift.bzl", "swift_binary")

swift_binary(
    name = "admin_tool",
    srcs = ["main.swift"],
    deps = ["//Shared:SharedKit"],
    visibility = ["//visibility:public"],
)
```

CLI'ni ishga tushirish:

```bash
bazel run //Tools/AdminCLI:admin_tool
```

---

## 5. iOS ilova nishoni: Mobil interfeys {#ios-target}

Xuddi shu `SharedKit` kutubxonasidan mobil ilova ham foydalanadi:

`Apps/iOS/Sources/MobileApp.swift`:

```swift
import SwiftUI
import SharedKit

@main
struct MobileApp: App {
    @State private var currentUser = User(
        username: "dasturchi",
        email: "dasturchi@domain.uz",
        role: .user
    )

    var body: some Scene {
        WindowGroup {
            NavigationStack {
                VStack(spacing: 20) {
                    Image(systemName: "person.circle.fill")
                        .font(.system(size: 80))
                        .foregroundColor(.blue)

                    Text(currentUser.username)
                        .font(.title)
                        .bold()

                    Text(currentUser.email)
                        .font(.subheadline)
                        .foregroundColor(.secondary)

                    Text("Roli: \(currentUser.role.rawValue)")
                        .font(.caption)
                        .padding(.horizontal, 10)
                        .padding(.vertical, 4)
                        .background(Color.green.opacity(0.2))
                        .foregroundColor(.green)
                        .clipShape(Capsule())
                }
                .navigationTitle("Foydalanuvchi Profili")
            }
        }
    }
}
```

`Apps/iOS/BUILD.bazel`:

```python
load("@rules_swift//swift:swift.bzl", "swift_library")
load("@rules_apple//apple:ios.bzl", "ios_application")

swift_library(
    name = "iOSLib",
    srcs = ["Sources/MobileApp.swift"],
    deps = ["//Shared:SharedKit"],
)

ios_application(
    name = "MobileApp",
    bundle_id = "uz.azimjondev.monorepo.mobile",
    families = ["iphone", "ipad"],
    infoplists = ["Info.plist"],
    minimum_os_version = "17.0",
    deps = [":iOSLib"],
    visibility = ["//visibility:public"],
)
```

Simulyatorda ishga tushirish:

```bash
bazel run //Apps/iOS:MobileApp
```

---

## 6. Atomik refaktoring: Ikkala platformani bir vaqtda yangilash {#atomic-refactor}

Faraz qilaylik, biz `UserRole` enumiga yangi `guest = "GUEST"` rolini qo'shishimiz kerak.

Multi-repo tizimida bu kamida 3 ta PR va bir necha soatlik kutishni talab qilardi.

Monorepoda esa:
1. `Shared/Sources/User.swift`ga `case guest = "GUEST"` qo'shamiz.
2. `Tools/AdminCLI/main.swift` va `Apps/iOS/Sources/MobileApp.swift`da zarur joylarni to'g'rilaymiz.
3. Bitta buyruq bilan barcha platformalarni tekshiramiz:
   ```bash
   bazel test //...
   ```
4. Hammasi yashil bo'lgach, bitta git commit qilamiz:
   ```bash
   git commit -m "feat(core): add guest role across iOS and CLI platforms"
   ```
Ikkala platforma hech qachon versiyalar nomutanosibligiga duch kelmaydi!

---

## 7. Monorepo'da Selective Testing: Vaqtni tejash san'ati {#monorepo-testing}

Monorepo'ning eng xavfli tomoni — loyiha ulg'aygan sari har bir PR'da butun repodagi yuzlab nishonlarni tekshirish sekinlashishi mumkin.

Bazel query aynan shu yerda najot beradi:

### Ssenariy 1: Dasturchi faqat iOS UI'ni o'zgartirdi
`Apps/iOS/Sources/MobileApp.swift` o'zgardi.

```bash
CHANGED="Apps/iOS/Sources/MobileApp.swift"
bazel query "tests(rdeps(//..., set($CHANGED)))"
```
Natija:
```text
//Apps/iOS:iOSTests
```
`Tools/AdminCLI` va `SharedTests` hatto ishga tushmaydi! CI atigi 20 soniyada o'tadi.

### Ssenariy 2: Dasturchi SharedKit'ni o'zgartirdi
`Shared/Sources/User.swift` o'zgardi.

```bash
CHANGED="Shared/Sources/User.swift"
bazel query "tests(rdeps(//..., set($CHANGED)))"
```
Natija:
```text
//Shared:SharedTests
//Apps/iOS:iOSTests
//Tools/AdminCLI:CLITests
```
Bazel barcha bog'liq testlarni topadi va har ikkala platformada ham xavfsizlikni to'liq kafolatlaydi!

---

## 8. Monorepo uchun GitHub Actions pipeline {#monorepo-ci}

`.github/workflows/monorepo_ci.yml`:

```yaml
name: Monorepo CI

on:
  pull_request:
    branches: [ main ]

jobs:
  selective-matrix:
    runs-on: macos-15
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Select Xcode 27.1
        run: sudo xcode-select -s /Applications/Xcode_27.1.app/Contents/Developer

      - name: Run Affected Tests Only
        run: |
          CHANGED=$(git diff --name-only origin/main...HEAD)
          echo "O'zgargan fayllar: $CHANGED"

          TESTS=$(bazel query "tests(rdeps(//..., set($CHANGED)))" 2>/dev/null || true)
          if [ -n "$TESTS" ]; then
            echo "Yugurtiriladigan testlar: $TESTS"
            bazel test $TESTS
          else
            echo "Hech qanday test ta'sirlanmadi."
          fi
```

---

## Amaliy mashqlar {#mashqlar}

1. **Yangi platforma: WatchOS yoki Server (Vapor).** Monorepoga `Apps/Watch/` papkasi qo'shing va `SharedKit`ni watchOS targetiga ulab ko'ring.
2. **`rdeps` chuqurligi tahlili.** `bazel query "rdeps(//..., //Shared:SharedKit, 1)"` buyrug'i bilan `SharedKit`ga bevosita bog'langan 1-darajali targetlar ro'yxatini chiqaring.
3. ⭐ **Breaking change eksperimenti.** `User.swift`dagi `username` maydoni nomini `fullName` deb o'zgartiring va `bazel build //...` xatolik bergan barcha fayllarni (iOS va CLI) bir vaqtda to'g'rilab ko'ring.

---

## O'zingizni tekshiring {#checklist}

- [ ] Monorepo multi-repoga nisbatan versiyalash va dependency boshqaruvida qanday ustunlik berishini bilaman.
- [ ] Bitta `SharedKit` moduli bir vaqtning o'zida iOS va macOS targetlariga qanday ulanishini tushunaman.
- [ ] Monorepoda `rdeps` orqali faqat o'zgargan qatlamga bog'liq testlarni filtrlash formulasini qo'llay olaman.
- [ ] Atomik commit nima ekanini va nima uchun u mobil jamoalarga xotirjamlik berishini tushuntira olaman.

---

## Xulosa {#xulosa}

- Monorepo — bu zamonaviy gigant platformalarning (Google, Uber, Airbnb) tanlovi bo'lib, uning samaradorligini faqat Bazel kabi kuchli build tizimi kafolatlay oladi.
- Biz iOS va macOS vositalarini umumiy Swift kutubxonasi bilan birlashtirdik va ularni selective testing bilan qurollantirdik.
- Endi biz Bazel'ning ichki mexanizmlariga eng chuqur sho'ng'iymiz: **O'z Starlark rule'larimizni yozish!**
- **Keyingi dars:** [Bazel 17: O'z rule'laringiz: genrule, rule(), provider'lar, depset'lar, aspect'lar va toolchain'lar](/ios-lessons/bazel-17-custom-rules)

---

## Manbalar {#manbalar}

1. [Dan Luu — Advantages of Monorepos](https://danluu.com/monorepo/) — Monorepo arxitekturasi bo'yicha klassik tahlil.
2. [Uber Engineering — iOS Monorepo Architecture](https://www.uber.com/blog/research/) — Uber'ning keng ko'lamli mobil monorepo tajribasi.
3. [Bazel Documentation — Workspaces and Multiple Targets](https://bazel.build/concepts/build-ref) — Bir nechta targetlarni bitta omborda tashkil etish.
