Especialização em Backend com PHP & Laravel • Laravel 11 & PHP 8.3+ • Eloquent ORM, Sanctum, Filas, Horizon e APIs Corporativas


🗺️ Mapa Conceitual do Tópico

flowchart TD
    A["Cliente HTTP / Frontend"] --> B["API Gateway / Router"]
    B --> C["Controller / Handler"]
    C --> D["Service Layer (Regras de Negócio)"]
    D --> E["Repository / ORM (Persistência)"]
    E --> F["Banco de Dados / Cache"]

    subgraph ARQ["Arquitetura do Capítulo"]
        G["Conceito: Cache Avançado com Redis e Tags de Cache"]
        H["Segurança, Validação e Resiliência"]
        I["Alta Performance e Escalabilidade"]
    end

    D --> ARQ

    style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style C fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style D fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
    style E fill:#fce4ec,stroke:#e91e63,stroke-width:2px
    style F fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px

🏛️ 1. Cache::remember(): Auto-População Atômica

O padrão mais comum de cache é “ler do cache; se não existir, calcular e guardar” — escrito manualmente isso são 4-5 linhas repetidas em todo lugar. Cache::remember() faz isso numa chamada só, e é atômico o suficiente para evitar a maioria das condições de corrida:

<?php
use Illuminate\Support\Facades\Cache;
use App\Models\Produto;

$produtos = Cache::remember('produtos:ativos', 3600, function () {
    return Produto::where('ativo', true)->get();
});

Se a chave produtos:ativos existe no Redis, o closure nunca executa — a query ao banco é pulada inteiramente. Isso é a diferença estrutural entre cache e apenas “guardar um valor”: o objetivo é evitar o trabalho caro (a query), não só evitar retransmitir o resultado.

🏷️ 2. O Problema que Cache Tags Resolve: Invalidação Seletiva

Sem tags, a única forma de invalidar cache relacionado a um produto é ou (a) dar Cache::forget() numa chave específica que você precisa saber de cor, ou (b) Cache::flush() — que apaga absolutamente tudo, inclusive cache de outras partes do sistema sem relação nenhuma. Cache Tags resolvem isso agrupando entradas por rótulo lógico, permitindo invalidar só o grupo relevante:

<?php
// Gravando com múltiplas tags
Cache::tags(['produtos', 'categorias'])->put('produtos:categoria:5', $produtos, 3600);

// Invalidando SÓ o que está marcado como 'produtos' -- 'categorias' continua intacto
Cache::tags('produtos')->flush();

Por baixo dos panos, o driver Redis mantém um índice reverso de “quais chaves pertencem a qual tag”, então o flush() de uma tag não precisa varrer o banco inteiro procurando chaves — ele consulta esse índice e apaga só as chaves marcadas.

⚠️ Gotcha importante: Cache Tags só funcionam com drivers que suportam essa indexação (Redis e Memcached) — o driver file ou database do Laravel lança exceção se você tentar usar Cache::tags() com eles.

🔒 3. Cache-Aside vs. Write-Through

O padrão usado em Cache::remember() é chamado Cache-Aside (ou lazy loading): a aplicação só popula o cache quando um read acontece e a chave está faltando. A alternativa é Write-Through: popular o cache no momento exato da escrita (update/create), garantindo que o cache nunca fique desatualizado às custas de escrever em dois lugares toda vez:

public function atualizar(Produto $produto, array $dados): Produto
{
    $produto->update($dados);
    // Write-Through: atualiza o cache no mesmo instante da escrita no banco
    Cache::tags('produtos')->put("produto:{$produto->id}", $produto, 3600);
    return $produto;
}

Cache-Aside é mais simples e cobre a maioria dos casos; Write-Through evita a janela (ainda que pequena) entre a escrita no banco e a próxima leitura repopular o cache, ao custo de mais pontos de código que precisam lembrar de manter o cache em sincronia.