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 / 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:
- 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
