Estructura de proyectos en Go: Prácticas y patrones
Estructura tus proyectos de Go para escalabilidad y claridad
Estructurar un proyecto de Go](https://www.glukhov.org/es/app-architecture/code-architecture/go-project-structure/ “Estructurar un proyecto de Go”) de manera efectiva es fundamental para la mantenibilidad a largo plazo, la colaboración en equipo y la escalabilidad. A diferencia de los frameworks que imponen estructuras de directorios rígidas, Go abraza la flexibilidad; pero con esa libertad viene la responsabilidad de elegir patrones que satisfagan las necesidades específicas de tu proyecto.

Comprender la filosofía de Go sobre la estructura de proyectos
La filosofía de diseño minimalista de Go se extiende a la organización de proyectos. El lenguaje no impone una estructura específica, confiando en cambio en que los desarrolladores tomen decisiones informadas. Este enfoque ha llevado a la comunidad a desarrollar varios patrones probados, desde estructuras planas simples para proyectos pequeños hasta arquitecturas sofisticadas para sistemas empresariales.
El principio clave es simplicidad primero, complejidad cuando sea necesaria. Muchos desarrolladores caen en la trampa de sobreingeniería en su estructura inicial, creando directorios profundamente anidados y abstracciones prematuras. Comienza con lo que necesitas hoy y refactoriza a medida que tu proyecto crece.
¿Cuándo debo usar el directorio internal/ en lugar de pkg/?
El directorio internal/ sirve un propósito específico en el diseño de Go: contiene paquetes que no pueden ser importados por proyectos externos. El compilador de Go aplica esta restricción, lo que hace que internal/ sea perfecto para lógica de aplicación privada, reglas de negocio y utilidades no destinadas al reutilización fuera de tu proyecto.
El directorio pkg/, por otro lado, indica que el código está destinado para consumo externo. Solo coloca código aquí si estás construyendo una biblioteca o componentes reutilizables que quieras que otros importen. Muchos proyectos no necesitan pkg/ en absoluto; si tu código no está siendo consumido externamente, mantenerlo en la raíz o en internal/ es más limpio. Al construir bibliotecas reutilizables, considera aprovechar los genéricos de Go para crear componentes reutilizables y seguros en tipos.
La estructura estándar de proyectos de Go
El patrón más ampliamente reconocido es golang-standards/project-layout, aunque es importante notar que esto no es un estándar oficial. Así es como se ve una estructura típica:
myproject/
├── cmd/
│ ├── api/
│ │ └── main.go
│ └── worker/
│ └── main.go
├── internal/
│ ├── auth/
│ ├── storage/
│ └── transport/
├── pkg/
│ ├── logger/
│ └── crypto/
├── api/
│ └── openapi.yaml
├── config/
│ └── config.yaml
├── scripts/
│ └── deploy.sh
├── go.mod
├── go.sum
└── README.md
Muchos equipos mantienen un pequeño paquete de registro compartido bajo internal/ (por ejemplo internal/logx) para que los binarios en cmd/ conecten un registrador al inicio; pkg/logger/ en el esquema anterior es apropiado solo cuando ese código está destinado a ser importado por otros módulos. Para una configuración de log/slog orientada a producción (líneas JSON, redacción, campos de contexto y traza, y señales que funcionan bien con pilas de monitoreo), consulta Registro Estructurado en Go con slog para Observabilidad y Alertas.
El directorio cmd/
El directorio cmd/ contiene los puntos de entrada de tu aplicación. Cada subdirectorio representa un binario ejecutable separado. Por ejemplo, cmd/api/main.go compila tu servidor de API, mientras que cmd/worker/main.go podría compilar un procesador de trabajos en segundo plano.
Mejor práctica: Mantén tus archivos main.go mínimos; solo lo suficiente para conectar dependencias, cargar la configuración e iniciar la aplicación. Toda la lógica sustantiva pertenece a paquetes que main.go importa.
// cmd/api/main.go
package main
import (
"log"
"myproject/internal/server"
"myproject/internal/config"
)
func main() {
cfg, err := config.Load()
if err != nil {
log.Fatal(err)
}
srv := server.New(cfg)
if err := srv.Start(); err != nil {
log.Fatal(err)
}
}
El directorio internal/
Aquí es donde reside tu código de aplicación privada. El compilador de Go impide que cualquier proyecto externo importe paquetes dentro de internal/, lo que lo hace ideal para:
- Lógica de negocio y modelos de dominio
- Servicios de aplicación
- APIs e interfaces internas
- Repositorios de base de datos (para elegir el ORM adecuado, consulta nuestra comparación de ORMs de Go para PostgreSQL)
- Lógica de autenticación y autorización
Organiza internal/ por característica o dominio, no por capa técnica. En lugar de internal/handlers/, internal/services/, internal/repositories/, prefiere internal/user/, internal/order/, internal/payment/ donde cada paquete contenga sus manejadores, servicios y repositorios.
¿Debería comenzar con una estructura de directorios compleja?
Absolutamente no. Si estás construyendo una pequeña herramienta o prototipo, comienza con:
myproject/
├── main.go
├── go.mod
└── go.sum
A medida que tu proyecto crece y identificas agrupaciones lógicas, introduce directorios. Podrías agregar un paquete db/ cuando la lógica de base de datos se vuelva sustancial, o un paquete api/ cuando los manejadores HTTP se multipliquen. Deja que la estructura surja naturalmente en lugar de imponerla desde el principio.
Estructuras planas vs. anidadas: Encontrando el equilibrio
Uno de los errores más comunes en la estructura de proyectos de Go es el anidamiento excesivo. Go favorece jerarquías superficiales; típicamente uno o dos niveles de profundidad. El anidamiento profundo aumenta la carga cognitiva y hace que las importaciones sean engorrosas.
¿Cuáles son los errores más comunes?
Anidamiento excesivo de directorios: Evita estructuras como internal/services/user/handlers/http/v1/. Esto crea complejidad de navegación innecesaria. En su lugar, usa internal/user/handler.go.
Nombres de paquetes genéricos: Nombres como utils, helpers, common o base son olores de código. No transmiten funcionalidad específica y a menudo se convierten en vertederos para código no relacionado. Usa nombres descriptivos: validator, auth, storage, cache.
Dependencias circulares: Cuando el paquete A importa el paquete B, y B importa A, tienes una dependencia circular; un error de compilación en Go. Esto generalmente señala una mala separación de responsabilidades. Introduce interfaces o extrae tipos compartidos a un paquete separado.
Mezcla de responsabilidades: Mantén los manejadores HTTP enfocados en preocupaciones HTTP, los repositorios de base de datos en el acceso a datos y la lógica de negocio en paquetes de servicios. Colocar reglas de negocio en manejadores dificulta las pruebas y acopla tu lógica de dominio a HTTP.
Diseño Guiado por Dominio y Arquitectura Hexagonal
Para aplicaciones más grandes, especialmente microservicios, el Diseño Guiado por Dominio (DDD) con Arquitectura Hexagonal proporciona una clara separación de responsabilidades.
¿Cómo estructuro un microservicio siguiendo el Diseño Guiado por Dominio?
La Arquitectura Hexagonal organiza el código en capas concéntricas con dependencias fluyendo hacia adentro:
internal/
├── domain/
│ └── user/
│ ├── entity.go # Modelos de dominio y objetos de valor
│ ├── repository.go # Interfaz de repositorio (puerto)
│ └── service.go # Servicios de dominio
├── application/
│ └── user/
│ ├── create_user.go # Caso de uso: crear usuario
│ ├── get_user.go # Caso de uso: recuperar usuario
│ └── service.go # Orquestación de servicios de aplicación
├── adapter/
│ ├── http/
│ │ └── user_handler.go # Adaptador HTTP (endpoints REST)
│ ├── postgres/
│ │ └── user_repo.go # Adaptador de base de datos (implementa el puerto del repositorio)
│ └── redis/
│ └── cache.go # Adaptador de caché
└── api/
└── http/
└── router.go # Configuración de rutas y middleware
Capa de dominio (domain/): Lógica de negocio central, entidades, objetos de valor e interfaces de servicios de dominio. Esta capa no tiene dependencias de sistemas externos; sin importaciones de HTTP ni de base de datos. Define interfaces de repositorio (puertos) que los adaptadores implementan.
Capa de aplicación (application/): Casos de uso que orquestan objetos de dominio. Cada caso de uso (por ejemplo, “crear usuario”, “procesar pago”) es un archivo o paquete separado. Esta capa coordina objetos de dominio pero no contiene reglas de negocio en sí misma.
Capa de adaptador (adapter/): Implementa interfaces definidas por las capas internas. Los manejadores HTTP convierten solicitudes en objetos de dominio, los repositorios de base de datos implementan persistencia y las colas de mensajes manejan la comunicación asíncrona. Esta capa contiene todo el código específico de framework e infraestructura.
Capa de API (api/): Rutas, middleware, DTOs (Objetos de Transferencia de Datos), versionado de API y especificaciones OpenAPI.
Esta estructura asegura que tu lógica de negocio central permanezca probable e independiente de frameworks, bases de datos o servicios externos. Puedes intercambiar PostgreSQL por MongoDB, o REST por gRPC, sin tocar el código de dominio. Si adoptas CQRS dentro de esta estructura, Implementando CQRS en Go muestra cómo la capa application/ se mapea naturalmente a paquetes separados de manejadores de comandos y consultas, manteniendo el lado de comandos estricto y el lado de consultas orientado a DTOs.
Patrones prácticos para diferentes tipos de proyectos
Herramienta CLI pequeña
Para aplicaciones de línea de comandos, querrás una estructura que soporte múltiples comandos y subcomandos. Considera usar frameworks como Cobra para la estructura de comandos y Viper para la gestión de configuración. Nuestra guía sobre construir aplicaciones CLI en Go con Cobra y Viper cubre este patrón en detalle.
mytool/
├── main.go
├── command/
│ ├── root.go
│ └── version.go
├── go.mod
└── README.md
Servicio de API REST
Al construir APIs REST en Go, esta estructura separa claramente las responsabilidades: los manejadores manejan preocupaciones HTTP, los servicios contienen la lógica de negocio y los repositorios gestionan el acceso a datos. Para una guía completa que cubre enfoques de biblioteca estándar, frameworks, autenticación, patrones de prueba y mejores prácticas listas para producción, consulta nuestra guía completa para construir APIs REST en Go. Para registro JSON estructurado, correlación de solicitudes y traza, y formas de registro que soportan alertas, consulta Registro Estructurado en Go con slog para Observabilidad y Alertas.
myapi/
├── cmd/
│ └── api/
│ └── main.go
├── internal/
│ ├── config/
│ ├── middleware/
│ ├── user/
│ │ ├── handler.go
│ │ ├── service.go
│ │ └── repository.go
│ └── product/
│ ├── handler.go
│ ├── service.go
│ └── repository.go
├── pkg/
│ └── httputil/
├── go.mod
└── README.md
Monorepo con múltiples servicios
myproject/
├── cmd/
│ ├── api/
│ ├── worker/
│ └── scheduler/
├── internal/
│ ├── shared/ # Paquetes internos compartidos
│ ├── api/ # Código específico de API
│ ├── worker/ # Código específico de worker
│ └── scheduler/ # Código específico de scheduler
├── pkg/ # Bibliotecas compartidas
├── go.work # Archivo de espacio de trabajo de Go
└── README.md
Pruebas y Documentación
Coloca los archivos de prueba junto al código que prueban usando el sufijo _test.go:
internal/
└── user/
├── service.go
├── service_test.go
├── repository.go
└── repository_test.go
Esta convención mantiene las pruebas cerca de la implementación, haciéndolas fáciles de encontrar y mantener. Para pruebas de integración que tocan múltiples paquetes, crea un directorio test/ separado en la raíz del proyecto. Para orientación completa sobre cómo escribir pruebas unitarias efectivas, incluidas pruebas impulsadas por tablas, mocks, análisis de cobertura y mejores prácticas, consulta nuestra guía sobre estructura y mejores prácticas de pruebas unitarias en Go.
La documentación pertenece en:
README.md: Resumen del proyecto, instrucciones de configuración, uso básicodocs/: Documentación detallada, decisiones de arquitectura, referencias de APIapi/: Especificaciones OpenAPI/Swagger, definiciones de protobuf
Para APIs REST, generar y servir documentación OpenAPI con Swagger es esencial para la descubribilidad de la API y la experiencia del desarrollador. Nuestra guía sobre agregar Swagger a tu API de Go cubre la integración con frameworks populares y mejores prácticas.
Gestionando dependencias con Go Modules
Cada proyecto de Go debería usar Go Modules para la gestión de dependencias. Para una referencia completa sobre comandos de Go y gestión de módulos, consulta nuestro Cheat Sheet de Go. Inicializa con:
go mod init github.com/yourusername/myproject
Esto crea go.mod (dependencias y versiones) y go.sum (checksums para verificación). Mantén estos archivos en control de versiones para construcciones reproducibles.
Actualiza las dependencias regularmente:
go get -u ./... # Actualizar todas las dependencias
go mod tidy # Eliminar dependencias no utilizadas
go mod verify # Verificar checksums
Principales conclusiones
-
Comienza simple, evoluciona naturalmente: No sobreingenieries tu estructura inicial. Agrega directorios y paquetes cuando la complejidad lo requiera.
-
Favorece jerarquías planas: Limita el anidamiento a uno o dos niveles. La estructura plana de paquetes de Go mejora la legibilidad.
-
Usa nombres de paquetes descriptivos: Evita nombres genéricos como
utils. Nombra los paquetes según lo que hacen:auth,storage,validator. -
Separa claramente las responsabilidades: Mantén los manejadores enfocados en HTTP, los repositorios en el acceso a datos y la lógica de negocio en paquetes de servicios.
-
Aprovecha
internal/para privacidad: Úsalo para código que no debería ser importado externamente. La mayor parte del código de la aplicación pertenece aquí. -
Aplica patrones de arquitectura cuando sea necesario: Para sistemas complejos, la Arquitectura Hexagonal y DDD proporcionan límites claros y testabilidad.
-
Deja que Go te guíe: Sigue los idioms de Go en lugar de importar patrones de otros lenguajes. Go tiene su propia filosofía sobre simplicidad y organización.
Enlaces útiles
- Cheat Sheet de Go
- Construyendo APIs REST en Go: Guía Completa
- Construyendo Aplicaciones CLI en Go con Cobra y Viper
- Pruebas Unitarias en Go: Estructura y Mejores Prácticas
- Registro Estructurado en Go con slog para Observabilidad y Alertas
- Agregando Swagger a tu API de Go
- Genéricos en Go: Casos de Uso y Patrones
- Comparando ORMs de Go para PostgreSQL: GORM vs Ent vs Bun vs sqlc
Otros Artículos Relacionados
- Estructura Estándar de Proyectos Go - Directrices de estructura de proyecto impulsadas por la comunidad
- Referencia de Módulos Go - Documentación oficial de módulos de Go
- Arquitectura Hexagonal en Go - Framework de arquitectura hexagonal de grado empresarial
- Diseño Guiado por Dominio en Go - Implementación DDD lista para producción
- Convenciones de Estructura de Proyectos Go - Patrones y ejemplos adicionales
- Effective Go - Guía oficial de mejores prácticas de Go
- Centro de Arquitectura de Aplicaciones — Diseño de API, estructura de código y patrones de integración