Ciao, tutti! Molti hanno chiesto come funzionino le semplici schedole o le applicazioni che offrono queste funzionalità. In questo articolo, vi invito a considerare come potete scrivere il vostro piccolo servizio in Go in pochi ore e mettere tutto in una database.
Iniziamo il nostro viaggio con Golang e MongoDB.
Perché Golang?
Voglio mostrarvi le chiavi:
- Design minimale e compilazione veloce
- Modello di concorrenza forte con Goroutines e canali
- Ecosistema vasto
- Cross-platform di default
Un altro fattore è non dover spendere troppo tempo a studiare le librerie o le soluzioni open-source. Nel mio caso, voglio creare un microservizio che funzioni praticamente da subito. Il linguaggio di programmazione Golang ha tutto ciò che serve.
Osserverò però che il linguaggio è già ricco di progetti molto cool che risolvono molti problemi per il programmatore. Progetti come:
- Gin: High-performance HTTP web framework
- Viper: Soluzione di configurazione (JSON, properties, file YML) per applicazioni Go
- GORM: Libreria ORM
- Protocol Buffers (Protobuf): Il modo migliore per serializzare i dati strutturati.
Non le reviewremo all’interno del contesto dell’articolo, ma forse ne scriverò qualcosa in seguito. Con Protobuf, ho già scritto un articolo interessante, “From JSON to FlatBuffers: Enhancing Performance in Data Serialization.”
Installazione
Installazione del linguaggio Go
Visita golang.org e scarica. Poi, vai al terminale e controlla.
go version
IDE
Installa solo VS Code (è gratuito).
E poi aggiungi l’estensione Golang:
Scrivi il tuo primo codice:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
E lo esegui:
go run main.go
Ecco fatto! Il prossimo passo è scegliere l’opzione migliore per raccogliere i nostri dati.
La nostra Struttura Compito
Penso che la nostra prima struttura dovrebbe essere davvero semplice. Partiamo con 3 campi:
- Titolo (testo)
- Descrizione (testo)
- Stato (booleano)
File JSON come riferimento:
{
"title": "Go to the groceries",
"description": "Purchase milk, eggs, and bread",
"completed": false
}
Perché MongoDB?
Abbiamo bisogno di raccogliere dati per le nostre attività ed essere flessibili. Non abbiamo bisogno di creare uno schema o una relazione tra cose.
- Schema Flessibile
- Scalabilità: Supporta la scalabilità orizzontale.
- Linguaggio di Queryricco
Per il nostro piccolo servizio, lo possiamo eseguire come docker-compose
.
# Usa root/example come credenziali utente/password
version: '3.1'
services:
mongo:
image: mongo
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
Preferisco il Compass GUI per lavorare con i nostri dati (raccolte, database, ecc.). Scaricalo qui.
Eseguilo e impostare le tue credenziali in “Opzioni Avanzate“. Funziona perfettamente e può aiutarti a trovare problemi e ottimizzare le richieste se ce ne sono.
Progettazione del Sistema (Really Small)
Per il nostro servizio, naturalmente, l’opzione migliore è creare metodi CRUD (Create, Read, Update, Delete), qualcosa del genere (senza delete):
http.HandleFunc("/api/v1/add", todoHandler.AddTask)
http.HandleFunc("/api/v1/get-all", todoHandler.GetAllTasks)
http.HandleFunc("/api/v1/update", todoHandler.UpdateTask)
http.HandleFunc("/api/v1/complete", todoHandler.CompleteTask)
Voglio usare il modo di separare tutte le responsabilità the folders.
- Handler – Layer HTTP
- Model – Strutture per i dati
- Use cases – Layer business con servizio e repository
La struttura del progetto potrebbe essere simile a questa:
todo-list/
│
├── cmd/
│ └── main.go
├── pkg/
│ └── handler
│ └── add_task.go
│ └── http_handler.go
│ └── complite_task.go
│ └── get_all_task.go
│ └── update_task.go
│ └── mapper
│ └── task.go
│ └── model
│ └── task.go
│ └── usecase
│ └── task
│ └── repository
│ └── add_task.go
│ └── complite_task.go
│ └── get_all_task.go
│ └── mongo_repositiry.go
│ └── repository.go
│ └── update_task.go
│ └── service
│ └── add_task.go
│ └── complite_task.go
│ └── get_all_task.go
│ └── service.go
│ └── update_task.go
└── go.mod
Come possiamo vedere, abbiamo un file “go.mod”, ma cos’è? È un gestore di pacchetti o gestore dipendenze. Possiamo installare e aggiungere librerie esterne e usarle pure. Per il nostro esempio, ci serve un paio di comandi che usano “go mod”.
- Inizializzare il nostro app ->
go mod init todo-service
. Inizializzeremo tutto; il gestore dipendenze creerà file e aggiungerà tutto ciò che serve. - Aggiungere dipendenze extra usando
go mod add "link"
.
Potete leggere di più sulla pagina Go Modules Reference .
Poi, focusiamo solo su un metodo – aggiungere attività. Per ulteriori esplorazioni e esempi di codice completi, visitate il repository GitHub Golang Workshop .
Connessione a MongoDB
Usando solo due dipendenze, possiamo creare una connessione:
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
naturalmente, dobbiamo aggiungerlo al nostro servizio. Usate i seguenti comandi:
go get "go.mongodb.org/mongo-driver/mongo"
e
go get "go.mongodb.org/mongo-driver/mongo/options"
Quindi, scrivete questo pezzo di codice:
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Impostare le opzioni del client MongoDB
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017").SetAuth(options.Credential{
Username: "root",
Password: "example",
})
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal(err)
}
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal(err)
}
log.Println("Connected to MongoDB!")
}
Strutture dati per il nostro app:
package model
import "go.mongodb.org/mongo-driver/bson/primitive"
type Task struct {
ID string `json:"id"`
Title string `json:"title"`
Desciption string `json:"description"`
Completed bool `json:"completed"`
}
type MongoTask struct {
ID primitive.ObjectID `json:"id" bson:"_id"`
Title string `json:"title"`
Desciption string `json:"description"`
Completed bool `json:"completed"`
}
Compito
– per richiesta HTTP,MongoTask
– per il layer MongoDB: L’uso di due strutture è facile perché a volte non abbiamo bisogno di inviare dati aggiuntivi ai nostri utenti. Per esempio, potremmo avere un campo segreto, come un nome utente, che dobbiamo nascondere.
1. Layer del repository:
type Repository interface {
AddTask(ctx context.Context, task model.MongoTask) error
}
func (r *MongoRepository) AddTask(ctx context.Context, task model.MongoTask) error {
task.ID = primitive.NewObjectID()
_, err := r.collection.InsertOne(ctx, task)
}
2. Layer del servizio:
type TodoService interface {
AddTask(ctx context.Context, task model.Task) error
}
func (s *Service) AddTask(ctx context.Context, task model.Task) error {
return s.Repo.AddTask(ctx, mapper.MapToDto(task))
}
3. Layer dell’handler (per processare le richieste HTTP):
func (h *Handler) AddTask(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
var task model.Task
err := json.NewDecoder(r.Body).Decode(&task)
if err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
err = h.Service.AddTask(ctx, task)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusCreated)
}
Ora, dobbiamo installare la connessione al database e inizializzare la dipendenza “layers” nel file main.go
.
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Imposta le opzioni del client MongoDB
clientOptions := options.Client().ApplyURI("mongodb://localhost:27017").SetAuth(options.Credential{
Username: "root",
Password: "example",
})
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
log.Fatal(err)
}
err = client.Ping(ctx, nil)
if err != nil {
log.Fatal(err)
}
log.Println("Connected to MongoDB!")
// Inizializza il repository, il servizio e l'handler
todoRepo := repository.NewMongoRepository(client)
todoService := service.NewService(todoRepo)
todoHandler := handler.NewHandler(todoService)
// Imposta le rotte
http.HandleFunc("/api/v1/add", todoHandler.AddTask)
// Crea un server
srv := &http.Server{
Addr: ":8080",
Handler: nil,
}
// da fare: avviare il servizio e arrestare
}
E provatelo con la richiesta:
# curl -X POST http://localhost:8080/add
#-H "Content-Type: application/json"
#-d '{
# "id": 1,
# "title": "Comprare prodotti alimentari",
# "completed": false
#}'
POST http://localhost:8080/api/v1/add
Content-Type: application/json
{
"title": "Add description to the structure",
"description": "your desc here..."
}
Questo è tutto. Poi, dobbiamo aggiungere e implementare nuovi metodi, e il nostro servizio sarà pronto a lavorare.
Conclusione
Hemmo creato un piccolo, ma robusto servizio di gestione dei compiti utilizzando Golang e MongoDB.
Come vediamo, se abbiamo bisogno di costruire un piccolo servizio, lo facciamo davvero rapidamente senza molti ostacoli. Nel mio caso, sarei davvero lieto di utilizzare MongoDB come database principale se ho documenti. È semplice da gestire.
Si può anche notare che non sarebbe peggiore in altre lingue di programmazione. In alcuni casi, addirittura più veloce. Per esempio, se si utilizza Python e FastAPI – e qui potrei non essere d’accordo con te. Il linguaggio Go è ancora basato sui paradigmi di linguaggi di programmazione come C++ e Java, che hanno OOP. Tale codice e metodologia consentono di tenere il codice così chiaro e pulito quanto possibile.
Per cominciare, sarà buono consolidare fattori come base, che ti aiuteranno a capire TDD e altre metodologie. Noterò anche che per evitare l’eccesso di metriche e testo nell’articolo, ho omesso la comparazione con altri linguaggi. Noterò che Go non ha problemi in questo senso, e avendo scritto la funzione principale, la puoi già eseguire nel tuo thread. E’ facile trovare confronti sul web, inclusi benchmark.
Per approfondire l’esplorazione e i completi esempi di codice, visita il repository GitHub (collegato precedentemente).
Grazie e buon difesa!
Source:
https://dzone.com/articles/build-a-to-do-list-with-mongodb-and-golang