DEV Community

Lucas Pereira de Souza
Lucas Pereira de Souza

Posted on

Edge Computing com Cloudflare Workers

logotech

# Rodando Lógica de Backend na Borda: O Futuro da Aplicações Distribuídas

A arquitetura de aplicações tem evoluído rapidamente, saindo dos tradicionais data centers centralizados para modelos cada vez mais distribuídos. Uma das tendências mais empolgantes nesse cenário é a execução de lógica de backend diretamente na \"borda" da rede. Mas o que isso significa e por que é tão importante para o futuro do desenvolvimento?

A Migração da Lógica para a Borda

Tradicionalmente, o backend de uma aplicação reside em servidores centralizados. Requisições de usuários viajam longas distâncias até esses servidores, onde a lógica de negócio é processada, e a resposta retorna. Esse modelo, embora funcional, introduz latência e pode sobrecarregar os servidores centrais com tarefas que poderiam ser resolvidas mais perto do usuário.

A computação de borda (Edge Computing) propõe levar o processamento para mais perto de onde os dados são gerados ou consumidos – na borda da rede. Isso inclui dispositivos de IoT, gateways de rede, CDNs (Content Delivery Networks) e até mesmo navegadores de usuários. Ao mover a lógica de backend para esses pontos, reduzimos drasticamente a latência, melhoramos a performance e habilitamos novos casos de uso.

Por que Rodar Backend na Borda?

  1. Latência Reduzida: O benefício mais óbvio. Processar requisições na borda minimiza o tempo de ida e volta (round-trip time), crucial para aplicações em tempo real, como jogos online, realidade aumentada/virtual e sistemas de controle industrial.
  2. Otimização de Banda: Em vez de enviar grandes volumes de dados brutos para um servidor central, a lógica de borda pode pré-processar, filtrar e agregar dados localmente, enviando apenas informações relevantes. Isso economiza largura de banda, especialmente em redes com conectividade limitada ou cara.
  3. Melhoria da Resiliência e Disponibilidade: Aplicações podem continuar funcionando mesmo com conectividade intermitente ou falha total do link com o data center central, pois a lógica essencial está disponível localmente na borda.
  4. Privacidade e Segurança: Dados sensíveis podem ser processados e anonimizados na borda antes de serem enviados para processamento centralizado, ajudando a cumprir regulamentações de privacidade.
  5. Escalabilidade: Distribui a carga de processamento, aliviando a pressão sobre os servidores centrais e permitindo que a aplicação escale de forma mais eficiente.

Exemplos de Código: TypeScript/Node.js na Borda

Vamos considerar um cenário onde precisamos validar dados de um sensor antes de enviá-los para um banco de dados central. Podemos executar essa validação em um gateway de IoT na borda.

Utilizaremos Node.js com TypeScript, aproveitando sua tipagem forte e o ecossistema robusto.

1. Definição das Interfaces (Tipagem Forte):

// interfaces.ts

/**
 * Representa os dados brutos de um sensor.
 */
interface RawSensorData {
  id: string;
  timestamp: number;
  value: any; // O valor pode ser de qualquer tipo inicialmente
  unit?: string; // Unidade de medida opcional
}

/**
 * Representa os dados de um sensor após validação e formatação.
 */
interface ValidatedSensorData {
  sensorId: string;
  readingTime: Date;
  numericValue: number;
  unit: string | null;
}

/**
 * Representa os possíveis erros de validação.
 */
type ValidationError = {
  field: string;
  message: string;
};
Enter fullscreen mode Exit fullscreen mode

2. Lógica de Validação e Processamento:

// sensorProcessor.ts
import { RawSensorData, ValidatedSensorData, ValidationError } from './interfaces';

/**
 * Valida e processa os dados brutos de um sensor.
 * @param data - Os dados brutos do sensor a serem processados.
 * @returns Um objeto contendo os dados validados ou uma lista de erros de validação.
 */
export function processSensorData(
  data: RawSensorData
): ValidatedSensorData | ValidationError[] {
  const errors: ValidationError[] = [];

  // Validação: sensorId (deve existir e ser string)
  if (!data.id || typeof data.id !== 'string') {
    errors.push({ field: 'id', message: 'Sensor ID is required and must be a string.' });
  }

  // Validação: timestamp (deve ser um número válido)
  if (typeof data.timestamp !== 'number' || isNaN(data.timestamp)) {
    errors.push({ field: 'timestamp', message: 'Timestamp must be a valid number.' });
  }

  // Validação: value (deve ser um número para este exemplo)
  let numericValue: number | undefined;
  if (typeof data.value === 'number' && !isNaN(data.value)) {
    numericValue = data.value;
  } else if (typeof data.value === 'string') {
    // Tenta converter string para número
    const parsedValue = parseFloat(data.value);
    if (!isNaN(parsedValue)) {
      numericValue = parsedValue;
    }
  }

  if (numericValue === undefined) {
    errors.push({ field: 'value', message: 'Sensor value must be a valid number or convertible to one.' });
  }

  // Se houver erros, retorna a lista de erros
  if (errors.length > 0) {
    return errors;
  }

  // Se a validação for bem-sucedida, formata os dados
  // A conversão '!' é segura aqui porque já validamos que numericValue não é undefined
  // e timestamp é um número válido.
  const validatedData: ValidatedSensorData = {
    sensorId: data.id!,
    readingTime: new Date(data.timestamp!),
    numericValue: numericValue!,
    unit: data.unit || null, // Define como null se não houver unidade
  };

  return validatedData;
}

// Exemplo de uso (simulando uma requisição na borda)
const rawData1: RawSensorData = {
  id: 'sensor-abc-123',
  timestamp: Date.now(),
  value: 25.5,
  unit: '°C',
};

const rawData2: RawSensorData = {
  id: 'sensor-xyz-789',
  timestamp: Date.now() - 5000,
  value: '30.2', // Valor como string, mas conversível
  unit: '°C',
};

const rawDataInvalid: RawSensorData = {
  id: '', // ID inválido
  timestamp: NaN, // Timestamp inválido
  value: null, // Valor inválido
};

const result1 = processSensorData(rawData1);
console.log('Resultado 1:', result1);
// Saída esperada: { sensorId: 'sensor-abc-123', readingTime: ..., numericValue: 25.5, unit: '°C' }

const result2 = processSensorData(rawData2);
console.log('Resultado 2:', result2);
// Saída esperada: { sensorId: 'sensor-xyz-789', readingTime: ..., numericValue: 30.2, unit: '°C' }

const resultInvalid = processSensorData(rawDataInvalid);
console.log('Resultado Inválido:', resultInvalid);
// Saída esperada: [ { field: 'id', message: 'Sensor ID is required and must be a string.' }, ... ]

// Simula o envio dos dados validados para um serviço central
function sendToCentral(data: ValidatedSensorData) {
  console.log('Enviando para o backend central:', data);
  // Aqui você implementaria a lógica de envio (ex: HTTP POST, MQTT, etc.)
}

if (Array.isArray(result1)) {
  console.error('Falha ao processar dados 1:', result1);
} else {
  sendToCentral(result1);
}

if (Array.isArray(result2)) {
  console.error('Falha ao processar dados 2:', result2);
} else {
  sendToCentral(result2);
}

if (Array.isArray(resultInvalid)) {
  console.error('Falha ao processar dados inválidos:', resultInvalid);
} else {
  // Não enviaria os dados inválidos
  sendToCentral(resultInvalid);
}

Enter fullscreen mode Exit fullscreen mode

Boas Práticas Aplicadas:

  • Tipagem Forte: Interfaces RawSensorData, ValidatedSensorData e ValidationError garantem que os dados tenham a estrutura esperada, prevenindo erros em tempo de execução.
  • Clean Code: Funções com responsabilidade única (processSensorData), nomes de variáveis e funções claros, e separação de preocupações (interfaces, lógica de processamento, simulação de envio).
  • Tratamento de Erros: A função retorna explicitamente os erros encontrados, permitindo que o chamador decida como lidar com eles (ex: registrar, descartar, tentar novamente).
  • Comentários: Explicações claras sobre o propósito das interfaces e a lógica de validação em linhas complexas ou decisões importantes.
  • Imutabilidade: Embora não estritamente forçada neste exemplo simples, a prática recomendada é não modificar o objeto de entrada data, mas sim retornar um novo objeto validatedData.

Conclusão

Rodar lógica de backend na borda da rede não é mais uma visão futurista, mas uma necessidade emergente para construir aplicações mais rápidas, eficientes e resilientes. Ao mover o processamento para mais perto do usuário ou da fonte de dados, podemos desbloquear novas experiências e otimizar drasticamente o uso de recursos. Ferramentas e plataformas como Cloudflare Workers, AWS Lambda@Edge, e soluções de IoT Edge estão capacitando desenvolvedores a implementar essa arquitetura.

Adotar a computação de borda significa repensar como projetamos e implementamos nossas aplicações, mas os benefícios em performance, custo e experiência do usuário são inegáveis. O futuro é distribuído, e a borda é o novo centro.

Top comments (0)