Cara Handle Database Transactions Di Go
Pertanyaan yang sangat bagus dan fundamental dalam Clean Architecture! Ini adalah salah satu tantangan terbesar dalam mengimplementasikan CA dengan database transactions. Mari saya jelaskan secara lengkap berbagai pendekatan dan solusi terbaiknya.
1. Masalah Utama
Scenario yang Ingin Dicapai:
// service/order_service.go
type OrderService struct {
userRepo repository.UserRepository
productRepo repository.ProductRepository
orderRepo repository.OrderRepository
}
func (s *OrderService) CreateOrder(userID, productID int) error {
// 1. Check user exists
user, err := s.userRepo.FindByID(userID)
if err != nil {
return err
}
// 2. Check product exists and has stock
product, err := s.productRepo.FindByID(productID)
if err != nil {
return err
}
if product.Stock <= 0 {
return errors.New("product out of stock")
}
// 3. Create order
order := &Order{
UserID: userID,
ProductID: productID,
Status: "pending",
}
err = s.orderRepo.Create(order)
if err != nil {
return err
}
// 4. Update product stock
product.Stock--
err = s.productRepo.Update(product)
if err != nil {
return err
}
// 5. Update user's order count
user.OrderCount++
err = s.userRepo.Update(user)
if err != nil {
return err
}
return nil
}Masalah:
Jika step 4 gagal, step 3 sudah commit (tidak atomic)
Tidak bisa rollback semua operasi
Service tidak tahu tentang database transaction
Tidak boleh pass
*pgx.Txke service (melanggar CA)
2. Solusi 1: Unit of Work Pattern
Konsep:
Unit of Work (UoW) adalah pattern yang meng-track perubahan pada objects dan men-commit semuanya dalam satu transaction.
Implementasi:
a. Definisikan Unit of Work Interface
b. Implementasi Concrete UoW
c. Repository yang Support Transaction
d. Service dengan Unit of Work
e. Setup di Main
3. Solusi 2: Transaction Manager Pattern
Konsep:
Menggunakan Transaction Manager yang menghandle transaction boundaries tanpa service perlu tahu implementation details.
Implementasi:
a. Transaction Manager Interface
b. Implementasi Transaction Manager
c. Repository yang Aware Transaction
d. Service dengan Transaction Manager
4. Solusi 3: Domain Events Pattern
Konsep:
Menggunakan domain events untuk men-decouple operasi dan men-handle side effects dalam transaction.
Implementasi:
a. Domain Events Interface
b. Event Implementations
c. Event Handlers
d. Transactional Event Dispatcher
e. Service dengan Domain Events
5. Solusi 4: Command Pattern with Transaction Decorator
Konsep:
Menggunakan command pattern untuk encapsulate business logic dan decorators untuk cross-cutting concerns seperti transactions.
Implementasi:
a. Command Interface
b. Command Implementations
c. Transaction Decorator
d. Service dengan Command Pattern
6. Perbandingan Solusi
Unit of Work
- Explicit transaction control - Centralized transaction logic - Easy to understand
- More boilerplate - Tight coupling between repositories
Complex business logic with multiple repository calls
Transaction Manager
- Clean separation - Easy to test - Flexible
- Context pollution - Hidden transaction logic
Medium complexity, good balance
Domain Events
- Highly decoupled - Extensible - Good for CQRS
- Complex to implement - Eventual consistency concerns
Complex domains with many side effects
Command + Decorator
- Very clean - Easy to extend - Good for cross-cutting concerns
- Overhead for simple cases - Learning curve
Applications with many cross-cutting concerns
7. Rekomendasi: Hybrid Approach
Best Practice untuk Kebanyakan Aplikasi:
8. Best Practices Summary
✅ Do:
Use Transaction Manager untuk sebagian besar use case
Keep Business Logic in Service Layer - jangan taruh di repository
Use Domain Events untuk side effects dan decoupling
Handle Rollback Explicitly - selalu rollback pada error
Test Transaction Boundaries - test both success and failure scenarios
❌ Don't:
Pass Database Connections ke service layer
Mix Transaction Logic dengan business logic
Forget to Rollback pada error conditions
Use Global Transactions - buat transaction per request
Ignore Nested Transactions - pertimbangkan savepoints
🎯 Pattern Selection Guide:
Simple CRUD
Transaction Manager
Complex Business Logic
Unit of Work
Event-Driven Architecture
Domain Events
Cross-Cutting Concerns
Command + Decorator
Microservices
Transaction Manager + Domain Events
9. Final Implementation Example
Complete Transaction Handling:
Dengan pendekatan ini, Anda mendapatkan:
Clean Architecture - service tidak tahu implementation details
Proper Transaction Management - semua operasi atomic
Testability - mudah mock dan test
Maintainability - code yang terorganisir dan scalable
Flexibility - mudah extend dengan pattern lain
Ini adalah solusi yang paling robust dan scalable untuk kebanyakan aplikasi Go dengan Clean Architecture.
Last updated