Hello

Back to Blog
Featured

OrderFlow-Go: Learning Microservices with Go & RabbitMQ

backendmicroservicesevent-driven
OrderFlow-Go: Learning Microservices with Go & RabbitMQ

Overview

OrderFlow-Go is a learning project designed to understand microservice architecture and event-driven communication using Go and RabbitMQ.

This project simulates an Order Management System where each service operates independently, maintains its own database, and communicates asynchronously through a message broker.

The main focus of this project is not on business complexity, but rather on architecture, data flow, and inter-service communication.


Background & Motivation

In large-scale systems, synchronous inter-service communication can become a bottleneck and a single point of failure.

Through this project, I wanted to learn:

  • How to separate domains into independent services
  • How event-driven architecture enables loose coupling
  • Using RabbitMQ as a message broker
  • Integrating Midtrans as a payment gateway
  • Implementing clean architecture in Go

Tech Stack & Rationale

  • Go
    Chosen for its high performance, robust concurrency model, and suitability for backend services.

  • PostgreSQL
    Used as the primary database with a database-per-service approach to maintain data isolation.

  • RabbitMQ
    Acts as the message broker for asynchronous inter-service communication.

  • Midtrans
    Serves as the payment gateway for online payment processing.

  • REST API
    Used for client-to-service communication and data querying.


System Architecture

The system architecture follows an event-driven microservices pattern.

Services Overview

  • Order Service
    Responsible for order creation and management.

  • Payment Service
    Processes payments based on incoming order events.

  • Notification Service
    Manages user notifications based on received events.

Event Flow

  1. Client creates an order through the Order Service
  2. Order Service stores data and publishes an order.created event
type OrderCreatedEvent struct {
	OrderID     string
	Email       string
	TotalAmount float64
}
  1. Payment Service consumes the event and processes payment
  2. Payment Service calls Midtrans API
  3. Payment Service receives callback from Midtrans
  4. Payment Service publishes a payment.status.updated event
type PaymentStatusChangedEvent struct {
	PaymentStatus string
	PaymentID     uuid.UUID
	OrderID       uuid.UUID
	PaymentMethod string
}
  1. Order Service and Notification Service consume this event

This approach ensures that each service is not directly dependent on one another.


Key Features

  • Microservice architecture with separate databases
  • Event-driven communication using RabbitMQ
  • Clean architecture (controller, usecase, repository)
  • Asynchronous processing for order workflows
  • REST API for data querying and management

Challenges & Solutions

Challenge 1: Service-to-Service Communication

Problem
Synchronous inter-service communication increases coupling and the risk of cascading failures.

Solution
Using RabbitMQ with a publish-subscribe approach.

Order Service simply publishes events without knowing who consumes them.

// Publish order.created event
publisher.Publish("order.created", payload)

Result
Services become more loosely coupled and scalable.

Challenge 2: Data Consistency Across Services

Problem
Each service has its own database, so there are no shared transactions.

Solution
Using eventual consistency:

  • Every status change is communicated through events
  • Each service is only responsible for its own domain

Trade-off
Data is not always consistent in real-time, but the system becomes more resilient.


Project Structure

Each service follows a consistent structure:

internal/
├── controllers   // HTTP handlers
├── routes        // API routes
├── usecase       // Business logic
├── repository    // Database access
├── models        // Domain models
├── broker        // RabbitMQ publisher / consumer

This structure keeps business logic independent from frameworks, making each service easier to test and evolve.


Verification Strategy

Testing is performed using an end-to-end approach:

  1. Create an order via Order Service
  2. Verify payment is created by Payment Service
  3. Verify notification is stored by Notification Service
  4. Monitor event flow through RabbitMQ Management UI

This approach ensures the entire workflow operates as designed.


Monitoring & Observability

  • RabbitMQ Management UI
    Used to monitor queues, message rates, and consumers.
  • Application Logs
    Each service logs:
    1. HTTP requests
    2. Database operations
    3. Event publishing & consumption
    4. Error handling

Trade-offs & Limitations

  • No distributed tracing yet
  • No retry mechanism or dead-letter queue
  • Basic security (no inter-service authentication)
  • Manual infrastructure (no Docker Compose / Kubernetes yet)

These limitations are intentional, as the project's primary focus is learning fundamental concepts.


Lessons Learned

Through this project, I learned:

  • How to think in the context of microservices
  • The importance of event-driven architecture for scalability
  • Trade-offs between consistency and availability
  • How to structure Go projects for maintainability

Future Improvements

  • Add retry mechanism and dead-letter queue
  • Implement saga pattern for distributed transactions
  • Add distributed tracing (OpenTelemetry)
  • Containerization with Docker Compose
  • Implement centralized logging

Conclusion

OrderFlow-Go helped me understand how large-scale backend systems are built using microservices and event-driven architecture.

This project serves as a foundation for further exploration into production-grade systems.


Links

© 2026 MUHAMMAD HABIB. BUILT WITH ❤️