tutorial

8 Struktur Direktori Ideal untuk Proyek graphql-go

8 Struktur Direktori Ideal untuk Proyek graphql-go

GraphQL menjadi solusi mainstream untuk API karena modelnya yang fleksibel dan kemampuan fetch data yang efisien. Salah satu library yang cukup populer untuk Go adalah graphql-go. Meskipun dokumentasi library ini solid, detail tentang struktur proyek sering kali diabaikan—padahal, project structure punya dampak signifikan ke maintainability dan collaboration.

Selama beberapa tahun membangun microservice dengan GraphQL di Go, saya sudah mencoba aneka pola, serta melihat berbagai bentuk codebase di open-source maupun internal product. Artikel ini membagikan 8 struktur direktori ideal yang menurut saya bisa membuat proyek graphql-go kamu lebih terorganisir, scaleable dan nyaman di-maintain oleh tim.


1. Struktur Dasar: Template Modular, bukan Monolith

Singkatnya, flat structure itu jebakan pemula. Proyek kecil memang bisa survive, tapi begitu berkembang, satu folder jadi lautan file yang berantakan. Struktur modular—berbasis domain atau feature—memberi ruang untuk bertumbuh.

Berikut template ideal yang bisa dijadikan starting point:

my-graphql-app/
├── cmd/
│   └── server/
│       └── main.go
├── internal/
│   ├── domain/
│   │   ├── user/
│   │   │   ├── model.go
│   │   │   ├── resolver.go
│   │   │   └── repository.go
│   │   └── post/
│   │       ├── model.go
│   │       ├── resolver.go
│   │       └── repository.go
│   ├── gql/
│   │   ├── schema/
│   │   │   └── schema.graphql
│   │   ├── loader.go
│   │   └── resolver.go
│   ├── config/
│   │   └── config.go
│   └── pkg/
│       ├── db/
│       │   └── db.go
│       ├── logger/
│       │   └── logger.go
│       └── utils/
│           └── utils.go
├── vendor/
├── go.mod
└── go.sum

2. cmd/ – Entry Point yang Clean

Di dalam folder cmd/, kita letakkan entry point aplikasi; biasanya satu folder per binary. Untuk server GraphQL, buat cmd/server/main.go. Di sinilah aplikasi start, parse config, dan menjalankan server.

Contoh:

package main

import (
  "log"
  "net/http"
  "my-graphql-app/internal/gql"
  "my-graphql-app/internal/config"
)

func main() {
  cfg := config.Load()
  
  srv := gql.NewGraphQLServer(cfg)
  http.Handle("/query", srv)

  log.Printf("Running at :%s...", cfg.Port)
  log.Fatal(http.ListenAndServe(":" + cfg.Port, nil))
}

3. internal/domain/ – Pisahkan Per Domain/Feature

Jangan campurkan semua resolvers dalam satu file. Gunakan internal/domain/ guna menyimpan resource per domain (misal: user, post). Setiap domain punya:

  • model.go: Struct data sesuai schema
  • repository.go: Abstraksi akses data
  • resolver.go: Implementasi resolver GraphQL spesifik untuk domain

Contoh internal/domain/user/model.go:

package user

type User struct {
  ID    string
  Name  string
  Email string
}

Dan resolver-nya:

package user

import "context"

type Resolver struct {
  Repo Repository
}

// Contoh query resolver
func (r *Resolver) GetUser(ctx context.Context, args struct{ID string}) (*User, error) {
  return r.Repo.FindByID(ctx, args.ID)
}

4. internal/gql/ – Pusat GraphQL Logic

Folder gql/ bersifat global untuk GraphQL, misal:

  • schema/: File .graphql, misal schema.graphql
  • loader.go: Fungsionalitas DataLoader (bila butuh efisiensi query)
  • resolver.go: Root resolver yang mendelegasikan ke masing-masing resolver domain

Contoh root resolver:

package gql

import (
  "my-graphql-app/internal/domain/user"
  "my-graphql-app/internal/domain/post"
)

type Resolver struct {
  User *user.Resolver
  Post *post.Resolver
}

5. internal/config/ – Konfigurasi Terkelola

Segala konfigurasi (port, env, dsb) ditempatkan di sini dalam satu source of truth, memudahkan testing dan deployment.

package config

import "os"

type Config struct {
  Port string
  DBUrl string
}

func Load() *Config {
  return &Config{
    Port: os.Getenv("PORT"),
    DBUrl: os.Getenv("DB_URL"),
  }
}

6. internal/pkg/ – Helper & Library Umum

Simpan library internal, helper, atau komponen infrastruktur (misal utilitas database, logger, JWT, dsb) di sini. Hindari kebiasaan kodok “helper.go” di root yang isinya random stuff.

Contoh basic logger:

package logger

import "log"

func Info(msg string) {
  log.Printf("[INFO]: %s", msg)
}

7. Testing: Dekatkan Test dengan Fungsinya

Terapkan konvensi Go, simpan test di file _test.go pada folder yang sama. Untuk integrasi GraphQL, ciptakan test case query ke API.

Contoh: internal/domain/user/user_test.go

func TestGetUser(t *testing.T) {
  // Set up dummy repository and context
  // Call resolver
  // Assert output
}

8. Documentasi & Schema yang Rapi

Jangan sepelekan API documentation. Simpan GraphQL schema di internal/gql/schema/schema.graphql. Lengkapi dengan deskripsi setiap type/query.

Contoh:

"""User yang terdaftar dalam sistem"""
type User {
  "ID unik user"
  id: ID!
  "Nama user"
  name: String!
  "Email user"
  email: String!
}

type Query {
  "Ambil user sesuai ID"
  user(id: ID!): User
}

Simulasi Alur Query

Mari visualisasikan alurnya dengan diagram Mermaid:

flowchart LR
    Client["Client"]
    GQLHandler["GraphQL Handler (/query)"]
    RootResolver["Root Resolver"]
    UserDomain["User Resolver (Domain)"]
    DB["User Repository & Database"]

    Client --> GQLHandler
    GQLHandler --> RootResolver
    RootResolver --> UserDomain
    UserDomain --> DB

Begitu request GraphQL masuk, handler akan parsing schema, request didelegasikan ke root resolver sesuai dengan type/query, baru dilempar ke resolver domain (misal: user), lalu diteruskan ke repository buat fetch data dari DB.


Tabel Referensi Struktur Folder

FolderIsi & Tanggung JawabContoh File
cmd/Entry point aplikasimain.go
internal/domain/Business logic per domain/modeluser/model.go
internal/gql/Integrasi GraphQL (schema & root resolver)schema.graphql, loader.go
internal/config/Konfigurasi terpusatconfig.go
internal/pkg/Utility/helper/infra internaldb.go, logger.go
vendor/Library dependencies
go.mod, go.sumGo module management

Kesimpulan

Mengadopsi struktur direktori yang rapi adalah investasi jangka panjang. Dengan memisahkan concern antar domain, utility, schema, dan entry point, kita membantu seluruh tim maintain dan scale codebase. Don’t let your project structure be an afterthought. Mulai dengan kerangka modular seperti di atas, lalu adaptasikan sesuai kebutuhan real teams. Struktur yang baik = maintainability, scalability, dan happy team.

Mau diskusi? Drop di komentar pengalaman kalian dengan struktur GraphQL project di Go!


Referensi:

comments powered by Disqus