Package vs Module: la distinción fundamental en Go

Un package es la unidad mínima de organización y compilación. Básicamente, es un directorio que contiene uno o varios archivos .go, donde todos deben declarar el mismo package al inicio. El compilador de Go procesa el código a nivel de paquete: cuando importas algo, el compilador busca ese directorio y compila todo su contenido como un solo bloque.

Un module es la unidad de distribución y versionado. Es un árbol completo de paquetes que se agrupa bajo una raíz definida por un archivo go.mod. Mientras que el paquete organiza tu lógica, el módulo define cómo se consume tu código desde fuera y qué versiones de otras librerías utiliza para ser reproducible.

¿Por qué Go separa estos conceptos? Para permitir que un proyecto crezca de forma modular sin perder el control de sus dependencias. Si todo fuera un solo paquete gigante, el versionado sería imposible; si cada carpeta fuera un módulo independiente, la resolución de dependencias sería un caos de archivos go.mod por todas partes. Debes usar paquetes para agrupar responsabilidades lógicas (como http o json) y módulos para definir el límite de tu proyecto o librería. Si intentas importar un paquete sin un módulo que lo contenga, el compilador no sabrá cómo resolver la ruta. Si confundes la ruta de importación con el nombre del paquete, crearás un código que, aunque compile, será una pesadilla de leer y mantener.

// Para que este ejemplo funcione, la estructura de archivos debe ser:
// /calculadora-app
// ├── go.mod
// ├── main.go
// └── math/
//     └── operations.go

// --- ARCHIVO: go.mod ---
// module example.com/calc-app

// --- ARCHIVO: math/operations.go ---
// package math
//
// // Suma devuelve la suma de dos enteros.
// // La mayúscula inicial la hace exportada (visible para otros paquetes).
// func Suma(a, b int) int {
//     return a + b
// }

// --- ARCHIVO: main.go ---
// package main
//
// import (
//     "fmt"
//     "example.com/calc-app/math" // [import path] = [module path] + [subdirectorio]
// )
//
// func main() {
//     // Accedemos a la función usando el nombre del paquete
//     resultado := math.Suma(10, 5)
//     fmt.Printf("El resultado es: %d\n", resultado)
// }

Desglose del ejemplo

El funcionamiento de este código se rige por la jerarquía definida en el archivo go.mod. El nombre del módulo es example.com/calc-app; este es el prefijo base para cualquier importación interna.

Cuando en main.go escribimos import "example.com/calc-app/math", le estamos diciendo al compilador: “busca dentro de mi módulo la carpeta llamada math“. El compilador localiza la carpeta math/, lee los archivos que contienen package math y prepara sus funciones para ser usadas.

Fíjate en la llamada math.Suma(10, 5). Aquí ocurre la distinción crucial: math es el nombre del paquete (declarado dentro del archivo .go), mientras que la ruta completa usada en el import es la ruta de importación. Para acceder a los miembros, siempre usas el nombre del paquete, no la ruta de archivos.

El error frecuente

Un error común es nombrar la carpeta de una forma y el paquete de otra. Aunque Go lo permite, rompe la convención y la intuición de otros desarrolladores.

// Estructura:
// /proyecto
// ├── go.mod (module example.com/app)
// └── logic/
//     └── calculator.go (package calc) <--- ¡ERROR DE CONVENCIÓN!

// En main.go:
import "example.com/app/logic"

func main() {
    // El compilador buscará "logic", pero tendrás que usar "calc" para acceder
    result := calc.Add(1, 2) 
}

En este caso, el import usa el nombre del directorio (logic), pero el código utiliza el nombre del paquete (calc). Esto genera una carga cognitiva innecesaria: el desarrollador busca el código en la carpeta logic, pero el autocompletado del IDE le sugiere calc. Mantén siempre el nombre del directorio y el package declarado en sincronía.

97

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio