← Back to Cecep Azhar
Cecep Azhar #microservices#backend#architecture#golang#distributed-systems

Microservices Architecture: Panduan Lengkap untuk Backend Developer Indonesia

Cecep Saeful Azhar • 16 Oct 2026
Microservices Architecture: Panduan Lengkap untuk Backend Developer Indonesia
Panduan lengkap microservices architecture untuk backend developer Indonesia. Arsitektur, best practices, dan deployment strategy dari pengalaman production.

Monolith vs Microservices: Kapan Harus Split?

Pertanyaan yang paling sering saya dapat dari developer Indonesia: 'Kapan harus pindah dari monolith ke microservices?' Jawaban saya selalu: jangan terburu-buru. Dari pengalaman saya bekerja di beberapa startup di Bandung, monolith yang well-structured seringkali lebih baik daripada microservices yang poorly designed.

Microservices menjadi worth it ketika: tim sudah cukup besar (10+ developer), ada bagian sistem yang perlu di-scale secara independen, atau ada requirement untuk technology diversity di berbagai service.

Designing Service Boundaries

Hal paling kritis dalam microservices adalah menarik batas antar service dengan benar. Saya menggunakan Domain-Driven Design (DDD) sebagai panduan:

// Service boundaries berdasarkan domain

// Order Service - menangani semua terkait pesanan
type OrderService struct {
    repository OrderRepository
    eventBus   EventBus
    client     *http.Client
}

func (s *OrderService) CreateOrder(ctx context.Context, req CreateOrderRequest) (*Order, error) {
    // Validasi stok via Inventory Service
    stock, err := s.client.CheckStock(ctx, req.ProductID, req.Quantity)
    if err != nil {
        return nil, fmt.Errorf("failed to check stock: %w", err)
    }
    
    // Buat order
    order := &Order{
        ID:        uuid.New(),
        ProductID: req.ProductID,
        Quantity:  req.Quantity,
        Status:    OrderStatusPending,
        CreatedAt: time.Now(),
    }
    
    if err := s.repository.Save(ctx, order); err != nil {
        return nil, err
    }
    
    // Publish event untuk service lain
    s.eventBus.Publish(OrderCreatedEvent{
        OrderID: order.ID,
        Amount:  stock.Price * float64(req.Quantity),
    })
    
    return order, nil
}

Inter-Service Communication

Ada dua pola komunikasi utama: synchronous (REST/gRPC) dan asynchronous (message queue). Saya menyarankan menggunakan synchronous untuk queries dan asynchronous untuk commands:

// Synchronous: REST untuk query
func (c *ProductClient) GetProduct(ctx context.Context, id string) (*Product, error) {
    resp, err := c.client.Get(fmt.Sprintf("%s/products/%s", c.baseURL, id))
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    var product Product
    json.NewDecoder(resp.Body).Decode(&product)
    return &product, nil
}

// Asynchronous: Event untuk commands
type OrderCreatedEvent struct {
    OrderID string  `json:"order_id"`
    Amount  float64 `json:"amount"`
}

// Inventory Service listens
eventBus.Subscribe("order.created", func(event OrderCreatedEvent) error {
    return inventoryService.ReserveStock(event.OrderID)
})

Deployment Strategy

Untuk deployment, saya menggunakan Kubernetes dengan rolling update strategy. Setiap service di-deploy secara independen dan bisa di-scale sesuai kebutuhan. Docker Compose untuk development environment, Kubernetes untuk staging dan production.

Yang paling penting: implement proper circuit breaker dan retry mechanism untuk inter-service calls. Di distributed system, network failure adalah hal yang pasti terjadi, bukan kemungkinan.

Monitoring dan Debugging

Monitoring di microservices lebih challenging karena ada banyak service yang saling berkomunikasi. Saya implement distributed tracing dengan Jaeger dan centralized logging dengan ELK stack. Dengan tracing, kita bisa melihat bagaimana request mengalir dari satu service ke service lain, dan dengan cepat menemukan bottleneck atau error.

← More articles from Cecep Azhar Central Content Hub Home →