Template de proyecto para construir APIs REST escalables y mantenibles usando arquitectura hexagonal (puertos y adaptadores) en Go.
Este proyecto implementa una API REST siguiendo los principios de arquitectura hexagonal (también conocida como arquitectura de puertos y adaptadores). Esta arquitectura permite desacoplar la lógica de negocio de los detalles técnicos, facilitando testing, mantenibilidad y escalabilidad.
templateApiRestGo/
├── main.go # Punto de entrada del programa
├── go.mod # Definición del módulo Go
├── go.sum # Checksums de dependencias
├── README.md # Este archivo
│
└── internal/
├── application/ # Capa de aplicación (lógica de negocio)
│ └── user_service.go # Servicios y puertos (interfaces)
│
├── domain/ # Capa de dominio (modelos)
│ └── user_model.go # Entidades del negocio
│
├── infrastructure/ # Adaptadores técnicos (detalles)
│ ├── db/ # Adaptador de base de datos
│ │ ├── conecction_mongo_client.go
│ │ └── mongo_user_repository.go
│ │
│ ├── http/ # Adaptador HTTP (API REST)
│ │ ├── router.go # Definición de rutas
│ │ └── user_handler.go # Handlers HTTP
│ │
│ └── repository/ # Implementaciones de repositorios
│ └── mongo_user_repository.go
│
└── bootstrap/ # Contenedor de dependencias
└── container.go # DI manual (inyección de dependencias)
- Contiene los modelos de negocio (entidades)
- No tiene dependencias externas
- Define la esencia del negocio
- Ejemplo:
Usermodel
// internal/domain/user_model.go
type User struct {
ID string
Name string
Email string
}- Contiene la lógica de negocio y los puertos (interfaces)
- No conoce cómo se implementan las interfaces (independiente de tecnología)
- Define qué operaciones puede hacer el sistema
- Ejemplo:
UserServiceyUserRepositoryinterface
// internal/application/user_service.go
type UserRepository interface {
Create(user *User) error
GetByID(id string) (*User, error)
}
type UserService struct {
repo UserRepository
}
func (s *UserService) CreateUser(user *User) error {
// Lógica de negocio aquí
return s.repo.Create(user)
}- Contiene los adaptadores (implementaciones concretas)
- Código específico de tecnología (MongoDB, HTTP, etc.)
- Se conecta con sistemas externos
- Subcapas:
db/: Adaptador de base de datoshttp/: Adaptador HTTP (REST API)repository/: Implementaciones de repositorios
// internal/infrastructure/repository/mongo_user_repository.go
type MongoUserRepository struct {
client *db.MongoClient
}
func (r *MongoUserRepository) Create(user *domain.User) error {
collection := r.client.Database.Collection("users")
_, err := collection.InsertOne(context.Background(), user)
return err
}- Contenedor de dependencias (DI manual)
- Crea todas las instancias y conecta las dependencias
- Configura toda la aplicación
// internal/bootstrap/container.go
func NewContainer() *Container {
mongoClient, err := db.NewMongoClient(uri, "testdb")
userRepo := repository.NewMongoUserRepository(mongoClient)
userService := application.NewUserService(userRepo)
userHandler := http.NewUserHandler(userService)
return &Container{...}
}Los puertos son contratos que definen cómo interactúa el sistema:
// Puerto de salida: cómo persiste datos
type UserRepository interface {
Create(user *User) error
GetByID(id string) (*User, error)
}
// Puerto de entrada: cómo recibe solicitudes HTTP
// Implementado por UserHandlerLos adaptadores conectan los puertos con tecnologías concretas:
- Adaptador HTTP: Convierte requests HTTP → dominio
- Adaptador MongoDB: Convierte dominio → documentos MongoDB
- Adaptador PostgreSQL (futuro): Alternativa a MongoDB
- Go 1.24 o superior
- MongoDB local o remoto
- Git
- Clonar o usar como template
git clone <repo>
cd templateApiRestGo- Instalar dependencias
go mod download- Configurar MongoDB
Actualiza la URI de conexión en
internal/bootstrap/container.go:
mongoClient, err := db.NewMongoClient(
"mongodb://localhost:27017", // ← Cambia aquí
"testdb",
)- Ejecutar la aplicación
go run main.goLa API estará disponible en http://localhost:3001
POST /users
Content-Type: application/json
{
"id": "user123",
"name": "John Doe",
"email": "john@example.com"
}Respuesta (201)
{
"message": "Usuario creado correctamente",
"user": {
"id": "user123",
"name": "John Doe",
"email": "john@example.com"
}
}HTTP Request
↓
UserHandler (Adaptador HTTP)
↓
UserService (Lógica de negocio)
↓
UserRepository (Puerto - interfaz)
↓
MongoUserRepository (Adaptador MongoDB)
↓
MongoDB Database
✨ Independencia de Frameworks: Cambiar Fiber por otro framework es fácil
✨ Independencia de Base de Datos: Cambiar MongoDB por PostgreSQL sin tocar la lógica
✨ Testing Facilitado: Mock fácil de interfaces
✨ Escalabilidad: Fácil agregar nuevas funcionalidades
✨ Mantenibilidad: Código organizado y desacoplado
✨ Reutilización: Lógica no depende de detalles técnicos
// test: Mock del repositorio
type MockUserRepository struct{}
func (m *MockUserRepository) Create(user *domain.User) error {
return nil // Simular éxito
}
func TestCreateUser(t *testing.T) {
mockRepo := &MockUserRepository{}
service := application.NewUserService(mockRepo)
user := &domain.User{ID: "1", Name: "Test"}
err := service.CreateUser(user)
if err != nil {
t.Fail()
}
}go test ./... -vgithub.com/gofiber/fiber/v2 → Framework HTTP (Adaptador)
go.mongodb.org/mongo-driver → Driver MongoDB (Adaptador)
Ver go.mod para versiones exactas.
- Crear
internal/infrastructure/postgres/postgres_user_repository.go
type PostgresUserRepository struct {
db *sql.DB
}
func (r *PostgresUserRepository) Create(user *domain.User) error {
// Implementación PostgreSQL
}- Actualizar
container.gopara usar PostgreSQL en lugar de MongoDB
No requiere cambios en: Domain, Application, HTTP layers ✨
- Crear método en
UserHandler - Registrar ruta en
router.go - Listo (sin cambios en lógica)
Para agregar mejoras a este template:
- Mantén la estructura de carpetas
- Respeta las capas (no hagas imports circulares)
- Documenta cambios importantes
Este template es de código abierto y disponible bajo licencia MIT.
Creado como template para proyectos Go con arquitectura hexagonal