Buenas prácticas en vistas CDS

A la hora de desarrollar nuestras vistas CDS, es importante seguir una serie de buenas prácticas como haríamos en cualquier otro lenguaje de programación. Estas buenas prácticas no van a optimizar nuestro código, pero si van a facilitar la vida a SAP y al resto de programadores que en el mismo proyecto o en un futuro tengan que ‘batallar’ con tu código.

Dividiremos nuestras vistas CDS en tres tipos:

  • Basic Interface Views
  • Composite Interface Views
  • Consumption Views

Indicaremos en cada vista a qué tipo pertenece a través de la siguiente anotación CDS ubicada en la cabecera de nuestro código:

@VDM.viewType:#TIPO

Esta anotación permitirá a SAP reconocer más fácilmente a qué tipo de vista hace referencia, y de esta forma, poder organizar internamente de manera más clara el modelo virtual de nuestros datos. De la misma forma, ayudará al programador a saber sobre qué tipo de vista esta realizando alguna modificación, para poder llevar a cabo las buenas prácticas que a continuación listaremos.

Basic Interface Views

Las vistas CDS básicas las anotaremos de la siguiente forma:

@VDM.viewType:#BASIC

Este tipo de vistas CDS serán las encargadas obtener la información de la base de datos. El acceso a la base de datos está limitada a las vistas CDS de tipo #BASIC, y el resto de vistas obtendrán los datos a partir de estas.

Una vista CDS básica obtiene datos de una única tabla de base de datos. Lo que hacemos con estas vistas es recuperar aquellos campos que nos interesan de cada tabla de base de datos con la que vamos a trabajar. Actúan como una especie de ‘espejo’ de la base de datos en formato vista CDS.

Utilizamos este tipo de vistas para incluir las anotaciones CDS semánticas, es decir, aquellas anotaciones CDS que aportan valor a nuestros campos, como podrían ser las anotaciones que asignan una unidad o moneda a un campo cantidad o importe.

Ejemplo de anotación CDS semántica:

 @Semantics.amount.currencyCode: 'Moneda'
 Flight.price      as Precio,
 @Semantics.currencyCode: true
 Flight.currency   as Moneda,

Composite Interface Views

Las vistas CDS composite las anotaremos de la siguiente forma:

@VDM.viewType:#COMPOSITE

Las vistas CDS composite se construyen a través de vistas básicas y de otras vistas composite. Actúan como puente entre las vistas de tipo #BASIC y de tipo #CONSUMPTION.

Las vistas CDS composite proyectan datos para un propósito especifico, como puede ser una aplicación Fiori o varias. Lo que hacemos en este tipo de vistas CDS es combinar las diferentes fuentes de datos; a través de joins y asociaciones entrelazamos los datos de las diferentes vistas básicas que hemos construido o, si el desarrollo es más complejo, entrelazaremos otras vistas composite para exponer los datos en esta vista.

En este tipo de vistas haremos los cálculos de datos necesario a través de funciones CDS, y será nuestra vista CDS referencia de donde obtendremos todos los datos posibles para el propósito del desarrollo.

En esta vista no realizaremos anotaciones CDS, ya que el objetivo de este tipo de vistas es poder desarrollar código reutilizable, útil para distintas aplicaciones que trabajen con un mismo tipo de semántica.

Consumption Views

Las vistas CDS de consumo las anotaremos de la siguiente forma:

@VDM.viewType:#CONSUMPTION

Las vistas de consumo se construyen a través de la composite view. Lo que hacemos en este tipo de vista es realizar un SELECT sobre nuestra vista composite para exponer SOLO aquellos campos que requiere nuestra aplicación.

Las vista de consumo no son reutilizables. Este tipo de vistas son las que exponen directamente en nuestro servicio y son las que contienen toda la información requerida para nuestro aplicativo. De este modo, podríamos tener dos aplicaciones Fiori con su vista de consumo respectiva para cada una, que consumen los datos a través de la misma vista composite, pero que exponen diferentes campos, haciendo uso del concepto ‘reutilizable’ de las vistas composite.

En esta vista CDS, podemos incluir anotaciones CDS relativas a una ayuda de búsqueda para un campo específico, por ejemplo. No obstante, las anotaciones CDS para construir nuestra aplicación Fiori se desarrollarán en otro fichero llamado Metadata Extensions. Para poder referenciar nuestro Metadata Extensions a nuestra vista de consumo, haremos uso de la anotación CDS siguiente en la cabecera de nuestra vista:

@Metadata.allowExtensions: true

Metadata Extensions

El objeto Metadata Extensions se utilizará para añadir las anotaciones CDS de interfaz que necesitemos para nuestra aplicación Fiori. Esta separación de la vista de consumo no va a optimizar el código ni la ejecución del aplicativo, pero si que va a permitir tener organizado nuestro código ya que en vistas de consumo muy extensas, tener que buscar el campo para modificar una anotación o añadir una nueva puede ser algo mas costoso. De este modo, sabemos que todas las anotaciones a modificar o añadir estarán incluidas en este objeto.

Para poder crear el Metadata Extensions pulsamos botón derecho sobre el directorio de nuestro paquete ‘Core Data Services’ → New → Metadata Extension.

Al crearse el Metadata Extension aparecerá el siguiente código por defecto:

annotate view nombre_vista with
{
}

Deberemos indicar el nombre de nuestra vista consumo al que hará referencia. Indicando el nombre, y añadiendo la anotación CDS indicada en el punto anterior sobre nuestra vista de consumo, tendremos asociados ambos objetos y automáticamente se agregaran los metadatos a nuestra vista en forma de anotaciones CDS.

Otras buenas prácticas

Como complemento a las buenas prácticas en la forma de estructurar nuestras vistas CDS para desarrollar un código reutilizable y entendible, vamos a ver algunos puntos interesantes que deberemos tener en cuenta para realizar código optimizado:

  • Utilizar join solo cuando sea necesario. Es importante entender el potencial de las asociaciones, que vimos en artículos anteriores, las cuales solo se ejecutan cuando solicitamos desde nuestra aplicación dichos campos en el informe. Todo las tablas enlazadas mediante join (INNER,LEFT,OUTER o CROSS) se van a ejecutar siempre, sin importar qué campos necesitamos realmente listar al usuario. Por ello, utilizaremos solamente los join cuando queramos excluir datos o sea obligatorio que se cumpla alguna condición concreta; en caso contrario, haremos uso de asociaciones.
  • Antes de crear una vista CDS que extraiga datos de una o diferentes tablas, es interesante comprobar si existe una vista CDS estándar que nos cuadre para nuestro aplicativo. En este enlace podéis revisar como buscar vistas CDS estandar.
  • Si estás comenzando con el desarrollo de aplicaciones SAP Fiori a partir de vistas CDS, recomiendo que expongais vuestras vistas a partir de un servicio oData creado manualmente a través de la transacción SEGW. Esto ayudará a comprender y mejorar en la implementación de servicios oData.

Organizando nuestra vista CDS de ejemplo

Para terminar con este artículo, vamos a implementar las buenas prácticas en la estructura de nuestras vistas CDS de ejemplo. Si recordamos nuestra vista CDS desarrollada en artículos anteriores, partimos del siguiente código:

define view ZCDS_EJEMPLO_BLOG
  as select from sflight as Flight
  association [0..1] to spfli                as Schedule    on  Flight.carrid = Schedule.carrid
                                                            and Flight.connid = Schedule.connid
{
      @UI.lineItem: [ { position: 10 } ]
      @UI.selectionField: [{position: 10}]
      @Search: { defaultSearchElement: true, fuzzinessThreshold: 1 }
      @Consumption.valueHelpDefinition: [ { entity:  { name:    'ZCDS_COMPANYIA_AEREA',
                                                 element: 'carrid' } } ]
  key Flight.carrid     as IdEmpresa,
      @UI.lineItem: [ { position: 20 } ]
      @UI.selectionField: [{position: 20}]
      @Search: { defaultSearchElement: true, fuzzinessThreshold: 1 }
  key Flight.connid     as CodVuelo,
      @UI.selectionField: [{position: 30}]
      @UI.lineItem: [ { position: 30 } ]
  key Flight.fldate     as Fecha,
      @Semantics.amount.currencyCode: 'Moneda'
      @UI.lineItem: [ { position: 40 } ]
      Flight.price      as Precio,
      @Semantics.currencyCode: true
      Flight.currency   as Moneda,
      Schedule.cityfrom as CiudadOrigen,
      Schedule.cityto   as CiudadDestino,
      @Semantics.quantity.unitOfMeasure: 'UnidadDistancia'
      @UI.lineItem: [ { position: 50 } ]
      Schedule.distance as Distancia,
      @Semantics.unitOfMeasure: true
      Schedule.distid   as UnidadDistancia
}

Como vemos, hemos creado nuestra lógica en una única vista CDS, donde incluimos la extracción de datos, las anotaciones CDS de interfaz, las anotaciones semánticas… Este código funciona correctamente, pero no sigue ninguna buena práctica. Vamos a organizar nuestro código en dos vistas CDS, una vista de tipo #COMPOSITE, donde haremos la extracción de nuestros datos, y una vista de tipo #CONSUMPTION donde expondremos los datos que queremos para nuestra aplicación.

Vista de tipo #COMPOSITE

@AbapCatalog.sqlViewName: 'ZVWIEJBLG'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Vista composite para ejemplo'
@VDM.viewType: #COMPOSITE

define view ZCDS_I_EJEMPLO_BLOG 
as select from sflight as Flight
  association [0..1] to spfli                as Schedule    on  Flight.carrid = Schedule.carrid
                                                            and Flight.connid = Schedule.connid
{

  key Flight.carrid     as IdEmpresa,
  key Flight.connid     as CodVuelo,
  key Flight.fldate     as Fecha,
      @Semantics.amount.currencyCode: 'Moneda'
      Flight.price      as Precio,
      @Semantics.currencyCode: true
      Flight.currency   as Moneda,
      Schedule.cityfrom as CiudadOrigen,
      Schedule.cityto   as CiudadDestino,
      @Semantics.quantity.unitOfMeasure: 'UnidadDistancia'
      Schedule.distance as Distancia,
      @Semantics.unitOfMeasure: true
      Schedule.distid   as UnidadDistancia
}

En la vista de tipo COMPOSITE haremos los cruces necesarios entre tablas o vistas básicas para la extracción de los datos. En esta vista expondremos todos los datos que puedan resultar útiles para aplicativos que trabajen con este tipo de “objeto”, en este caso, Vuelos. Además, añadiremos las anotaciones CDS de tipo semánticas para indicar los campos Cantidad e Importe.

Vista de tipo CONSUMPTION

@AbapCatalog.sqlViewName: 'ZVEJEMBLOG'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Vista cds ejemplo blog'
@VDM.viewType: #CONSUMPTION
@OData.publish: true
@Metadata.allowExtensions: true

define view ZCDS_EJEMPLO_BLOG
  as select from ZCDS_I_EJEMPLO_BLOG

{
       @Consumption.valueHelpDefinition: [ { entity:  { name:    'ZCDS_COMPANYIA_AEREA',
                                                  element: 'carrid' } } ]
  key  IdEmpresa,
  key  CodVuelo,
  key  Fecha,
       Precio,
       CiudadOrigen,
       CiudadDestino,
       Distancia
}

En la vista de consumo realizamos un SELECT sobre nuestra vista de tipo COMPOSITE. En esta vista exponemos solo aquellos campos que necesitamos para el fin de nuestro aplicativo. Es importante exponer solamente aquello que se requiere ya que, si exponemos campos que se obtienen por asociación que nunca vamos a utilizar, estamos forzando a nuestro código a realizar más consultas de las que requiere, y por ende, penalizar su rendimiento.

Anotamos la vista para indicar que es de tipo #CONSUMPTION e incluimos la anotaciones que permite extender la vista a través de un Metadata Extensions. Como podemos observar, han desaparecido casi todas las anotaciones CDS de esta vista, salvo la de la ayuda de búsqueda que podemos ponerla a este nivel de vista de consumo. Por último, solo queda crear nuestro Metadata Extensiones y anotar los campos correspondientes.

Metadata Extensions

Para crear nuestro Metadata Extensiones, pulsamos en botón derecho sobre la carpeta “Core Data Services” → New → Metadata Extensions.

Creando Metadata Extension

Nuestro Metadata Extension tendrá la siguiente forma:

@Metadata.layer: #CUSTOMER
@Search.searchable: true
annotate view ZCDS_EJEMPLO_BLOG with
{
  @UI.lineItem: [ { position: 10 } ]
  @UI.selectionField: [{position: 10}]
  @Search: { defaultSearchElement: true, fuzzinessThreshold: 1 }
  IdEmpresa;
  @UI.lineItem: [ { position: 20 } ]
  @UI.selectionField: [{position: 20}]
  @Search: { defaultSearchElement: true, fuzzinessThreshold: 1 }
  CodVuelo;
  @UI.selectionField: [{position: 30}]
  @UI.lineItem: [ { position: 30 } ]
  Fecha;
  @UI.lineItem: [ { position: 40 } ]
  Precio;
  @UI.lineItem: [ { position: 50 } ]
  Distancia;

}

En primer lugar, este tipo de fichero nos obliga a indicar que vista estamos anotando a través de la línea ‘annotate view …. ‘. Aquí debemos indicar nuestra vista de consumo, la cual hemos anotado para permitir su extension. En caso de no haber anotado nuestra vista de consumo, tendremos un error en dicha línea indicando que no es posible.

En segundo lugar, pondremos aquellos campos que queremos anotar. A diferencia de las vistas CDS, donde se utiliza la coma para separar cada campo, en los Metadata Extension utilizaremos punto y coma.

Por último, indicaremos las anotaciones CDS que requiramos para nuestra aplicación. De esta forma, obtenemos un código mucho más estructurado y con lógicas separadas, facilitando la vida a nivel de compresión y de desarrollo a futuros programadores que tengan que meter mano en nuestro código.

Conclusión

Seguir o no las buenas prácticas de desarrollo esta en la mano de cada uno. Por norma general, llevamos a cabo los proyectos junto a un equipo, y no solemos permanecer en una empresa de por vida. Por ello, es importante que nuestro código sea entendible para nuestro propio equipo y/o para futuros programadores que deban modificarlo.

Además, de esta forma ayudamos a SAP a entender más fácilmente nuestro código y poder estructurar internamente las vistas como corresponde. Sin olvidar que separar correctamente la lógica en los tipos de vista correctos, nos proporcionará un código limpio y reutilizable en futuros desarrollos.

Deja un comentario