sapui5tutors SAPUI5 • Fiori • SAP BTP Step-by-step tutorials Real project examples Interview Q&A
Practical SAPUI5 • Fiori • SAP BTP tutorials and interview prep

CDS Views (DDLS) in ABAP: A Practical Introduction

Every modern ABAP application — RAP, Fiori Elements, OData services — sits on top of CDS views. If you are coming from classic ABAP, where the data model was a pile of transparent tables and the occasional database view, CDS (Core Data Services, written in the DDLS language) can feel abstract at first. In practice, it is simpler than it looks: CDS views are just a way to define your data model in the database layer, with semantics attached.


What a CDS view actually is

A CDS view is a data definition written in DDLS (Data Definition Language Syntax) that lives in the ABAP repository and gets created as a view in the HANA database on activation. Unlike a classic SE11 database view, a CDS view carries annotations — metadata that tells consumers what the data means, not just what it contains.

Here is a minimal example:

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Customer basic data'
define view entity ZI_Customer
  as select from zcustomer_table
{
  key customer_id   as CustomerId,
      name          as Name,
      city          as City,
      country       as Country
}

A few things to notice. The key marks the primary key. The aliases after as define the element names that consumers see. And the annotations at the top control behavior: no view enhancement, no authorization check on this basic interface view.

A CDS view is not just a SELECT statement with a name. The annotations are the point — they turn raw columns into a consumable data model.

The layered model: why there is more than one view

In real projects you rarely expose a single CDS view directly. The standard pattern has layers:

  • Interface views (like ZI_Customer above) — close to the database tables, reusable across applications. Think of them as the stable foundation.
  • Projection views — shaped for a specific consumer. They select from interface views, add UI and OData annotations, and define what a Fiori app or API actually sees.

The projection view is where annotations like @UI.lineItem or @OData.publish: true live. The interface view stays clean and reusable; the projection view is tailored. When requirements change for one app, you adjust its projection — the foundation does not move.


Annotations: where the magic happens

Annotations are the reason CDS replaced so many older techniques. A few categories you will meet constantly:

Semantics — @Semantics.amount.currencyCode: 'Currency' tells the framework that a field is a monetary amount tied to a currency field. The UI then formats it correctly without any code.

UI — @UI.lineItem: [{ position: 10 }] places a field on a Fiori Elements list report. @UI.identification puts it on the object page header. The app UI is, to a large extent, generated from these.

Consumption and OData — @OData.publish: true exposes the view as an OData service. @Search.searchable enables Fiori search on it.

Here is the customer view with UI annotations added, the way a projection view would carry them:

@EndUserText.label: 'Customer projection for sales app'
@UI.headerInfo: { typeName: 'Customer',
                  title: { value: 'Name' } }
define view entity ZC_Customer
  as projection on ZI_Customer
{
  @UI.lineItem: [{ position: 10 }]
  @UI.identification: [{ position: 10 }]
  key CustomerId,

  @UI.lineItem: [{ position: 20 }]
  Name,

  @UI.lineItem: [{ position: 30 }]
  City,

  Country
}

No UI code was written. A Fiori Elements app generated on top of this projection renders a list report with three columns and an object page header — driven entirely by annotations.


Associations: relationships without joins in every query

CDS views define relationships with association, and the framework resolves them on demand:

define view entity ZI_Order
  as select from zorder_table
  association [1..1] to ZI_Customer as _Customer
    on $projection.customer_id = _Customer.CustomerId
{
  key order_id    as OrderId,
      customer_id as CustomerId,
      total       as Total,
      _Customer
}

The _Customer association is exposed as a navigation path. Consumers expand it when they need customer data (?$expand=_Customer in OData) and ignore it when they do not. You define the relationship once, in the model, instead of rewriting the join in every report.

Define relationships once as associations in the data model. Every consumer — OData, analytics, RAP — reuses them instead of re-implementing joins.

Where CDS views sit in the stack

To place it all on one map:

Database tables (persistence)
        ↓
Interface CDS views (reusable data model, associations)
        ↓
Projection CDS views (consumer-specific shaping + annotations)
        ↓
Service definition / binding → OData → Fiori Elements app

In RAP, the behavior definition attaches to these views to add transactional behavior. In analytics, the same views feed queries. The CDS layer is the single data model everything else builds on — which is why "learn CDS properly" is the most repeated advice for ABAP developers moving to the cloud stack. It is not hype. Everything downstream depends on getting this layer right.