Naming idiomático de paquetes en Go

El nombre de un package en Go no es una simple etiqueta de organización; es el calificador de cada símbolo que exportas. Cuando escribes json.Marshal o http.Get, el nombre del paquete le da contexto al método sin necesidad de ser repetitivo. Esta convención existe para maximizar la legibilidad y evitar el stuttering o redundancia de nombres, permitiendo que el código fluya de forma natural. Debes aplicar este principio siempre que definas una nueva unidad de lógica para que tu API sea predecible y fácil de descubrir mediante el autocompletado. Si ignoras esto y optas por nombres genéricos como util, common o helpers, terminarás con un “vertedero de código” (junk drawer) que carece de cohesión; un paquete llamado util es, casi siempre, la señal de que las responsabilidades de tus módulos no han sido bien definidas y que el paquete está intentando “hacer cosas” en lugar de “proveer capacidades” específicas.

package main

import (
	"fmt"
)

// --- Simulación de un paquete llamado 'payment' ---
// En un proyecto real, esto viviría en un directorio llamado /payment.

// Client representa la conexión con una pasarela de pagos.
// Idiomático: El tipo es 'Client', no 'PaymentClient'.
// El paquete 'payment' ya proporciona el contexto necesario.
type Client struct {
	apiKey string
}

// New es el constructor estándar para este paquete.
// Uso: payment.New("key") -> Limpio y directo.
// Evitar: payment.NewPaymentClient("key") -> Redundancia innecesaria (stuttering).
func New(apiKey string) *Client {
	return &Client{apiKey: apiKey}
}

// Charge procesa un cobro.
// Uso: client.Charge(10.0)
// Evitar: client.ChargePayment(10.0) -> El método no necesita repetir el nombre del paquete.
func (c *Client) Charge(amount float64) error {
	if c.apiKey == "" {
		return fmt.Errorf("la apiKey es obligatoria")
	}
	fmt.Printf("Ejecutando cobro de $%.2f con la llave %s\n", amount, c.apiKey)
	return nil
}

// --- Programa Principal ---

func main() {
	// Caso 1: Uso de un paquete con naming idiomático.
	// En la vida real, llamaríamos a payment.New(...)
	apiKeyValue := "sk_live_51N9X2"
	client := New(apiKeyValue)

	if err := client.Charge(150.75); err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Println("\nNota: Observa cómo los nombres de los tipos y funciones")
	fmt.Println("no repiten la palabra 'Payment', evitando la redundancia.")
}

En el ejemplo anterior, hemos simulado la lógica que debería residir en un paquete independiente. Al analizar la implementación de New y el tipo Client, verás que no hemos repetido la palabra “Payment” en sus nombres. Si hubiéramos nombrado al tipo como PaymentClient, el uso en el código principal sería payment.PaymentClient, lo cual es redundante y dificulta la lectura. Lo mismo ocurre con el método Charge; como ya sabemos que estamos operando sobre un cliente de pagos, llamar al método ChargePayment sería un error de diseño. Un buen diseño de paquetes hace que el autocompletado del IDE se convierta en una herramienta de descubrimiento, no en una lista de nombres redundantes.

El error frecuente es la creación de paquetes util, common o helpers. Al principio parece práctico para mover funciones que “no encajan” en ningún lado, pero esto crea un problema de arquitectura. Estos paquetes terminan siendo contenedores sin una responsabilidad única, lo que rompe la cohesión del sistema de tipos. Además, suelen provocar dependencias circulares: intentas mover una función de util a un paquete con lógica real, pero esa función necesita algo de util, bloqueando tu arquitectura.

107

Dejar un comentario

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

Scroll al inicio