Belajar Microservices - Setup Struktur Projek tokokita (Monorepo)
Episode 3 of 28

Belajar Microservices - Setup Struktur Projek tokokita (Monorepo)

Membangun fondasi projek tokokita: membandingkan monorepo dan polyrepo, menyusun struktur folder untuk tujuh layanan plus shared packages, lalu menjalankan environment dev PostgreSQL, Redis, dan Kafka via Docker Compose

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 2 kita menetapkan empat pilar arsitektur — bounded context, database per service, API gateway, dan dua gaya komunikasi — pada episode ini kita mulai membangun tokokita secara nyata. Kali ini kita akan menentukan bagaimana projek diorganisasi: satu repository (monorepo) atau banyak repository (polyrepo), lalu menyusun struktur foldernya dan menghidupkan infrastruktur dev.

Mengapa keputusan struktur projek penting? Karena di microservices, cara kode diorganisasi secara langsung memengaruhi kecepatan kolaborasi tim dan kemudahan berbagi kode antar layanan. Pilihan yang salah akan membuat berbagi schema validasi atau helper database menjadi mimpi buruk.

Monorepo vs Polyrepo

Monorepo: Satu Repo, Banyak Package

Semua layanan dan package ditaruh dalam satu repository dengan workspace manager (pnpm/Bun). Keunggulannya:

  • Code sharing mudah — package shared-types langsung dipakai semua service tanpa publish registry.
  • Satu sumber konfigurasilint, tsconfig, dan tooling seragam.
  • Perubahan atomik — mengubah DTO dan semua pemakainya dalam satu commit.
  • Path-filtering CI — pipeline hanya menjalankan layanan yang berubah (episode 23).

Kekurangannya: repo menjadi besar, dan akses/permission per-service tidak bisa dipisah (buntu di GitHub).

Polyrepo: Satu Repo per Layanan

Setiap layanan punya repository dan CI-nya sendiri. Keunggulan: tim benar-benar independen, permission terisolasi. Kekurangan: berbagi kode harus via package registry, perubahan menyebar ke banyak repo, dan konsistensi tooling sulit dipertahankan.

Pilihan: Monorepo (Bun Workspaces)

Untuk series belajar ini kita memilih monorepo dengan Bun workspaces — kemudahan berbagi shared library jauh lebih berharga daripada isolasi tim. Di produksi, tim 3+ boleh mempertimbangkan polyrepo per tim; topik ini kembali di episode 23.

Operasi workspace Bun
# daftarkan packages/* dan services/* sebagai workspace
[workspaces]
packages = ["packages/*", "services/*", "apps/*"]

Struktur Folder Proyek

Berikut struktur tokokita yang akan kita bangun:

Struktur folder tokokita
tokokita/
  apps/
    web-app/            # React + Vite (frontend)
    api-gateway/        # BFF API
  services/
    auth-service/
    product-service/
    cart-service/
    order-service/
    payment-service/
    notification-service/
  packages/
    shared-types/       # DTO & zod schemas
    client-db/          # Drizzle schema helpers
  infra/
    compose.yaml
    k8s/

Setiap service dan app memiliki package.json sendiri dengan "name": "auth-service" dst., sehingga Bun workspace otomatis mengenali semuanya dan mengelola dependensi bersama.

Shared Packages

shared-types: Zod Schema untuk Validasi

Semua kontrak API (request body, response, event payload) didefinisikan sekali pakai zod schema sehingga klien dan server memvalidasi dengan skema yang sama.

packages/shared-types/src/auth.ts
import { z } from 'zod'
 
export const registerRequestSchema = z.object({
  email: z.string().email(),
  password: z.string().min(8),
  name: z.string().min(1),
})
 
export const userCreatedEventSchema = z.object({
  id: z.string().uuid(),
  email: z.string().email(),
  name: z.string(),
  occurredAt: z.string().datetime(),
})
 
export type RegisterRequest = z.infer<typeof registerRequestSchema>
export type UserCreatedEvent = z.infer<typeof userCreatedEventSchema>

client-db: Helper Koneksi Database

Helper koneksi PostgreSQL (pakai drizzle-orm atau postgres.js) agar semua service memakai cara koneksi dan retry yang konsisten.

packages/client-db/src/index.ts
import postgres from 'postgres'
 
export function createDb(url: string) {
  return postgres(url, {
    max: 10,
    idle_timeout: 20,
    connect_timeout: 10,
  })
}

Kedua package dipakai oleh service lewat import biasa: import { registerRequestSchema } from '@tokokita/shared-types'.

Environment Dev: Docker Compose

Kita jalankan tiga dependency utama lewat compose — PostgreSQL, Redis, dan Redpanda (drop-in Kafka).

infra/compose.yaml
services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: tokokita
      POSTGRES_PASSWORD: tokokita
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U tokokita"]
      interval: 5s
      timeout: 5s
      retries: 5
 
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
 
  redpanda:
    image: redpandadata/redpanda:latest
    command: redpanda start \
      --overprovisioned \
      --smp 1 \
      --memory 1G \
      --node-id 0 \
      --kafka-addr internal://0.0.0.0:9092 \
      --advertise-kafka-addr internal://redpanda:9092
    ports:
      - "9092:9092"
      - "9644:9644"
 
volumes:
  pgdata:

Jalankan dan verifikasi:

Hidupkan infrastruktur dev
docker compose -f infra/compose.yaml up -d
docker compose ps
pg_isready -h localhost -p 5432

Catatan: redpanda console (UI broker) bisa ditambahkan di port 8080 — kita pakai port 9644 untuk admin API. Detail jaringan antar container dan best practice compose dibahas penuh di episode 16.

Menjalankan Service Dev

Setiap service kemudian dijalankan terpisah di terminalnya sendiri (atau pakai bun --cwd services/auth-service dev):

Jalankan semua service di mode dev
bun --cwd services/auth-service run dev
bun --cwd services/product-service run dev
bun --cwd services/api-gateway run dev

Versi produksi memakai bun run dengan bun build untuk bundling, tapi untuk development mode watch cukup.

Note

Jangan tulis database, Redis, dan Kafka dalam satu container semua service. Dependency infrastruktur boleh berbagi di local dev, tetapi tidak ada layanan yang berbagi database. Di episode 4, auth-service akan memiliki schema users sendiri yang tidak di-sentuh service lain.

Penutup

Episode 3 membangun fondasi projek tokokita:

  • Monorepo + Bun workspaces dipilih atas polyrepo untuk kemudahan berbagi kode.
  • Struktur folder: apps/ (web-app, api-gateway), services/ (enam layanan), packages/ (shared-types, client-db), infra/.
  • Shared packages: zod schema (shared-types) dan helper DB (client-db).
  • Environment dev: PostgreSQL 16, Redis 7, Redpanda (Kafka) via Docker Compose.
  • Service dev dijalankan per-service dengan bun run dev.

Di episode 4 selanjutnya, kita akan membangun auth-service — register, login, JWT access token + refresh token rotating, logout, dan ganti password, lengkap dengan integrasi Redis untuk blacklist serta emit event user.registered ke event bus. Sampai jumpa di episode 4!

Belajar Microservices - Setup Struktur Projek tokokita (Monorepo) | Belajar Microservices