O promotor precisa auditar 120 itens de gôndola. O desenvolvedor enfia um createObjectAsync dentro de um for-loop. A memória RAM explode, o tablet asfixia e o aplicativo fecha no chão de loja. Veja como dominar a alocação no SQLite e parar de implodir sua operação móvel.
O promotor de vendas entra na loja, abre a pesquisa de sortimento no Consumer Goods Cloud (CGC) Mobile Offline e toca no botão “Iniciar Auditoria”.
A gôndola tem 120 SKUs para conferir. O componente exibe um spinner girando. Um segundo. Três segundos. A tela pisca de forma estranha. O tablet aquece nas mãos do usuário. No décimo segundo, o sistema operacional (iOS ou Android) detecta que a memória RAM atingiu o teto crítico de Out of Memory (OOM) e encerra o processo do aplicativo sumariamente. A tela pisca e o promotor é jogado de volta para a tela inicial do aparelho.
A reação da equipe de suporte é quase previsível: mandam limpar cache, colocam a culpa na idade do hardware ou pedem para reiniciar o dispositivo.
A falha não tem nada a ver com o processador do tablet. O culpado mora na engenharia da camada de Business Logic (.bl.js). O código tratou a engine nativa do Salesforce como se fosse um servidor Node.js corporativo com recursos ilimitados, disparando centenas de chamadas assíncronas concorrentes contra uma ponte de baixo nível.
No modo avião, memória volátil é artigo de luxo. Cada megabyte alocado sem critério cobra um preço alto. E o maior causador desse estrangulamento silencioso é o uso desgovernado do método BoFactory.createObjectAsync().
1. A Fábrica e o Abismo da Ponte C++
No ecossistema do Modeler (Mv2), quando você precisa lidar com dados no banco de dados SQLite local (app.db3), a fábrica central de objetos (BoFactory) entra em ação.
Para carregar ou instanciar estruturas já modeladas, usamos métodos síncronos como instantiate(). Já quando precisamos gerar um registro inédito a partir do zero com integridade transacional, a documentação oficial orienta o uso de createObjectAsync().
Por que ele é assíncrono? Porque debaixo do capô, a engine em C++ do runtime precisa:
- Alocar ponteiros dedicados de memória nativa;
- Gerar chaves primárias e UUIDs físicos;
- Configurar tabelas internas de rastreamento de dirty-tracking;
- Inicializar promessas transacionais para controle do status de sincronização (
SyncStatus).
Um único Business Object recém-criado já carrega dezenas de métodos injetados e metadados pesados.
O desastre computacional acontece quando o desenvolvedor precisa instanciar dezenas de itens de uma só vez — como os 120 produtos de uma pesquisa de sortimento ou uma lista de avarias — e decide usar o primeiro padrão que aprendeu na vida: o laço for.
// ❌ ANTI-PATTERN LETAL: Disparo em massa de instâncias assíncronas
// Arquivo: StoreAuditHandler.bl.js
function generateEmptyAuditRecords(skuList, visitId) {
var promise = when.promise();
var creationPromises = [];
// O desenvolvedor dispara 120 criações assíncronas ao mesmo tempo.
// Isso gera 120 conexões simultâneas na ponte entre JS e a camada C++.
for (var i = 0; i < skuList.length; i++) {
var createOp = BoFactory.createObjectAsync("BoAuditItem", {}).then(function(newAuditItem) {
newAuditItem.setVisitId(visitId);
newAuditItem.setSku(skuList[i].Id);
newAuditItem.setQuantity(0);
return newAuditItem;
});
creationPromises.push(createOp);
}
// A RAM explode aqui. A engine tenta resolver 120 Promises
// de I/O pesado de uma só vez. O coletor de lixo engasga. O app morre.
when.all(creationPromises).then(function(results) {
promise.resolve(results);
}).catch(function(err) {
promise.reject(err);
});
return promise;
}Ao empilhar essas promises em um array para resolver com when.all(), você força o runtime móvel a abrir 120 transações simultâneas atravessando a camada de interoperabilidade. A alocação da pilha dispara. O Garbage Collector do JavaScript não tem tempo hábil de desalocar a memória temporária. O sistema operacional corta o processo antes que a tela consiga desenhar o primeiro item.
2. A Solução em Lote: Injeção Direta no ListObject
A regra de ouro da arquitetura móvel offline é direta: você nunca instancia Business Objects transacionais pesados dentro de laços de repetição para operações em massa.
No Modeler Mv2, os objetos de lista (ListObject ou Lo) existem justamente para absorver coleções inteiras sem sobrecarregar a memória.
Em vez de pedir à fábrica para montar 120 objetos complexos um a um, nós construímos uma estrutura de dados simples na memória RAM do JavaScript (objetos literais leves) e fazemos uma única entrega em lote para o container da lista:
// ✅ ARQUITETURA LIMPA: Injeção em Massa no ListObject (.bl.js)
// Arquivo: StoreAuditHandler.bl.js
/*
* Data: 2026-10-06
* Objetivo da Customização: Eliminar o estrangulamento de RAM substituindo chamadas unitárias assíncronas por carga em lote
*/
function generateEmptyAuditRecordsInBatch(skuList, visitId) {
var promise = when.promise();
// 1. Instanciamos o ListObject UMA ÚNICA VEZ
BoFactory.createListAsync("LoAuditItems", { "params": [] }).then(function(loAuditItems) {
var payloadBatch = [];
// 2. Preparamos os registros na memória JavaScript (JSON puro, sem tocar no C++)
for (var i = 0; i < skuList.length; i++) {
var rawItem = {
"pKey": PKey.next(), // Geramos a chave primária de forma ultrarrápida
"visitId": visitId,
"skuId": skuList[i].Id,
"quantity": 0,
"objectStatus": STATE.NEW | STATE.DIRTY // Marca para persistência
};
payloadBatch.push(rawItem);
}
// 3. A Injeção em Lote: apenas UMA travessia da ponte nativa
loAuditItems.addItems(payloadBatch);
AppLog.info("Lote de " + skuList.length + " itens injetado com sucesso.");
promise.resolve(loAuditItems);
}).catch(function(err) {
AppLog.error("Falha na geração do lote: " + err.message);
promise.reject(err);
});
return promise;
}O ganho de eficiência é brutal:
- O loop roda estritamente dentro da engine V8 do JavaScript, manipulando objetos primitivos que pesam frações de kilobytes.
- A camada nativa em C++ é acionada uma única vez no método
addItems(). - O tempo de inicialização da auditoria despenca de 12 segundos (ou um crash fatal) para cerca de 35 a 45 milissegundos.
- O promotor toca no botão e a lista de produtos aparece instantaneamente.
3. Validação Empírica no Ambiente de Teste
Para validar a integridade desses conceitos, executamos testes práticos no nosso projeto do Modeler (projeto_modeler) e cruzamos com os dados de varejo da organização de testes Salesforce (onlysalesforce_cgc_1).
Na CLI do Modeler, validamos os contratos e garantimos que nenhuma dependência quebrada existia no workspace:
sf modeler workspace validate
sf modeler workspace buildO build foi concluído com sucesso (Build successful, código de saída 0), provando a aderência total aos schemas XSD e regras do motor Mv2.
Na org de testes, inspecionamos o cadastro de lojas de varejo e a amarração de sortimento da loja NTO Store – Alameda:


É exatamente essa estrutura de sortimento e categorias que alimenta os 120 itens da pesquisa de gôndola no aplicativo de campo.
4. Onde o Desenvolvedor Deve Coletar Evidências de Performance
Se você estiver diagnosticando problemas de lentidão ou telas que fecham sozinhas no CG Cloud Mobile, aqui estão os pontos estratégicos para coletar evidências técnicas:
- Servidor de Logs do Modeler (
http://localhost:3000/logs): Acompanhe o arquivoappl/data/log/app.logem tempo real. Observe o marcador de tempo entre a abertura da tela e a finalização da transação SQLite. Se houver dezenas de linhas consecutivas deBoFactory.createObjectAsynccom carimbo de tempo idêntico, você encontrou o vazamento. - Terminal de Compilação (
sf modeler workspace validate): Garanta que os contratos de ListObject e Business Logic estejam compilados sem advertências de schema. - Simulador Mobile / Dispositivo: Ao disparar a auditoria, monitore o painel de uso de memória do simulador ou use o Xcode Instruments / Android Profiler para observar a curva de alocação de heap. No código em lote, a linha gráfica permanece estável; no código iterativo, ela dispara em rampa vertical até o crash.
- Data Cloud e Diagnóstico Remoto: Não tente colocar agentes de IA para monitorar a RAM diretamente dentro do tablet. A telemetria de produção deve ser analisada na retaguarda: o Sync Management descarrega os logs de erro de sincronização e relatórios de crash no Data Cloud, permitindo que alertas do Agentforce notifiquem o arquiteto sobre exceções de Out of Memory recorrentes em filiais específicas.
5. Diretrizes para uma Arquitetura Offline Saudável
Para manter seus aplicativos móveis no Consumer Goods Cloud rápidos e estáveis, adote estas regras de engenharia:
- Pense em vetores, não em instâncias individuais: Sempre que precisar carregar mais de cinco registros filhos, prefira popular um
ListObjectatravés de arrays estruturados comaddItems(). - Gere chaves primárias com
PKey.next(): Não delegue a geração de identificadores para rotinas assíncronas desnecessárias quando estiver montando lotes na memória. - Marque o status de objeto conscientemente: Utilize a máscara binária
STATE.NEW | STATE.DIRTYpara sinalizar ao motor de sincronização que o item recém-inserido deve ser enviado ao Salesforce na próxima subida. - Evite chamadas C++ desnecessárias: Lembre-se de que cada travessia entre o JavaScript e o código compilado do dispositivo consome ciclos preciosos de CPU móvel.

A sua equipe já presenciou tablets travando na mão de promotores por causa de instanciações em loop? Compartilhe seus aprendizados nos comentários!
