Padrões Avançados de Projeto em Go: da Teoria à Prática
Go (Golang) é uma linguagem que incentiva a simplicidade e legibilidade do código. No entanto, quando o projeto cresce, até o código mais conciso pode se transformar em caos sem uma arquitetura adequada. Em nível avançado, desenvolvedores Go utilizam ativamente padrões de projeto, adaptados às características da linguagem: goroutines, channels, interfaces e generics (desde Go 1.18+).
Neste artigo, analisaremos 4 padrões poderosos que ajudarão você a escrever código escalável, testável e de alto desempenho em Go. Cada padrão é acompanhado por um exemplo prático.
1. Padrão Pipeline para processamento de dados em fluxo
Pipeline é um dos padrões mais populares em Go para trabalhar com processamento sequencial de dados. Ele se encaixa perfeitamente no modelo de goroutines e channels. Cada etapa de processamento é executada em uma goroutine separada, e os dados são transmitidos através de channels.
Quando aplicar: Processamento de logs, transformação de dados, processos ETL, streaming de vídeo.
Exemplo: Pipeline para filtragem e transformação de números.
package main
import "fmt"
// Stage 1: Geração de númerosfunc generate(nums ...int) <-chan int { out := make(chan int) go func() { for _, n := range nums { out <- n } close(out) }() return out}
// Stage 2: Multiplicação por 2func multiply(in <-chan int) <-chan int { out := make(chan int) go func() { for n := range in { out <- n * 2 } close(out) }() return out}
// Stage 3: Filtragem (apenas pares)func filterEven(in <-chan int) <-chan int { out := make(chan int) go func() { for n := range in { if n%2 == 0 { out <- n } } close(out) }() return out}
func main() { // Montando o pipeline pipeline := filterEven(multiply(generate(1, 2, 3, 4, 5)))
for result := range pipeline { fmt.Println(result) // Saída: 4, 8 (apenas pares após multiplicação) }}Vantagens: Processamento paralelo natural, baixo consumo de memória (dados não se acumulam), facilidade para adicionar novas etapas.
2. Fan-Out / Fan-In: escalando o processamento
Este padrão é uma evolução avançada do Pipeline. Fan-Out (ramificação) inicia várias goroutines para processar dados em paralelo a partir de um único channel. Fan-In (fusão) coleta os resultados de vários channels em um único.
Quando aplicar: Parsing de páginas web, processamento de grande quantidade de arquivos, requisições paralelas a APIs.
Exemplo: Verificação paralela de disponibilidade de URLs.
package main
import ( "fmt" "net/http" "sync")
func worker(urls <-chan string, results chan<- string, wg *sync.WaitGroup) { defer wg.Done() for url := range urls { resp, err := http.Get(url) if err != nil { results <- fmt.Sprintf("Erro: %s - %v", url, err) continue } resp.Body.Close() results <- fmt.Sprintf("OK: %s - %d", url, resp.StatusCode) }}
func fanOutFanIn(urls []string, numWorkers int) []string { urlChan := make(chan string, len(urls)) resultChan := make(chan string, len(urls)) var wg sync.WaitGroup
// Fan-Out: iniciando workers for i := 0; i < numWorkers; i++ { wg.Add(1) go worker(urlChan, resultChan, &wg) }
// Enviando URLs for _, url := range urls { urlChan <- url } close(urlChan)
// Aguardando conclusão de todos os workers wg.Wait() close(resultChan)
// Fan-In: coletando resultados var results []string for res := range resultChan { results = append(results, res) } return results}