Bounded Contexts Not Borders: Why Microservices Are a Misnomer

작성자

카테고리:

← 피드로
DEV Community · Puneet Khandelwal · 2026-08-09 개발(SW)

Puneet Khandelwal

I’ve spent the last decade preaching microservices, but I was wrong. What we call microservices usually means a bunch of loosely coupled and independently deployable units that are actually a misnomer. We need to focus on true functional boundaries instead of arbitrary borders.

The problem starts with how we build them. We draw lines based on technical convenience, org charts, or vague requirements. Systems balloon with unnecessary complexity. Bounded contexts reflect the actual domain.

Take an e-commerce app. Order management, inventory tracking, and payment processing look like natural split points. Then they start talking to each other directly. Order management calls inventory, which checks payment status. You get a distributed monolith that is harder to debug than a clean single-binary app.

Here is a basic Go setup before splitting:

// monolithic.go
package main

import (
    "net/http"

    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()

    // Order management
    r.HandleFunc("/orders", handleOrders).Methods("GET")

    // Inventory tracking
    r.HandleFunc("/inventory", handleInventory).Methods("GET")

    // Payment processing
    r.HandleFunc("/payment", handlePayment).Methods("POST")

    http.ListenAndServe(":8080", r)
}

Enter fullscreen mode Exit fullscreen mode

Now look at what happens when we split by technical convenience:

// orderService.go
package main

import (
    "net/http"

    "github.com/gorilla/mux"
)

func handleOrders(w http.ResponseWriter, r *http.Request) {
    inventoryService := newInventoryService()
    levels, err := inventoryService.GetLevels()
    if err!= nil {
 http.Error(w, err.Error(), http.StatusInternalServerError)
 return
    }

    paymentService := newPaymentService()
    status, err := paymentService.GetStatus()
    if err!= nil {
 http.Error(w, err.Error(), http.StatusInternalServerError)
 return
    }

    w.Write([]byte(`{"orders": []}`))
}

// inventoryService.go
package main

func handleInventory(w http.ResponseWriter, r *http.Request) {
    paymentService := newPaymentService()
    status, err := paymentService.GetStatus()
    if err!= nil {
 http.Error(w, err.Error(), http.StatusInternalServerError)
 return
    }

    w.Write([]byte(`{"levels": []}`))
}

// paymentService.go
package main

func handlePayment(w http.ResponseWriter, r *http.Request) {
    orderService := newOrderService()
    status, err := orderService.GetStatus()
    if err!= nil {
 http.Error(w, err.Error(), http.StatusInternalServerError)
 return
    }

    w.Write([]byte(`{"status": "success"}`))
}

Enter fullscreen mode Exit fullscreen mode

Every service relies on the others. Coupling creeps back in.

Bounded contexts fix this by matching domain logic. Keep order management, inventory, and payments separate, but clean up the chatty network calls.

Focus on real domain boundaries. Systems become easier to scale and maintain when you stop carving arbitrary lines.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다