Aller au contenu
Développement medium

Packages et modules Go

6 min de lecture

Go

Un module regroupe des packages versionnés ; un package regroupe des fichiers d'un même dossier. Ce guide vous apprend à structurer un vrai projet Go : créer un module, comprendre la visibilité par la casse, isoler du code privé avec internal/, et adopter la disposition cmd/ + internal/ des outils sérieux. C'est ce qui transforme un main.go fourre-tout en projet maintenable. Public visé : vous avez déjà écrit quelques programmes Go. Exemples testés sur Go 1.26.

  • Créer un module avec go mod init et comprendre go.sum.
  • Exporter ou masquer un identifiant par la casse.
  • Isoler du code privé dans le dossier internal/.
  • Structurer un projet avec cmd/ et internal/.

Un module est l'unité de distribution et de versionnement en Go. On le crée avec go mod init en lui donnant un chemin d'import (souvent l'URL du dépôt).

Fenêtre de terminal
go mod init exemple/monoutil

Cette commande crée un fichier go.mod qui déclare le nom du module et la version de Go :

module exemple/monoutil
go 1.26

Quand vous ajoutez une dépendance avec go get, Go l'inscrit dans go.mod et enregistre son empreinte cryptographique dans go.sum. Ce second fichier verrouille le contenu exact de chaque dépendance : il garantit que tout le monde compile avec les mêmes octets. Les deux fichiers se versionnent dans Git.

Chaque dossier de code est un package, nommé par la première ligne package X. La règle de visibilité de Go est d'une simplicité radicale : un identifiant qui commence par une majuscule est exporté (visible des autres packages), une minuscule le garde privé au package.

package greeter
// Bonjour est exportée (majuscule) : utilisable ailleurs.
func Bonjour(nom string) string { return "Bonjour, " + nom + " !" }
// secret est privée (minuscule) : invisible hors du package.
func secret() string { return "invisible dehors" }

Pas de mots-clés public/private : la casse fait tout. C'est concis et cela rend l'API d'un package immédiatement lisible. On expose le strict nécessaire et on garde le reste minuscule.

La visibilité par la casse s'arrête au package. Pour empêcher d'autres modules d'importer votre code, Go réserve un nom de dossier spécial : internal/. Un package sous internal/ n'est importable que par le code du même module.

monoutil/
├── go.mod
├── cmd/
│ └── app/
│ └── main.go # importe internal/greeter (OK, même module)
└── internal/
└── greeter/
└── greeter.go
cmd/app/main.go
package main
import (
"fmt"
"exemple/monoutil/internal/greeter"
)
func main() {
fmt.Println(greeter.Bonjour("Go")) // Bonjour, Go !
}

Depuis cmd/app, l'import de internal/greeter fonctionne car c'est le même module. Un projet extérieur qui tenterait d'importer exemple/monoutil/internal/greeter serait rejeté à la compilation par l'outillage Go. C'est le moyen de garder une frontière nette entre votre API publique et vos détails d'implémentation.

Cette structure est la convention des outils Go de production. cmd/ contient les points d'entrée (un dossier par binaire), internal/ la logique métier privée.

  • cmd/<binaire>/main.go : le minimum, juste l'assemblage et l'appel du code interne.
  • internal/<domaine>/ : la vraie logique, découpée par responsabilité.

Garder les main.go fins et pousser la logique dans internal/ rend le code testable (on teste les packages internes, pas le main) et permet plusieurs binaires partageant le même cœur.

Un exercice de structure. Réfléchissez à l'organisation avant d'ouvrir la réponse.

Vous avez un main.go unique qui calcule et affiche une salutation. Réorganisez-le en un module propre :

  • Créez le module exemple/monoutil avec go mod init.
  • Placez la fonction de salutation dans un package internal/greeter, avec une fonction exportée Bonjour(nom string) string.
  • Le point d'entrée va dans cmd/app/main.go et se contente d'appeler greeter.Bonjour.

Indice : le chemin d'import complet est exemple/monoutil/internal/greeter.

  • Un module (go mod init) est l'unité de versionnement ; go.mod le déclare, go.sum verrouille les dépendances.
  • Un package est un dossier de code ; un identifiant exporté commence par une majuscule, sinon il est privé.
  • Le dossier internal/ rend un package importable uniquement par le même module.
  • La disposition cmd/ + internal/ garde les main fins et la logique métier testable.
  • On expose le strict nécessaire : une petite API publique, des détails en minuscule et sous internal/.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn