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
- Client creates an order through the Order Service
- Order Service stores data and publishes an
order.createdevent
type OrderCreatedEvent struct {
OrderID string
Email string
TotalAmount float64
}- Payment Service consumes the event and processes payment
- Payment Service calls Midtrans API
- Payment Service receives callback from Midtrans
- Payment Service publishes a
payment.status.updatedevent
type PaymentStatusChangedEvent struct {
PaymentStatus string
PaymentID uuid.UUID
OrderID uuid.UUID
PaymentMethod string
}- 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 / consumerThis 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:
- Create an order via Order Service
- Verify payment is created by Payment Service
- Verify notification is stored by Notification Service
- 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:- HTTP requests
- Database operations
- Event publishing & consumption
- 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
- GitHub Repository
https://github.com/Mhabib34/orderflow-go