Overview
The backend follows a Hexagonal Architecture (Ports & Adapters) pattern where infrastructure concerns are shared across all domain modules.Directory Structure
Shared Infrastructure Ports
1. TransactionManager (internal/shared/infrastructure/database/transaction.go)
Purpose: Provides database transaction support for atomic operations across modules.
internal/shared/infrastructure/database/postgresql/transaction.go (GORM)
Usage in Leave Module:
2. OutboxRepository (internal/shared/infrastructure/outbox/repository.go)
Purpose: Implements the Transactional Outbox pattern for reliable event publishing.
internal/shared/infrastructure/outbox/postgresql/outbox_repository.go
Usage Pattern (when needed in leave module):
Design Principles
✅ DO: Import from internal/shared/infrastructure
❌ DON’T: Duplicate port definitions in modules
✅ DO: Initialize in module.go
Benefits
- Single Source of Truth: Infrastructure interfaces defined once
- Consistency: All modules use the same transaction/outbox patterns
- Testability: Easy to mock shared infrastructure
- Maintainability: Changes to infrastructure affect all modules uniformly
- Reusability: No code duplication across modules
When to Use Shared vs Module-Specific
Shared Infrastructure (internal/shared/infrastructure/)
- Database transactions
- Outbox pattern
- Caching interfaces
- Message queue interfaces
- Authentication/Authorization (Better Auth integration)
Module-Specific (internal/modules/{module}/ports/)
- Domain repositories (LeaveRepository, UserRepository)
- Domain services (LeaveService, UserService)
- Module-specific business logic interfaces