## πŸ‘‹ ΠŸΡ€ΠΈΠ²Π΅Ρ‚! Π― ОлСг ΠšΠΎΠ·Ρ‹Ρ€Π΅Π² Staff Golang Engineer Β· Ex Ozon, Avito, Tinkoff πŸ“Ί [YouTube](https://www.youtube.com/@olezhek28go) Β· πŸ’¬ [Telegram](https://t.me/olezhek28go) --- # Dependency Injection Π² Go: ΠΎΡ‚ Π°Π½Ρ‚ΠΈΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½ΠΎΠ² ΠΊ элСгантным Ρ€Π΅ΡˆΠ΅Π½ΠΈΡΠΌ Π Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ с ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°ΠΌΠΈ Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Ρ… ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΎΠ² ΠΊ Π²Π½Π΅Π΄Ρ€Π΅Π½ΠΈΡŽ зависимостСй (DI) Π² Go-прилоТСниях. ВсС ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ **ΠΎΠ΄Π½Ρƒ ΠΈ Ρ‚Ρƒ ΠΆΠ΅ бизнСс-Π»ΠΎΠ³ΠΈΠΊΡƒ** ΠΈΠ· `internal/` β€” мСняСтся Ρ‚ΠΎΠ»ΡŒΠΊΠΎ способ сборки зависимостСй. ## Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° ``` internal/ β”œβ”€β”€ config/ β€” конфигурация прилоТСния (DSN, Redis, HTTP-адрСс) β”œβ”€β”€ database/ β€” абстракция Π‘Π” (интСрфСйс DB + рСализация) β”œβ”€β”€ cache/ β€” абстракция кэша (интСрфСйс Cache + Redis-Π·Π°Π³Π»ΡƒΡˆΠΊΠ°) β”œβ”€β”€ events/ β€” шина событий (Publish/Subscribe) β”œβ”€β”€ repository/ β€” Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΈ (user, session, notification) β”œβ”€β”€ service/ β€” сСрвисы (auth, user, notification) β”œβ”€β”€ api/ β€” HTTP-Ρ…Π΅Π½Π΄Π»Π΅Ρ€ ΠΈ Ρ€ΠΎΡƒΡ‚ΠΈΠ½Π³ └── app/ β€” сборка прилоТСния + кастомный DI-ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ cmd/ β”œβ”€β”€ antipattern/ β€” ручная сборка (ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°) β”œβ”€β”€ antipattern-broken/ β€” ручная сборка (сломанная) β”œβ”€β”€ wire-di/ β€” Google Wire β”œβ”€β”€ wire-di-broken/ β€” Wire + цикличСскиС зависимости β”œβ”€β”€ fx-di/ β€” Uber Fx (свои интСрфСйсы Ρƒ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ потрСбитСля) β”œβ”€β”€ fx-di-broken/ β€” Fx + цикличСскиС зависимости β”œβ”€β”€ fx-di-shared/ β€” Uber Fx (ΠΎΠ±Ρ‰ΠΈΠ΅ интСрфСйсы) β”œβ”€β”€ fx-di-modules/ β€” Uber Fx (ΠΌΠΎΠ΄ΡƒΠ»ΡŒΠ½Π°Ρ организация) └── manual-di/ β€” кастомный lazy-ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ (Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅) ``` ## ΠšΠ»ΡŽΡ‡Π΅Π²ΠΎΠΉ ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½: интСрфСйс опрСдСляСт ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΈΡ‚Π΅Π»ΡŒ ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ сСрвис ΠΈ Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ опрСдСляСт **свой собствСнный интСрфСйс** для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ зависимости β€” Ρ‚ΠΎΠ»ΡŒΠΊΠΎ с Ρ‚Π΅ΠΌΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π΅ΠΌΡƒ Π½ΡƒΠΆΠ½Ρ‹. Π­Ρ‚ΠΎ Go-ΠΈΠ΄ΠΈΠΎΠΌΠ° ΠΈ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏ раздСлСния интСрфСйсов (ISP). НапримСр, `UserRepo` опрСдСляСт интСрфСйс `UserDB` с Π½ΡƒΠΆΠ½Ρ‹ΠΌΠΈ Π΅ΠΌΡƒ ΠΌΠ΅Ρ‚ΠΎΠ΄Π°ΠΌΠΈ, Π° `SessionRepo` β€” свой `SessionDB`. Оба Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΡŽΡ‚ΡΡ ΠΎΠ΄Π½ΠΈΠΌ ΠΈ Ρ‚Π΅ΠΌ ΠΆΠ΅ `*database.db`. ## ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ ### 1. `cmd/antipattern/` β€” Ручная сборка зависимостСй ВсС зависимости ΡΠΎΠ·Π΄Π°ΡŽΡ‚ΡΡ Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ Π² `main()` Π² строгом порядкС. Π Π°Π±ΠΎΡ‚Π°Π΅Ρ‚, Π½ΠΎ: - порядок ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Тёстко зафиксирован - ΠΏΡ€ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ зависимости Π»Π΅Π³ΠΊΠΎ ΠΎΡˆΠΈΠ±ΠΈΡ‚ΡŒΡΡ - ΠΊΠΎΠ΄ main() растёт Π»ΠΈΠ½Π΅ΠΉΠ½ΠΎ с количСством ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² ```bash go run ./cmd/antipattern/ ``` ### 2. `cmd/antipattern-broken/` β€” Бломанная ручная сборка Π’ΠΎΡ‚ ΠΆΠ΅ ΠΊΠΎΠ΄, Π½ΠΎ строки пСрСставлСны мСстами. **ΠšΠΎΠΌΠΏΠΈΠ»ΠΈΡ€ΡƒΠ΅Ρ‚ΡΡ Π±Π΅Π· ошибок**, Π½ΠΎ ΠΏΠ°Π΄Π°Π΅Ρ‚ Π² Ρ€Π°Π½Ρ‚Π°ΠΉΠΌΠ΅ с nil pointer dereference β€” кэш ΠΈ сСрвисы ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ Π΄ΠΎ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ. ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚, ΠΏΠΎΡ‡Π΅ΠΌΡƒ ручная сборка хрупкая: компилятор Π½Π΅ Π»ΠΎΠ²ΠΈΡ‚ ошибки порядка ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ. ```bash go run ./cmd/antipattern-broken/ # curl http://localhost:8080/users/me β†’ panic: nil pointer # curl http://localhost:8080/health β†’ 200 OK (зависимости Π½Π΅ задСйствованы) ``` ### 3. `cmd/wire-di/` β€” Google Wire (кодогСнСрация) Π Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ описываСт ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Ρ‹ ΠΈ привязки Π² `wire.go`, Wire Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Ρ‹ΠΉ ΠΊΠΎΠ΄ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Π² `wire_gen.go`. Π Π΅ΡˆΠ°Π΅Ρ‚ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ порядка, Π½ΠΎ: - 18 Π²Ρ‹Π·ΠΎΠ²ΠΎΠ² `wire.Bind()` для ΠΌΠ°ΠΏΠΏΠΈΠ½Π³Π° Ρ‚ΠΈΠΏΠΎΠ² β†’ интСрфСйсы - eager-инициализация (всё создаётся сразу ΠΏΡ€ΠΈ стартС) - ΠΊΠΎΠ³Π΄Π° ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΈΡ‚Π΅Π»ΡŒ опрСдСляСт свой интСрфСйс, количСство Bind растёт быстро ```bash task generate-wire # гСнСрация ΠΊΠΎΠ΄Π° go run ./cmd/wire-di/ ``` ### 4. `cmd/wire-di-broken/` β€” Wire ΠΈ цикличСскиС зависимости ДСмонстрируСт Ρ†ΠΈΠΊΠ»: `EventBus β†’ NotificationService β†’ UserService β†’ AuthService β†’ EventBus`. Wire ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Π΅Ρ‚ Ρ†ΠΈΠΊΠ» **Π½Π° этапС Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ** ΠΈ отказываСтся Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΊΠΎΠ΄. ```bash task generate-wire-broken # ошибка: cycle for *EventBus ``` ### 5. `cmd/fx-di/` β€” Uber Fx (свои интСрфСйсы Ρƒ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ потрСбитСля) Π Π°Π½Ρ‚Π°ΠΉΠΌ DI-ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ Π½Π° рСфлСксии. Π Π°Π±ΠΎΡ‚Π°Π΅Ρ‚, Π½ΠΎ ΠΊΠΎΠ³Π΄Π° ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΈΡ‚Π΅Π»ΡŒ опрСдСляСт свой интСрфСйс β€” Ρ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚ ΠΌΠ½ΠΎΠ³ΠΎ Π±ΠΎΠΉΠ»Π΅Ρ€ΠΏΠ»Π΅ΠΉΡ‚Π°: - `fx.Out`/`fx.In` структуры для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ ΠΏΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€Π° - ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½Π½Ρ‹Π΅ зависимости Ρ‡Π΅Ρ€Π΅Π· struct-Ρ‚Π΅Π³ΠΈ - ~360 строк ΠΊΠΎΠ΄Π° ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ Fx Π·Π°Ρ‚ΠΎΡ‡Π΅Π½ ΠΏΠΎΠ΄ Β«ΠΎΠ΄ΠΈΠ½ Ρ‚ΠΈΠΏ β€” ΠΎΠ΄ΠΈΠ½ интСрфСйс» (ΠΊΠ°ΠΊ Π² Java/Spring), ΠΈ ΠΏΡ€ΠΈ Go-ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π΅ ΠΊ интСрфСйсам становится Π³Ρ€ΠΎΠΌΠΎΠ·Π΄ΠΊΠΈΠΌ. ```bash go run ./cmd/fx-di/ ``` ### 6. `cmd/fx-di-broken/` β€” Fx ΠΈ цикличСскиС зависимости Π’ΠΎΡ‚ ΠΆΠ΅ Ρ†ΠΈΠΊΠ», Ρ‡Ρ‚ΠΎ ΠΈ Π² `wire-di-broken`, Π½ΠΎ обнаруТиваСтся **Π² Ρ€Π°Π½Ρ‚Π°ΠΉΠΌΠ΅** ΠΏΡ€ΠΈ стартС прилоТСния. ```bash go run ./cmd/fx-di-broken/ # ошибка: cannot depend on the provided type ``` ### 7. `cmd/fx-di-shared/` β€” Uber Fx (ΠΎΠ±Ρ‰ΠΈΠ΅ интСрфСйсы) Fx с Β«Java-styleΒ» Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€ΠΎΠΉ: ΠΎΠ΄ΠΈΠ½ ΠΎΠ±Ρ‰ΠΈΠΉ интСрфСйс Π½Π° Ρ‚ΠΈΠΏ. Код Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΏΡ€ΠΎΡ‰Π΅ (~56 строк), lifecycle-Ρ…ΡƒΠΊΠΈ для graceful старта ΠΈ остановки. ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ Fx ΠΎΡ‚Π»ΠΈΡ‡Π½ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚, ΠΊΠΎΠ³Π΄Π° интСрфСйсы ΠΎΠ±Ρ‰ΠΈΠ΅, Π° Π½Π΅ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡŽΡ‚ΡΡ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΌ ΠΏΠΎΡ‚Ρ€Π΅Π±ΠΈΡ‚Π΅Π»Π΅ΠΌ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎ. ```bash go run ./cmd/fx-di-shared/ ``` ### 8. `cmd/fx-di-modules/` β€” Uber Fx (ΠΌΠΎΠ΄ΡƒΠ»ΡŒΠ½Π°Ρ организация) ΠŸΡ€ΠΎΠ΄Π²ΠΈΠ½ΡƒΡ‚Ρ‹ΠΉ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ с `fx.Module`: ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Π΄ΠΎΠΌΠ΅Π½ (auth, user, notification) β€” ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΌΠΎΠ΄ΡƒΠ»ΡŒ Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎΠΌ Ρ„Π°ΠΉΠ»Π΅. ΠŸΠΎΠ΄Ρ…ΠΎΠ΄ для Π±ΠΎΠ»ΡŒΡˆΠΈΡ… ΠΊΠΎΠΌΠ°Π½Π΄: каТдая ΠΊΠΎΠΌΠ°Π½Π΄Π° Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΡƒΠ΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ свой ΠΌΠΎΠ΄ΡƒΠ»ΡŒ, ΠΊΠΎΠ½Ρ„Π»ΠΈΠΊΡ‚ΠΎΠ² Π² `main.go` Π½Π΅Ρ‚. ```bash go run ./cmd/fx-di-modules/ ``` ### 9. `cmd/manual-di/` β€” ΠšΠ°ΡΡ‚ΠΎΠΌΠ½Ρ‹ΠΉ lazy-ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ (Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅) ЀинальноС Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅: кастомный DI-ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ ΠΈΠ· `internal/app/di.go` с Π»Π΅Π½ΠΈΠ²ΠΎΠΉ ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠ΅ΠΉ. - ~17 строк Π² main, ~50 строк Π² ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π΅ - зависимости ΡΠΎΠ·Π΄Π°ΡŽΡ‚ΡΡ ΠΏΡ€ΠΈ ΠΏΠ΅Ρ€Π²ΠΎΠΌ ΠΎΠ±Ρ€Π°Ρ‰Π΅Π½ΠΈΠΈ (lazy) - рСкурсивноС Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ зависимостСй β€” порядок Π½Π΅ Π²Π°ΠΆΠ΅Π½ - Π½ΠΈΠΊΠ°ΠΊΠΎΠΉ ΠΌΠ°Π³ΠΈΠΈ, рСфлСксии ΠΈ ΠΊΠΎΠ΄ΠΎΠ³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ - ошибки ловятся Π½Π° этапС компиляции ```bash go run ./cmd/manual-di/ ``` ## Π‘Ρ€Π°Π²Π½Π΅Π½ΠΈΠ΅ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΎΠ² | ΠŸΡ€ΠΈΠΌΠ΅Ρ€ | ΠŸΠΎΠ΄Ρ…ΠΎΠ΄ | ΠžΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ ошибок | Π‘ΠΎΠΉΠ»Π΅Ρ€ΠΏΠ»Π΅ΠΉΡ‚ | Π¦ΠΈΠΊΠ»Ρ‹ | |--------|--------|--------------------|-------------|-------| | `antipattern` | Ручная сборка | Π Π°Π½Ρ‚Π°ΠΉΠΌ (nil pointer) | НСт | НС ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Π΅Ρ‚ | | `wire-di` | ΠšΠΎΠ΄ΠΎΠ³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΡ | ГСнСрация ΠΊΠΎΠ΄Π° | 18 Bind() | Π›ΠΎΠ²ΠΈΡ‚ ΠΏΡ€ΠΈ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ | | `fx-di` | РСфлСксия (runtime) | Π Π°Π½Ρ‚Π°ΠΉΠΌ (старт) | fx.In/Out структуры | Π›ΠΎΠ²ΠΈΡ‚ ΠΏΡ€ΠΈ стартС | | `fx-di-shared` | РСфлСксия (runtime) | Π Π°Π½Ρ‚Π°ΠΉΠΌ (старт) | ΠœΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ | Π›ΠΎΠ²ΠΈΡ‚ ΠΏΡ€ΠΈ стартС | | `fx-di-modules` | РСфлСксия (runtime) | Π Π°Π½Ρ‚Π°ΠΉΠΌ (старт) | ΠœΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½Ρ‹ΠΉ | Π›ΠΎΠ²ΠΈΡ‚ ΠΏΡ€ΠΈ стартС | | `manual-di` | Lazy-ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ | ΠšΠΎΠΌΠΏΠΈΠ»ΡΡ†ΠΈΡ | НСт | Π Π΅ΡˆΠ°Π΅Ρ‚ Ρ‡Π΅Ρ€Π΅Π· lazy | ## ВрСбования - Go 1.26+ - [Task](https://taskfile.dev/) β€” таск-Ρ€Π°Π½Π½Π΅Ρ€ (Π·Π°ΠΌΠ΅Π½Π° Make) ### Установка Task ```bash # macOS brew install go-task # Linux (snap) sudo snap install task --classic # Или Ρ‡Π΅Ρ€Π΅Π· go install go install github.com/go-task/task/v3/cmd/task@latest ``` ## Запуск ```bash # Установка зависимостСй go mod download # Установка Wire (для ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠ² с ΠΊΠΎΠ΄ΠΎΠ³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠ΅ΠΉ) task install-wire # Запуск любого ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π° go run ./cmd/<имя-ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π°>/ ``` ### ДоступныС task-ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ ```bash task install-wire # ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ Wire локально Π² bin/ task generate-wire # ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ wire_gen.go для cmd/wire-di/ task generate-wire-broken # ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠ° Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ с Ρ†ΠΈΠΊΠ»ΠΎΠΌ (ΡƒΠΏΠ°Π΄Ρ‘Ρ‚ с ошибкой) ```