Dalam membangun microservices, memilih communication pattern yang tepat sangat krusial. Di artikel ini, saya akan share pengalaman menggunakan berbagai patterns di production.
1. REST API (Synchronous)
Kapan Digunakan?
- Communication antar services yang membutuhkan response langsung
- Public-facing APIs
- Simple CRUD operations
Contoh Implementation
// User Service
func GetUserHandler(w http.ResponseWriter, r *http.Request) {
userID := mux.Vars(r)["id"]
user, err := userService.GetByID(userID)
if err != nil {
http.Error(w, err.Error(), http.StatusNotFound)
return
}
json.NewEncoder(w).Encode(user)
}
Kelebihan
✅ Simple & familiar ✅ Human-readable ✅ Good tooling support ✅ Easy debugging
Kekurangan
❌ Verbose payloads ❌ No type safety ❌ Slower than binary protocols
2. gRPC (Synchronous)
Kapan Digunakan?
- Internal service-to-service communication
- High-performance requirements
- Strong typing needed
- Streaming data
Contoh Implementation
// user.proto
syntax = "proto3";
service UserService {
rpc GetUser(GetUserRequest) returns (User);
rpc StreamUsers(Empty) returns (stream User);
}
message User {
int64 id = 1;
string name = 2;
string email = 3;
}
// Server implementation
func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
user, err := s.userRepo.FindByID(req.Id)
if err != nil {
return nil, status.Error(codes.NotFound, "user not found")
}
return &pb.User{
Id: user.ID,
Name: user.Name,
Email: user.Email,
}, nil
}
Kelebihan
✅ High performance (binary protocol) ✅ Type safety with protobuf ✅ Built-in streaming ✅ Auto-generated client code
Kekurangan
❌ Steeper learning curve ❌ Less human-readable ❌ Browser support requires proxy
3. Message Broker (Asynchronous)
Kapan Digunakan?
- Event-driven architecture
- Tasks yang bisa diproses async
- Decoupling services
- Need for reliability & retry
Contoh dengan RabbitMQ
// Publisher
func PublishOrderCreated(order Order) error {
body, _ := json.Marshal(order)
err := channel.Publish(
"orders", // exchange
"order.created", // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "application/json",
Body: body,
},
)
return err
}
// Consumer
func ConsumeOrders() {
msgs, _ := channel.Consume(
"order-queue",
"",
false, // auto-ack
false,
false,
false,
nil,
)
for msg := range msgs {
var order Order
json.Unmarshal(msg.Body, &order)
if err := processOrder(order); err != nil {
msg.Nack(false, true) // requeue
continue
}
msg.Ack(false)
}
}
Kelebihan
✅ Loose coupling ✅ Better fault tolerance ✅ Load leveling ✅ Built-in retry & DLQ
Kekurangan
❌ Eventual consistency ❌ More complex debugging ❌ Additional infrastructure
Real-World Use Case
Di project Xetia.io, saya menggunakan kombinasi ketiganya:
REST API:
- Public APIs untuk mobile/web client
- Admin dashboard
gRPC:
- Communication antara Order Service → Inventory Service
- Real-time data sync between services
RabbitMQ:
- Order processing pipeline
- Email/notification dispatch
- Delivery status updates
Pattern Decision Matrix
| Kriteria | REST | gRPC | Message Broker |
|---|---|---|---|
| Speed | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| Simplicity | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
| Reliability | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Type Safety | ⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐ |
| Debugging | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
Kesimpulan
Tidak ada “best” pattern - semuanya tergantung use case Anda:
- REST: Default choice untuk simplicity
- gRPC: Ketika performance & type safety penting
- Message Broker: Untuk async processing & loose coupling
Kombinasi ketiganya seringkali menjadi solusi terbaik untuk system yang kompleks.
Yang penting: measure, monitor, and adapt berdasarkan kebutuhan actual Anda! 📊