artigo

Service Portal e Now Experience: duas interfaces, dois públicos, uma decisão que dura anos

O ServiceNow já lançou três 'futuros' de interface em uma década — o que importa é saber para qual público cada uma foi construída.

7 min de leitura intermediário servicenow arquitetura service-portal

#a terceira vez que a interface "certa" mudou

Se você acompanha ServiceNow há tempo suficiente, já viu esse filme: o Classic UI era a interface, ponto final, até 2015, quando o Service Portal chegou (release Fuji) prometendo autoatendimento decente para usuário final. Aí veio 2021 e o Now Experience apareceu com o UI Builder, prometendo a mesma coisa para quem trabalha filas e workspaces. Cada um foi anunciado como o futuro da plataforma no lançamento — e, olhando de fora, dá pra achar que a ServiceNow simplesmente não decide o que quer.

Não é indecisão. É o ritmo normal de uma plataforma corporativa que evolui junto com o resto da web: AngularJS era o padrão razoável em 2016, Web Components é o padrão razoável agora. O que muda de verdade não é a tecnologia em si — é que a interface que você constrói hoje vai durar bem mais do que o hype do lançamento, então vale entender onde a ServiceNow está investindo antes de apostar um projeto grande numa plataforma específica.

#três interfaces, três públicos, um só objetivo

A forma mais rápida de parar de confundir as três é lembrar que elas não competem — resolvem problemas de pessoas diferentes:

Classic UIService PortalNow Experience / UI Builder
para quemadministradores e desenvolvedoresusuário final (autoatendimento, catálogo)agentes e técnicos (filas, workspaces)
base técnicaJelly, formulários clássicosAngularJS 1.x + widgetsWeb Components (padrão W3C) + low-code
investimento atualmantido para administração, sem evolução de UXmantido, correções de bug, sem grandes novidadesonde a ServiceNow está colocando o orçamento de produto
novo projeto?só para configuração de plataformaviável, com plano de migração em menterecomendado para interfaces de agente

O ponto de atenção fica no meio da tabela. Pesquisando a posição oficial mais recente da ServiceNow, a notícia é que o Service Portal não está sendo descontinuado — ainda tem base de clientes grande demais para isso — mas o Employee Center — que hoje ainda roda sobre o mesmo stack técnico do Service Portal, com a evolução mais recente (Employee Center Pro) já construída em cima do UI Builder/Now Experience — é o caminho recomendado hoje para portal de autoatendimento novo, com investimento trimestral contínuo. Ou seja: se você está começando um portal do zero em 2026, a pergunta não é "Service Portal ainda funciona?" (funciona), é "esse projeto vai durar o suficiente para eu preferir já nascer no caminho que está recebendo investimento?".

#widget: quatro partes, uma responsabilidade cada

Enquanto o Service Portal continua vivo — e continua, na prática, em boa parte das instâncias que você vai encontrar por aí —, vale entender onde os projetos de widget mais degringolam. Um widget tem quatro peças: HTML template, CSS, Controller (AngularJS) e Server Script. O problema não é ter quatro peças, é quando uma invade o território da outra.

O deslize mais comum: lógica de negócio dentro do Controller. Validação de regra, cálculo, decisão de "pode ou não pode" — isso é domínio do servidor, não do cliente. É exatamente a mesma separação que já vale para Business Rules: quem orquestra não deveria ser quem decide.

html
<!-- HTML: só apresentação, três estados possíveis -->
<div class="request-status-widget">
  <div ng-if="c.loading" class="spinner"></div>
  <div ng-if="!c.loading && c.error" class="alert alert-danger">{{c.error}}</div>
  <div ng-if="!c.loading && !c.error">
    <h4>{{c.data.short_description}}</h4>
    <span class="badge" ng-class="c.badgeClass(c.data.state)">{{c.data.state_label}}</span>
    <button ng-if="c.data.pode_cancelar" ng-click="c.cancelar(c.data.sys_id)">cancelar solicitação</button>
  </div>
</div>
javascript
/* Controller: só orquestra evento e apresentação, zero regra de negócio */
function($scope) {
  var c = this;
  c.error = null;

  // mapa de apresentação — não é regra de negócio, é só "qual cor pra qual estado"
  c.badgeClass = function(state) {
    var mapa = { "1": "badge-info", "2": "badge-warning", "3": "badge-success", "-1": "badge-danger" };
    return mapa[String(state)] || "badge-default";
  };

  c.cancelar = function(sysId) {
    c.loading = true;
    c.server.get({ action: "cancelar", sys_id: sysId }).then(function(r) {
      if (r.data.ok) {
        c.data.state = r.data.novo_estado;
        c.data.state_label = r.data.novo_estado_label;
        c.data.pode_cancelar = false;
      } else {
        c.error = r.data.erro;
      }
      c.loading = false;
    });
  };
}
javascript
/* Server Script: recebe a ação, delega pro Script Include — a regra mora lá, não aqui */
(function() {
  if (input && input.action === "cancelar") {
    var svc = new CatalogRequestService();
    data = svc.cancelarSolicitacao(input.sys_id, gs.getUserID());
    return;
  }
  if (options.record_sys_id) {
    data = new CatalogRequestService().getDetalhes(options.record_sys_id);
  }
})();

Repare que o Server Script em si já não tem regra nenhuma — ele recebe a intenção ("cancelar isso") e devolve o resultado de um CatalogRequestService. Se amanhã a regra de "quando pode cancelar" mudar, você edita o Script Include, não caça a lógica espalhada em três arquivos do widget.

#UI Builder: quatro conceitos que resolvem 80% das dúvidas

No Now Experience você não escreve AngularJS — monta a página com Web Components no UI Builder, low-code. A curva de aprendizado é outra, mas quatro conceitos resolvem a maior parte das perguntas de quem está começando:

conceitoo que éboa prática
Page e Variantuma Page pode ter várias Variants — versões da mesma página por contexto ou personause Variant pra adaptar por role, não duplique a página inteira
Data Resourcefonte de dados configurável (Record, List, Scripted)um Data Resource primário por Page; componentes filhos recebem dado via property, não criam resource próprio
Client State Parameterestado local da página, sem ida ao servidoraba ativa, filtro selecionado — não persiste entre sessões; se precisar persistir, é Record, não State Parameter
Actions e Eventscomunicação entre componentes da mesma páginaprefira evento de página a acoplamento direto entre componentes

O padrão de erro mais comum aqui é o oposto do widget: em vez de lógica vazando pro lugar errado, é duplicação — cada componente filho criando o próprio Data Resource pra buscar o mesmo registro que o pai já tem. Além de gastar uma request HTTP a mais por componente, isso cria duas fontes de verdade pra um dado que deveria ser um só.

#performance de interface: meça antes que o usuário reclame

A percepção de lentidão do usuário não é proporcional ao tempo real — é dominada pela primeira tela e pela resposta a clique. Um formulário de 5 segundos não vira "hoje está lento", vira "o sistema é lento" — generalização que gruda.

métricametaonde medircausa mais comum quando estoura
carregamento de formulário< 2sTransaction Log (campo "total")Display BR pesado, muitos widgets simultâneos
paginação de lista< 1stempo de resposta de queryfalta de índice, ACL com script sem cache
busca no portal< 3sTransaction Log de searchcampo sem Text Index
time to interactive do portal< 3sChrome DevTools > Networkwidgets em sequência, server script pesado no load

A prática de melhor custo-benefício de todo o capítulo: pré-carregar dado via Display Business Rule + g_scratchpad, eliminando a chamada GlideAjax que normalmente acontece no onLoad. Em vez do form abrir, disparar um Client Script que pergunta pro servidor "me dá esse dado", e só então renderizar — o dado já chega dentro do form, sem round-trip extra.

Outras três que custam pouco e resolvem muito: ng-if em vez de ng-show para conteúdo pesado no Service Portal (ng-show renderiza tudo no DOM mesmo escondido, ng-if nem cria o elemento); limite prático de 8 a 10 widgets por página, porque cada um soma seu próprio processamento server-side na mesma request de renderização (e um widget marcado para carregar em block dispara, além disso, uma chamada AJAX própria); e lazy loading em lista longa, carregando mais conforme o usuário rola em vez de trazer tudo de uma vez.

#e agora?

  • Se você tem um Service Portal em produção, abra dois ou três widgets ao acaso e confira se o Controller tem if de regra de negócio — validação, cálculo de permissão, decisão de fluxo. Se tiver, é candidato a mover pro Script Include.
  • Meça o Transaction Log do formulário mais usado da sua instância antes de mexer em qualquer coisa. Se já estiver abaixo de 2 segundos, ótimo — guarde o número como baseline pra próxima mudança.
  • Este artigo completa a trilha de interface começada em precedência de campos entre camadas (capítulo 5, sobre Client Script e UI Policy) — os dois juntos cobrem a camada que o usuário final realmente toca, do form clássico ao portal.
LinkedIn X/Twitter WhatsApp ☕ me paga um café