Skip to content
Santekno.com | Level Up Your Engineering Skills
ID
📖 0%
31 Aug 2025 · 5 mnt baca ·Artikel 62 / 125
Go

62 Resolver dengan Dependency Injection di Go

IH
Ihsan Arif
Penulis di Santekno · Backend Engineer

62 Resolver dengan Dependency Injection di Go

Dependency Injection (DI) di Go adalah praktik penting yang mendukung testing, scalability, dan pemisahan concern. Namun, integrasi teknik DI dengan provider dependency—seperti resolver pada GraphQL—masih sering membingungkan, terutama untuk engineer Go yang terbiasa dengan struktur kode monolitik. Dalam artikel ini, saya akan membahas penerapan 62 resolver dengan Dependency Injection di Go, membedah konsep ini, menjelaskan best-practices, dan melengkapi dengan contoh kode, diagram, serta tabel simulasi. Mari kita mulai.


Apa Itu Resolver dalam Konteks Go?

Pada umumnya, resolver merujuk pada fungsi/factory yang “meresolusikan” dependensi runtime saat code berjalan. Contoh paling nyata terlihat pada aplikasi GraphQL di Go, misal menggunakan gqlgen . Resolver merepresentasikan layer antara query GraphQL dan implementation business logic, seringkali bergantung pada service, repository atau komponen lain.

Danger
Angka 62 di judul menunjuk pada jumlah resolver yang kita simulasikan di skala proyek menengah-besar (misal: 62 endpoint/service logic yang perlu “diresolusikan”).

Tantangan Mengelola 62 Resolver secara Manual

Masalah utama ketika mengelola banyak resolver di Go:

  1. Duplikasi kode boilerplate: Injeksi dependensi secara manual di setiap resolver.
  2. Sulit di-maintain: Setiap penambahan dependency baru, mengubah constructor tiap resolver.
  3. Testing lebih susah: Kalau DI tidak konsisten, mocking dan testing jadi rumit.

Dependency Injection dasar di Go

Go bukan Java atau .NET yang memiliki DI container secara native. Namun, praktik dependency injection tetap bisa digunakan dengan prinsip constructor injection atau interface injection.

Berikut contoh pola paling dasar:

go
 1type UserService interface {
 2    GetUser(id string) (*User, error)
 3}
 4
 5type UserResolver struct {
 6    Service UserService
 7}
 8
 9func NewUserResolver(svc UserService) *UserResolver {
10    return &UserResolver{Service: svc}
11}

Untuk 62 resolver, tanpa DI yang baik:

go
1var postResolver   = NewPostResolver(postService)
2var commentResolver = NewCommentResolver(commentService)
3var friendResolver  = NewFriendResolver(friendService)
4// dst untuk ke-62 resolver...

Terlalu verbose dan mudah terjadi kesalahan.


Membuat Layer Dependency Injection yang Efektif

Strategi populer untuk resolver injection di Go:

  • Manual wiring — Langsung menghubungkan dependency pada main.go atau composition root
  • Menggunakan DI container — Library third-party seperti uber-go/dig

Mari bahas keduanya.

1. Manual Wiring

Paling simple dan “Go idiomatic”. Tabel berikut membandingkan manual wiring dan container:

KriteriaManual WiringDI Container (dig)
BoilerplateCukup BanyakMinim
Compile-time SafetyTinggiSedang
Refactor MudahTergantungMudah
Learning CurveRendahSedang

Contoh Manual Wiring (Constructor Injection)

Misal kita punya 3 service dan ingin extend ke 62 resolver (kode berikut prinsip dasarnya):

go
 1type AppResolvers struct {
 2    UserResolver    *UserResolver
 3    PostResolver    *PostResolver
 4    CommentResolver *CommentResolver
 5    // ... sampai 62 resolver
 6}
 7
 8func NewAppResolvers(db *sql.DB) *AppResolvers {
 9    userService := NewUserService(db)
10    postService := NewPostService(db)
11    commentService := NewCommentService(db)
12
13    return &AppResolvers{
14        UserResolver:    NewUserResolver(userService),
15        PostResolver:    NewPostResolver(postService),
16        CommentResolver: NewCommentResolver(commentService),
17    }
18}

Jika dependency chain semakin dalam (misal service tergantung repository, tergantung cache dll), wiring jadi semakin panjang.


2. Dependency Injection Container dengan Uber-dig

Jika resolver mencapai puluhan, library seperti Uber Dig bisa sangat membantu. dig menyederhanakan pembuatan dependency graph & otomatis resolve dependency.

Instalasi

shell
1go get go.uber.org/dig

Implementasi dengan Uber-dig: Simulasi Resolver

Misal kita punya 3 resolver dari total 62 (untuk ringkas):

go
 1import (
 2    "database/sql"
 3    "go.uber.org/dig"
 4)
 5
 6type UserService struct{ db *sql.DB }
 7type PostService struct{ db *sql.DB }
 8type CommentService struct{ db *sql.DB }
 9
10func NewUserService(db *sql.DB) *UserService      { return &UserService{db} }
11func NewPostService(db *sql.DB) *PostService      { return &PostService{db} }
12func NewCommentService(db *sql.DB) *CommentService{ return &CommentService{db} }
13
14type UserResolver struct{ Service *UserService }
15type PostResolver struct{ Service *PostService }
16type CommentResolver struct{ Service *CommentService }
17
18func NewUserResolver(svc *UserService) *UserResolver       { return &UserResolver{svc} }
19func NewPostResolver(svc *PostService) *PostResolver       { return &PostResolver{svc} }
20func NewCommentResolver(svc *CommentService) *CommentResolver { return &CommentResolver{svc} }
21
22func main() {
23    container := dig.New()
24    container.Provide(sql.Open) // supply *sql.DB
25    container.Provide(NewUserService)
26    container.Provide(NewPostService)
27    container.Provide(NewCommentService)
28    container.Provide(NewUserResolver)
29    container.Provide(NewPostResolver)
30    container.Provide(NewCommentResolver)
31
32    // resolve all resolvers
33    container.Invoke(func(
34        userResolver *UserResolver,
35        postResolver *PostResolver,
36        commentResolver *CommentResolver,
37    ) {
38        // bisa inject ke router, GraphQL server, dll
39    })
40}

Scalability: Cukup tambah .Provide() setiap resolver/service baru. Uber-dig menguraikan dependency graph dan inject secara otomatis, walaupun jumlahnya ratusan.


Diagram Alur Dependency Injection

Mari lihat gambaran wiring resolver dengan DI menggunakan mermaid:

MERMAID
graph TD
  subgraph Database Layer
    DB[(Database)]
  end

  subgraph Service Layer
    USV[UserService]
    PSV[PostService]
    CSV[CommentService]
  end

  subgraph Resolver Layer
    UR[UserResolver]
    PR[PostResolver]
    CR[CommentResolver]
  end

  DB --> USV
  DB --> PSV
  DB --> CSV

  USV --> UR
  PSV --> PR
  CSV --> CR

  %% Dst. hingga 62 resolver

Bayangkan diagram di atas membesar ke 62 node di layer Service & Resolver—tanpa DI container, wiring-nya sangat rawan error.


Studi Simulasi: Penambahan Resolver ke-63

Tanpa DI Container

  • Ubah constructor AppResolvers
  • Tambah dependency di main.go
  • Add/match di tiap service dan resolver
  • Tinggi risiko error

Dengan DI Container

  • Tambah .Provide(NewNewResolver) pada container
  • Implementasi tetap konsisten
  • No need to modify existing code

Tabel Simulasi Penambahan Dependency:

DependencyManual Wiring (Langkah)uber-dig (Langkah)
Kode di Resolver11
Kode di AppResolvers10
Kode di main.go2-31
Risiko Human ErrorSedangRendah

Testing Resolver dengan Dependency Injection

Keuntungan utama DI: testing lebih mudah! Kita bisa mock/services, inject dependency ke resolver, dan isolasi unit test.

go
1func TestUserResolver_GetUser(t *testing.T) {
2    mockService := &MockUserService{}
3    resolver := NewUserResolver(mockService)
4    // lanjutkan dengan pengujian
5}

Kesimpulan

Mengelola 62+ resolver di project Go adalah tantangan serius jika dependensi tidak didesain dengan baik. Dependency Injection—baik manual maupun menggunakan container seperti Uber-dig—adalah solusi yang scalable, testable, dan maintainable. Meski Go tidak memiliki DI container built-in, pola constructor injection dan adopt library eksternal memberikan fleksibilitas tinggi untuk menangani codebase yang kompleks.

Jadikan DI sebagai standar baru ketika membangun proyek Go berskala besar—baik untuk resolver GraphQL, REST handler, atau service logic lainnya!


Referensi:


Semoga artikel ini membantu Anda membangun resolver yang scalable di Go!
Have fun coding 🚀

Artikel Terkait

💬 Komentar