Files
RRBEC-LocalServer/Servidor Golang.md
Welton Moura 936aad779b feat: RRBEC Local Server - Go backend with Django sync
- Implement local-first architecture with SQLite
- Add bidirectional sync with Django via ChangeLog
- JWT authentication with auto-refresh token
- REST API for products, orders, commands, payments
- Stock management with automatic deduction
2026-04-04 17:38:40 -03:00

7.6 KiB
Raw Permalink Blame History

Servidor Golang

eu tenho uma aplicação de API em django rodando em uma VPS (aplicação de gestão de bar). tenho um app lado client para desktop feito com electron. eu predento criar uma especie de midware em go para criar um db local junto com o cliente e o go sicronizase com o django. para ser mais especifico, o go criaria um servidor local onde o cliente desktop acessaria o db local e modificaria, outros dispositivos mobile fariam o mesmo, e o go ficasse constantemente sicronizando com o django. como voce me recomendaria implementar essa ideia?

O Gemini disse

Essa é uma arquitetura clássica de Edge Computing ou Local-first, muito comum em sistemas de PDV (Ponto de Venda), onde o bar não pode parar se a internet cair.

O Go é a escolha perfeita para isso devido ao baixo consumo de memória e à facilidade de compilar um executável único que roda no Windows, Linux ou Mac.

Aqui está a recomendação de arquitetura para o seu "Middleware Go":


1. Stack Recomendada para o Middleware

Para manter o sistema leve e rápido na VPS e no cliente:

  • Banco de Dados Local:SQLite. É um arquivo único, não precisa de instalação de servidor (como Postgres) e o Go lida perfeitamente com ele usando a lib modernc.org/sqlite (que não precisa de CGO).
  • API Local (Go):Gin ou Echo. Eles servirão os endpoints que o Electron e os celulares vão consumir.
  • Sincronização:Gorm (para facilitar a manipulação dos dados) e o pacote nativo net/http para falar com o Django.

2. Desenho da Arquitetura

  1. Electron/Mobile: Fazem requisições POST/GET para http://localhost:8080 (o Middleware Go).

  2. Go (Middleware): * Salva a transação imediatamente no SQLite local (Garante a venda).

    • Retorna "Sucesso" para o cliente (Baixa latência).
    • Em uma Goroutine (segundo plano), tenta enviar os dados para o Django.
  3. Django (Cloud): Recebe os dados, valida e retorna um "OK". O Go marca aquela linha no SQLite como sincronizado = true.


3. Estratégia de Sincronização (O "Pulo do Gato")

Para evitar conflitos e perda de dados, sua tabela no SQLite precisa de três campos extras:

  • uid: Um UUID gerado no Go (não use IDs sequenciais de banco, ou o Django vai se confundir).
  • sincronizado: Um booleano (true/false).
  • atualizado_em: Um timestamp para saber qual versão é a mais nova.

Exemplo de Fluxo no Go:

Go

// 1. O PDV envia um pedido
// 2. Go salva no SQLite:
db.Create(&Pedido{ID: "uuid-123", Total: 50.0, Sincronizado: false})

// 3. Uma Goroutine roda a cada 30 segundos:
func syncer() {
    for {
        var pedidos []Pedido
        // Busca o que ainda não subiu para o Django
        db.Where("sincronizado = ?", false).Find(&pedidos)
    
        for _, p := range pedidos {
            err := enviarParaDjango(p)
            if err == nil {
                db.Model(&p).Update("sincronizado", true)
            }
        }
        time.Sleep(30 * time.Second)
    }
}

4. Como implementar passo a passo

Passo 1: O Banco Local

Use o SQLite para que o dono do bar não precise instalar nada. O Go cria o arquivo .db automaticamente se ele não existir.

Passo 2: Service Discovery (Como o Mobile acha o Go?)

Como o IP do computador onde o Go está rodando pode mudar, você tem duas opções:

  1. IP Fixo: Configurar o computador do bar com um IP estático na rede local.
  2. mDNS (ZeroConf): Use uma lib em Go como grandcat/zeroconf. O Go "anuncia" na rede: "Eu sou o servidor do Bar" . O app mobile detecta isso automaticamente sem digitar IP.

Passo 3: Autenticação Dupla

O seu middleware em Go deve ter uma API_KEY.

  • O Electron envia a chave para o Go.
  • O Go usa as credenciais de sistema para falar com o Django.

5. Vantagens dessa abordagem

  • Offline First: Se a internet do bar cair, o Electron continua funcionando 100%. Quando a internet volta, o Go sobe tudo em lote (batch).
  • Velocidade: O Electron não espera o sinal ir até a nuvem e voltar para imprimir um pedido. É instantâneo.
  • Segurança: Você pode criptografar o arquivo SQLite local.

Para um projeto que mistura banco de dados local, API e sincronização em segundo plano, o Design Pattern mais indicado é uma versão simplificada da Clean Architecture (ou Hexagonal).

O objetivo é separar a Lógica de Negócio (regras do bar) da Infraestrutura (SQLite, API Django, Servidor Gin). Assim, se você quiser trocar o SQLite por um Postgres no futuro, ou o Gin pelo Fiber, o coração do app não muda.


1. Estrutura de Pastas Sugerida

Uma estrutura padrão em Go para projetos de médio porte segue este esqueleto:

Plaintext

meu-middleware-go/
├── cmd/
│   └── server/
│       └── main.go          # Ponto de entrada (inicializa tudo)
├── internal/                # Código privado (não pode ser importado por outros projetos)
│   ├── api/                 # Handlers HTTP (Rotas do Gin)
│   ├── database/            # Conexão e migrações do SQLite
│   ├── models/              # Estruturas de dados (Pedidos, Itens)
│   ├── repository/          # Consultas ao SQLite (CRUD)
│   ├── service/             # Lógica de negócio e regras de sincronização
│   └── sync/                # O "Worker" que fala com o Django
├── go.mod                   # Gerenciador de pacotes
└── go.sum                   # Checksum das dependências

2. Divisão de Responsabilidades (Design Pattern)

Aqui aplicamos o padrão Repository/Service:

Models (internal/models)

Define como os dados são. No seu caso, o diferencial é ter campos de controle de sincronização.

Go

type Pedido struct {
    ID           string    `gorm:"primaryKey"`
    ValorTotal   float64
    Sincronizado bool      `gorm:"default:false"`
    UpdatedAt    time.Time
}

Repository (internal/repository)

É o único que "fala" com o SQLite. Ele não sabe o que é Django ou JSON, ele só sabe salvar e buscar no banco local.

  • *Função exemplo:*SavePedido(p models.Pedido), GetNaoSincronizados().

Service (internal/service)

Aqui mora a inteligência. O Service chama o Repository para salvar localmente e, ao mesmo tempo, pode disparar um evento para o módulo de sincronização.

  • Responsabilidade: Validar se o estoque permite a venda, calcular impostos locais.

Sync Worker (internal/sync)

Este é um processo separado (usando Goroutines). Ele roda em um loop infinito (ex: a cada 30 segundos), pergunta ao Repository quais pedidos não foram sincronizados e tenta enviar para o Django via http.Post.


3. Por que essa estrutura?

  1. Independência: O cmd/main.go apenas "monta o quebra-cabeça". Se você precisar rodar apenas o sincronizador sem a API, é fácil separar.
  2. Testabilidade: Você consegue testar a lógica de cálculo do bar (Service) sem precisar ligar o banco de dados de verdade.
  3. Segurança de Dados: Ao separar o sync, você garante que se o Django estiver fora do ar, a API local (que o Electron usa) continue respondendo rápido, pois ela só interage com o repository local.

4. O Fluxo de Dados

  1. Electron envia JSON → API (Gin) .
  2. API chama Service.
  3. Service manda Repository salvar no SQLite.
  4. Sync Worker (em background) lê o SQLite e envia para o Django.
  5. Django responde OK → Sync Worker manda Repository marcar como Sincronizado: true.