Advanced (Diperbarui: 3/6/2026)

Microservices dengan Claude Code: boundary, API, Compose, dan testing

Microservices dengan Claude Code: boundary, kontrak API, Compose, test, observability, dan rollout.

Microservices dengan Claude Code: boundary, API, Compose, dan testing

Microservices adalah desain yang memecah aplikasi besar menjadi layanan kecil yang independen dan saling terhubung lewat API atau event. Claude Code berguna jika dipakai untuk meninjau service boundary, kontrak API, kepemilikan database, Docker Compose lokal, gateway, observability, testing, dan rollout checklist sekaligus.

Namun microservices bukan jalan pintas otomatis. Setelah sistem dipecah, ada biaya baru: kegagalan jaringan, kompatibilitas API, transaksi terdistribusi, korelasi log, dan rollback yang lebih rumit. Kalau domain produk masih berubah setiap hari, modular monolith sering lebih aman. Fondasi terkait bisa dibaca di Claude Code API development, Docker Compose, logging dan monitoring, serta event-driven architecture.

Gunakan referensi resmi sebagai jangkar review: Anthropic Claude Code overview, Docker Compose documentation, dan OpenAPI Specification 3.1. Ini membantu memisahkan saran Claude Code dari kontrak yang benar-benar akan dioperasikan.

Mulai Dari Boundary

Prompt awal sebaiknya meminta alasan pemisahan, bukan hanya daftar service.

Anda adalah reviewer arsitektur untuk memecah aplikasi e-commerce menjadi microservices.

Konteks:
- Flow order sering berubah.
- Inventory harus bisa dirilis terpisah karena integrasi gudang.
- Payment dan notification tidak boleh memblokir katalog.

Keluarkan:
1. Kandidat service dan tanggung jawabnya.
2. Data yang dimiliki setiap service.
3. Interaksi synchronous API.
4. Interaksi asynchronous event.
5. Minimum scope untuk sprint pertama dengan Docker Compose.

Aturan:
- Tidak ada shared database table.
- Nama tabel internal tidak muncul di API.
- Gateway tidak berisi business logic.

Rujukan resmi yang relevan: Microservices architecture guide, AKS microservices reference architecture, dan API Management gateway overview.

Kontrak API Dan Kepemilikan Data

Tulis kontrak sebelum handler.

openapi: 3.1.0
info:
  title: Order Service API
  version: 1.0.0
paths:
  /orders:
    post:
      summary: Create an order after reserving inventory
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [customerId, items]
              properties:
                customerId:
                  type: string
                items:
                  type: array
                  minItems: 1
                  items:
                    type: object
                    required: [sku, quantity]
                    properties:
                      sku:
                        type: string
                      quantity:
                        type: integer
                        minimum: 1
      responses:
        "201":
          description: Order accepted
        "409":
          description: Inventory could not be reserved
ServiceMemilikiBoleh memanggilTidak boleh
gatewaytidak ada data bisnisorder, inventorymenghitung stok atau diskon
order-serviceorders, order_itemsinventory API, order-eventsmembaca tabel inventory
inventory-servicestock, reservationsbelum adamembaca tabel orders
notification-servicedelivery logsorder-eventsmengubah status order

Jika satu layar butuh order dan stok, jangan JOIN lintas database service. Gunakan API composition, read model, search index, atau cache yang diisi oleh event.

Untuk review, simpan service-inventory.json kecil di repositori. Manusia menentukan boundary; Claude Code mengecek apakah perubahan melanggar boundary itu.

{
  "services": [
    {
      "name": "gateway",
      "owns": [],
      "mayCall": ["order-service", "inventory-service"],
      "mustNot": ["store business data", "calculate discounts"]
    },
    {
      "name": "order-service",
      "owns": ["orders", "order_items"],
      "mayCall": ["inventory-service"],
      "mustNot": ["read inventory tables directly"]
    },
    {
      "name": "inventory-service",
      "owns": ["stock", "reservations"],
      "mayCall": [],
      "mustNot": ["change order status"]
    }
  ],
  "releaseRules": [
    "no shared database tables",
    "public APIs hide internal table names",
    "every service has healthcheck, logs, tests, and rollback notes"
  ]
}

Contoh Lokal Yang Bisa Dijalankan

mkdir microservices-demo
cd microservices-demo
mkdir services
npm init -y
npm pkg set type=module
npm install express zod pino redis undici

compose.yaml:

services:
  gateway:
    image: node:22-alpine
    working_dir: /workspace
    command: node services/service.mjs
    environment:
      SERVICE: gateway
      PORT: 3000
      ORDER_URL: http://order-service:3000
      INVENTORY_URL: http://inventory-service:3000
    ports:
      - "8080:3000"
    volumes:
      - .:/workspace
    depends_on:
      - order-service
      - inventory-service
  order-service:
    image: node:22-alpine
    working_dir: /workspace
    command: node services/service.mjs
    environment:
      SERVICE: order
      PORT: 3000
      INVENTORY_URL: http://inventory-service:3000
      REDIS_URL: redis://redis:6379
    volumes:
      - .:/workspace
    depends_on:
      redis:
        condition: service_healthy
      inventory-service:
        condition: service_started
  inventory-service:
    image: node:22-alpine
    working_dir: /workspace
    command: node services/service.mjs
    environment:
      SERVICE: inventory
      PORT: 3000
    volumes:
      - .:/workspace
  redis:
    image: redis:7-alpine
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 10

services/service.mjs:

import express from "express";
import pino from "pino";
import { createClient } from "redis";
import { request } from "undici";
import { z } from "zod";
import { randomUUID } from "node:crypto";

const service = process.env.SERVICE ?? "inventory";
const port = Number(process.env.PORT ?? 3000);
const log = pino({ name: service });

function base(name) {
  const app = express();
  app.use(express.json());
  app.use((req, res, next) => {
    req.requestId = req.header("x-request-id") ?? randomUUID();
    res.setHeader("x-request-id", req.requestId);
    next();
  });
  app.get("/health", (_req, res) => res.json({ ok: true, service: name }));
  return app;
}

function inventory() {
  const app = base("inventory");
  const stock = new Map([["sku-1", 5], ["sku-2", 2]]);
  const Reserve = z.object({ sku: z.string().min(1), quantity: z.number().int().positive() });
  app.get("/inventory/:sku", (req, res) => res.json({ sku: req.params.sku, quantity: stock.get(req.params.sku) ?? 0 }));
  app.post("/inventory/reservations", (req, res) => {
    const parsed = Reserve.safeParse(req.body);
    if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() });
    const available = stock.get(parsed.data.sku) ?? 0;
    if (available < parsed.data.quantity) return res.status(409).json({ error: "insufficient_stock", available });
    stock.set(parsed.data.sku, available - parsed.data.quantity);
    log.info({ requestId: req.requestId, sku: parsed.data.sku }, "reserved");
    res.status(201).json({ sku: parsed.data.sku, remaining: stock.get(parsed.data.sku) });
  });
  app.listen(port, () => log.info({ port }, "inventory started"));
}

async function order() {
  const app = base("order");
  const redis = createClient({ url: process.env.REDIS_URL ?? "redis://localhost:6379" });
  await redis.connect();
  const Order = z.object({
    customerId: z.string().min(1),
    items: z.array(z.object({ sku: z.string().min(1), quantity: z.number().int().positive() })).min(1),
  });
  app.post("/orders", async (req, res) => {
    const parsed = Order.safeParse(req.body);
    if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() });
    for (const item of parsed.data.items) {
      const response = await request(`${process.env.INVENTORY_URL}/inventory/reservations`, {
        method: "POST",
        headers: { "content-type": "application/json", "x-request-id": req.requestId },
        body: JSON.stringify(item),
      });
      if (response.statusCode >= 400) return res.status(response.statusCode).json(await response.body.json());
    }
    const order = { id: randomUUID(), ...parsed.data, status: "accepted" };
    await redis.xAdd("order-events", "*", { type: "OrderAccepted", payload: JSON.stringify(order) });
    res.status(201).json(order);
  });
  app.listen(port, () => log.info({ port }, "order started"));
}

function gateway() {
  const app = base("gateway");
  async function forward(req, res, url) {
    const response = await request(url, {
      method: req.method,
      headers: { "content-type": "application/json", "x-request-id": req.requestId },
      body: req.method === "GET" ? undefined : JSON.stringify(req.body),
    });
    res.status(response.statusCode).send(await response.body.text());
  }
  app.post("/orders", (req, res) => forward(req, res, `${process.env.ORDER_URL}/orders`));
  app.get("/inventory/:sku", (req, res) => forward(req, res, `${process.env.INVENTORY_URL}/inventory/${encodeURIComponent(req.params.sku)}`));
  app.listen(port, () => log.info({ port }, "gateway started"));
}

if (service === "inventory") inventory();
else if (service === "order") await order();
else if (service === "gateway") gateway();

Jalankan:

docker compose up
curl http://localhost:8080/inventory/sku-1
curl -X POST http://localhost:8080/orders -H "content-type: application/json" -d '{"customerId":"cust-1","items":[{"sku":"sku-1","quantity":2}]}'
docker compose down

Kriteria Sebelum Go Live

Sebelum rilis, jangan hanya menghitung jumlah service. Pastikan setiap service punya pemilik data, kontrak API, contract test, endpoint health, structured logs, dan jalur rollback. Minta Claude Code mengubah satu alur bisnis menjadi checklist dan menandai bagian yang bisa di-retry atau harus ditangani manusia.

Observability, Testing, Dan Rollout

Sejak sprint pertama, sertakan x-request-id, structured logs, nama service, ID bisnis, /health, error rate, dan latency. Minta Claude Code mengecek kompatibilitas API, ownership migration, gateway yang bersih dari business logic, jalur 400/409/500, kegagalan Redis, feature flags, canary, dan rollback.

Use case yang cocok: e-commerce dengan order, inventory, payment, notification; B2B SaaS dengan billing, permission, audit log; platform konten dengan ingestion, transformasi, search, dan delivery. Pitfall umum: membagi berdasarkan tabel, shared database, shared domain library yang terlalu besar, business rule di gateway, dan rilis tanpa observability.

Kalau butuh snippet CLAUDE.md, checklist review, dan template kontrak API yang bisa dipakai ulang, mulai dari produk praktis ClaudeCodeLab.

Jika ingin meninjau boundary, kontrak API, Compose, dan monitoring pada proyek nyata, mulai dari Claude Code training and consultation dengan satu workflow bisnis dulu.

#Claude Code #microservices #API design #Docker Compose #observability #testing
Gratis

PDF gratis: cheatsheet Claude Code

Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.

Kami menjaga datamu dan tidak mengirim spam.

Masa

Tentang penulis

Masa

Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.