Blog SAPienteSAPiente
CDS Views

CDS Aspects: como reutilizar campos, cálculos e anotações entre views

SAPiente9 de jun. de 2026· 5 min read

O fim do copy-paste de fórmulas entre views

Toda base CDS grande sofre do mesmo mal: a mesma fórmula — uma classificação ABC, um cálculo de idade, um bloco de anotações analíticas — copiada e colada em dezenas de views. Aí o threshold muda, alguém atualiza três views e esquece a quarta, e os relatórios começam a divergir sutilmente.

CDS Aspects resolvem isso. Um aspect é um artefato de reuso do ABAP CDS, definido com DEFINE ASPECT, que encapsula campos, cálculos e anotações repetidos num único objeto — uma single source of truth. As views consomem o aspect via BIND ASPECT e INCLUDE, e mudar a regra em um lugar reflete em todas elas.

Este guia mostra o que é um CDS Aspect, quando (e quando não) usá-lo, a sintaxe de definição e consumo, e exemplos prontos — classificação de clientes, cálculo de idade e padronização de anotações para analytics.

O essencial em 4 linhas

  • Um CDS Aspect agrupa campos, cálculos e anotações reutilizáveis — definido com DEFINE ASPECT.

  • Ele não gera view SQL e não pode ser consultado em SELECT; é consumido via BIND ASPECT + INCLUDE.

  • Funciona em view entities modernas — não em views DDIC clássicas (DEFINE VIEW) nem direto em OData/ABAP SQL.

  • Use quando o mesmo cálculo aparece em 3+ views; não use para lógica trivial ou de uma view só.

Não confunda os dois "aspects" do ABAP CDS: este post trata do aspect de reuso (campos/anotações). Existe também um artefato diferente, também chamado "aspect", usado em access control (o DEFINE ASPECT dentro de DEFINE ACCESSPOLICY, ligado ao pfcg_auth) — esse é assunto do guia de CDS Access Control (DCL). Mesmo nome, propósitos opostos.

O que é um CDS Aspect?

Um CDS Aspect é um artefato de reuso em tempo de modelagem — ele não tem persistência própria nem representação SQL no banco. Pense nele como um "bloco" de elementos calculados e anotações que você define uma vez e injeta em várias view entities. Ele resolve o problema clássico de duplicação de fórmulas: quando você percebe que está copiando o mesmo CASE de view em view, é hora de extrair para um aspect.

Por não ser uma data source, um aspect não pode aparecer num FROM e não compila em SELECT FROM aspect. Quem precisa de uma fonte consultável usa uma view; o aspect entra dentro dessa view.

Quando usar (e quando não usar) um aspect?

A regra prática para extrair um aspect é a duplicação: se o mesmo cálculo ou bloco de anotações aparece em três ou mais views, vale a indireção. Abaixo disso, costuma não compensar o overhead de criar o aspect, fazer o bind e o include.

Bons casos de uso:

  • Cálculos repetidos em várias views (KPIs, conversões, classificações de negócio como faixas ABC ou etárias).

  • Blocos de anotações reutilizáveis — semânticas de moeda/quantidade ou anotações analíticas para SAC e Analytics for RAP.

  • Arquiteturas CDS grandes onde a manutenção centralizada é crítica: mudar a regra num lugar deve refletir em N views.

Quando evitar:

  • Só 1 ou 2 views consomem a lógica — mantenha o cálculo direto na view.

  • A lógica envolve loops, exceções ou estado — aspect só aceita expressões SQL declarativas. Para procedural, use AMDP ou CDS Scalar Function.

  • Você precisa de joins ou associations complexas — isso pertence à view, não ao aspect (que não tem FROM).

  • A lógica é específica de uma única view — não generalize por estética.

Anti-padrão "kitchen sink": um único aspect com 30 campos misturando moeda, datas, classificações e formatação. Quebre em aspects coesos (Z_CURRENCY_ASPECT, Z_DATE_FORMAT_ASPECT), cada um com uma responsabilidade clara.

Como definir um aspect (DEFINE ASPECT)?

A definição declara os campos de entrada (tipados) e os elementos calculados que os usam. O esqueleto é simples:

[@aspect_annotations]
define aspect aspect_name
{
   field1 : type1;                       // campo de entrada
   field2 : type2;
   [@field_annotation]
   calculated_field : <expression>;      // elemento calculado
}

Veja um exemplo real: uma classificação ABC de clientes que várias views analíticas precisam — em vez de repetir o CASE em cada uma, encapsule no aspect:

@EndUserText.label: 'Customer ABC Classification'
define aspect Z_CUSTOMER_ABC_CLASSIFICATION
{
   total_revenue : abap.curr( 15, 2 );

   @EndUserText.label: 'ABC Class'
   case
      when total_revenue >= 1000000 then 'A'
      when total_revenue >=  250000 then 'B'
      else                               'C'
   end as ABCClass,

   @EndUserText.label: 'Strategic Customer Flag'
   case
      when total_revenue >= 1000000 then cast( 'X' as abap.char( 1 ) )
      else                               cast( ''  as abap.char( 1 ) )
   end as IsStrategic
}

Como consumir um aspect (BIND + INCLUDE)?

O consumo tem duas partes. Primeiro o bind aspect mapeia os campos de entrada do aspect para os elementos da view, usando o operador =>. Depois, na lista de elementos, o include traz os campos calculados. As cláusulas-chave:

  • bind aspect <nome> ( campo_entrada => $projection.campo_local, ... ) — mapeia as entradas.

  • include <nome>.* — traz todos os campos do aspect; include <nome>.campo traz um só.

  • excluding { campo1, campo2 } — exclui campos específicos.

  • excluding bound elements — remove automaticamente os campos que foram só de entrada do bind, evitando duplicação. Padrão recomendado ao usar include ... .*.

Juntando tudo, a view de relatório consome o aspect ABC assim:

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Sales Report by Customer'
define view entity Z_SALES_REPORT_BY_CUSTOMER
  as select from Z_SALES_AGGREGATED as S

  bind aspect Z_CUSTOMER_ABC_CLASSIFICATION (
     total_revenue => $projection.YearlyRevenue
  )

{
   key S.customer_id,
       S.customer_name,
       S.YearlyRevenue,
       S.country,

       include Z_CUSTOMER_ABC_CLASSIFICATION.*
              excluding bound elements
}

Uma segunda view, num contexto totalmente diferente (pipeline comercial), reusa o mesmo aspect — só muda o campo que ela liga em total_revenue:

  bind aspect Z_CUSTOMER_ABC_CLASSIFICATION (
     total_revenue => $projection.HistoricalRevenue
  )

Resultado: a regra de classificação vive em um único lugar. Se o threshold mudar de 1M para 1,5M, você altera no aspect e ambas as views refletem na hora. Não é exatamente isso que a gente quer de um modelo de dados sustentável?

Como passar variáveis de sessão no bind?

O valor ligado a um campo de entrada não precisa vir de uma coluna — pode ser uma variável de sessão, como $session.system_date. Isso é perfeito para cálculos relativos à data atual, como idade. Veja um aspect que calcula idade e faixa etária a partir da data de nascimento:

@EndUserText.label: 'Age and Age Bracket Calculation'
define aspect Z_AGE_CALCULATION
{
   birth_date    : abap.datn;
   reference_dt  : abap.datn;

   @EndUserText.label: 'Age in Years'
   division( days_between( birth_date, reference_dt ), 365, 0 ) as AgeInYears,

   @EndUserText.label: 'Age Bracket'
   case
      when division( days_between( birth_date, reference_dt ), 365, 0 ) < 25 then 'Under 25'
      when division( days_between( birth_date, reference_dt ), 365, 0 ) < 35 then '25-34'
      when division( days_between( birth_date, reference_dt ), 365, 0 ) < 50 then '35-49'
      when division( days_between( birth_date, reference_dt ), 365, 0 ) < 65 then '50-64'
      else                                                                       '65+'
   end as AgeBracket
}

Na view de funcionários, a data de referência é ligada à data do sistema via sessão:

define view entity Z_EMPLOYEE_DEMOGRAPHICS
  as select from Z_EMPLOYEE as E

  bind aspect Z_AGE_CALCULATION (
     birth_date   => $projection.BirthDate,
     reference_dt => $session.system_date
  )

{
   key E.employee_id,
       E.full_name,
       E.department,
       E.BirthDate,

       include Z_AGE_CALCULATION.*
              excluding bound elements
}

Como padronizar anotações analíticas com aspects?

Um dos usos mais valiosos do aspect nem é cálculo — é garantir que toda view de KPI exponha as medidas com as mesmas anotações analíticas, necessárias para SAC e Analytics for RAP. Em vez de repetir cinco linhas de anotação em cada view, você as concentra no aspect:

define aspect Z_KPI_MEASURE_ANNOTATIONS
{
   raw_amount   : abap.curr( 15, 2 );
   raw_currency : abap.cuky;

   @EndUserText.label: 'KPI Amount'
   @Semantics.amount.currencyCode: 'KpiCurrency'
   @AnalyticsDetails.measure.dataPoint.criticalityCalculation.improvementDirection: #INCREASE
   raw_amount as KpiAmount,

   raw_currency as KpiCurrency
}

Cada view de KPI (revenue, EBITDA, margem) faz o bind e ganha automaticamente o pacote completo de anotações — sem omissões e sem divergência entre relatórios.

Como incluir só alguns campos ou excluir outros?

Nem toda view quer todos os campos do aspect. O include aceita seleção pontual, e o excluding remove campos específicos do .*:

// Só um campo do aspect
{
   key T.ticket_id,
       include Z_SLA_COMPLIANCE.SlaStatus
}

// Todos, menos um
{
   key T.ticket_id,
       include Z_SLA_COMPLIANCE.*
              excluding { ResolutionMinutes }
              excluding bound elements
}

Quais são as limitações de um CDS Aspect?

O aspect é poderoso para reuso, mas tem fronteiras claras. Conhecê-las evita tentar usá-lo onde ele não cabe.

Restrição

Detalhe

Sem SELECT direto

Não é view; não aparece em FROM.

Sem OData / ABAP SQL diretos

Consuma via uma view que inclui o aspect.

Bind manual obrigatório

Você mapeia explicitamente cada campo de entrada.

Sem composições/aggregates

Não substitui modelagem de aggregate root.

Sem joins próprios

Não tem FROM, logo não declara joins.

Só em view entities

Views DDIC clássicas (DEFINE VIEW) não suportam.

Quais são as boas práticas e armadilhas?

O aspect entrega integridade por ser single source of truth: uma correção propaga para todas as views, e uma omissão se torna impossível, já que a referência é direta. Para extrair valor disso de forma sustentável, vale um checklist mental:

  • Extraia só quando a lógica aparece em 3+ views — reuso real, não abstração prematura.

  • Mantenha o aspect coeso: uma responsabilidade clara por aspect.

  • Documente o aspect e cada campo com @EndUserText.label.

  • Dê nomes que descrevem a regra (Z_CUSTOMER_ABC_CLASSIFICATION), nunca Z_ASPECT_001.

  • Use excluding bound elements com include ... .* para não duplicar os campos de entrada.

  • Coloque as anotações semânticas/analíticas no aspect, não nas views que o consomem.

Perguntas frequentes

Posso consultar um CDS Aspect com SELECT?

Não. O aspect não é uma data source e não tem representação SQL no banco — SELECT FROM aspect não compila. Ele só é consumido dentro de uma view entity, via bind aspect e include. Se você precisa de algo consultável, crie uma view e inclua o aspect nela.

Qual a diferença entre CDS Aspect e CDS Scalar Function?

O aspect encapsula expressões SQL declarativas (campos, CASE, funções built-in) reutilizadas entre views. A Scalar Function devolve um valor único e pode ser chamada em SELECT, WHERE e ABAP. Para lógica procedural ou um valor consumível em qualquer lugar, use a função; para um bloco de elementos/anotações reusado em views, use o aspect.

Aspect funciona em views DDIC clássicas (DEFINE VIEW)?

Não. CDS Aspects são suportados apenas em view entities modernas (e projection views transacionais). A sintaxe antiga DEFINE VIEW baseada em DDIC não os reconhece. É mais um motivo para migrar suas views para view entities nas releases atuais.

O que faz o excluding bound elements?

Ao usar include aspect.*, os campos que você apenas mapeou no bind (as entradas) viriam duplicados na lista de elementos. O excluding bound elements remove esses campos de entrada automaticamente, deixando só os calculados. É o padrão recomendado para evitar duplicação no SELECT list.

Aspect e o "aspect" de access control são a mesma coisa?

Não — apenas compartilham o nome. O aspect deste post é de reuso de campos e anotações. O outro, usado em DCL com DEFINE ACCESSPOLICY e pfcg_auth, serve para controle de acesso por linha. São artefatos distintos com propósitos diferentes; cuidado para não misturá-los em pesquisas e documentação.

Conclusão

CDS Aspects trazem ao ABAP CDS o que faltava para modelos grandes: reuso real de cálculos e anotações, com uma fonte única de verdade. Defina a regra uma vez com DEFINE ASPECT, ligue as entradas com bind aspect e traga os campos com include — e nunca mais persiga uma fórmula divergente espalhada por dez views.

A regra de bolso continua valendo: extraia um aspect quando a duplicação for real (3+ views), mantenha-o coeso e nomeie pela regra de negócio. Reserve a indireção para onde ela paga — e deixe joins, persistência e lógica procedural para os artefatos certos.

Referências oficiais

TagsClean CoreDesenvolvedor
Avalie este conteúdo
para avaliar
SAPiente
@sapiente

Blog sobre SAP, ABAP, BTP, Fiori e tudo que envolve o ecossistema SAP.

Ver perfil →
FacebookInstagramYouTubeTwitter

Comentários 0

Entre na conversa

Faça login para deixar seu comentário neste artigo.

Ainda sem comentários

Seja o primeiro a comentar este artigo.