TDEV
İletişime Geçin
BTP 9 dk okuma · Ocak 2025

CAP ile BTP'de Cloud-Native Geliştirme

SAP Cloud Application Programming Model — Node.js veya Java, CDS şema-first yaklaşım, hybrid development, S/4HANA remote service entegrasyonu ve multi-tenancy. BTP'de uygulama geliştirmenin doğru çerçevesi.

TDEV

TDEV Teknik Ekibi

Ocak 2025 · SAP BTP · Cloud-Native

SAP Cloud Application Programming Model (CAP), BTP üzerinde cloud-native uygulama geliştirmek için tasarlanmış açık kaynaklı bir framework. Node.js veya Java ile yazılabilen, CDS tabanlı şema tanımı, otomatik OData servisleri ve HANA Cloud entegrasyonuyla — SAP ekosistemi için Spring Boot benzeri bir deneyim sunuyor.

CAP mı RAP mı?

Projede sık karşılaştığımız soru bu. İkisi de CDS kullanıyor ve OData üretiyor, ama çalıştıkları ortam ve hedefleri farklı:

RAPCAP
Çalıştığı ortamS/4HANA backend (ABAP)BTP — Cloud Foundry veya Kyma
RuntimeABAPNode.js veya Java
KullanımIn-app extensionSide-by-side extension, özgün uygulama
VeriSAP tabloları (HANA)Özgün model veya remote service
OData versiyonuV4 (otomatik)V4 veya V2 (yapılandırılabilir)

Pratik kural: S/4HANA'nın içini değiştiriyorsanız → RAP. BTP'de yeni bir uygulama veya S/4HANA'yı dışarıdan genişletiyorsanız → CAP.

Proje Yapısı

CAP UYGULAMA MİMARİSİ CDS ŞEMA db/schema.cds Entity · Aspect · Type SERVİS TANIMI srv/service.cds Projection · @restrict SERVİS HANDLER srv/service.js before · on · after API ÇIKTISI OData V4 Fiori · UI5 · REST HANA Cloud (Üretim) SQLite (Geliştirme) cds watch · hot reload S/4HANA (Remote) BTP Destination OData V2/V4 proxy
CDS şemasından OData API'ye CAP uygulama akışı — yerel SQLite ile BTP'de HANA arasında şeffaf geçiş
my-cap-app/
├── db/
│   └── schema.cds          # Veri modeli (entities, types, aspects)
├── srv/
│   ├── orders-service.cds  # Servis tanımı
│   └── orders-service.js   # Node.js handler (veya .java)
├── app/                    # Opsiyonel: Fiori/UI5 uygulaması
├── test/                   # Jest veya JUnit testleri
├── .cdsrc.json             # CAP yapılandırması
└── package.json

CDS Data Model

CAP'ta her şey şemayla başlar. managed aspect, otomatik olarak createdAt, createdBy, modifiedAt, modifiedBy alanlarını ekler.

// db/schema.cds
namespace tdev.orders;

entity Orders : managed {
  key ID       : UUID;
      customer : String(100) @mandatory;
      date     : Date;
      status   : String(20) default 'OPEN'
                 @assert.range enum { OPEN; CONFIRMED; CLOSED; };
      items    : Composition of many OrderItems on items.order = $self;
}

entity OrderItems {
  key ID       : UUID;
      order    : Association to Orders;
      product  : String(100);
      quantity : Integer  @assert.range: [1, 9999];
      price    : Decimal(13,2);
      total    : Decimal(13,2); // virtual — handler hesaplıyor
}

Servis Tanımı

// srv/orders-service.cds
using tdev.orders from '../db/schema';

@path: '/orders'
service OrdersService {

  @Capabilities: {
    Insertable: true,
    Updatable:  true,
    Deletable:  true
  }
  entity Orders    as projection on orders.Orders;
  entity OrderItems as projection on orders.OrderItems;

  /** Özel action: tüm siparişi kapat */
  action closeOrder(orderID: UUID) returns Orders;

  /** Aylık sipariş özeti */
  function monthlySummary(year: Integer, month: Integer)
    returns array of { month: String; total: Decimal; count: Integer; };
}

Service Handler (Node.js)

// srv/orders-service.js
const cds = require('@sap/cds')

module.exports = class OrdersService extends cds.ApplicationService {

  async init() {
    const { Orders, OrderItems } = this.entities

    // CREATE öncesi validasyon
    this.before('CREATE', Orders, async (req) => {
      const { customer, date } = req.data
      if (!customer?.trim()) {
        return req.reject(400, 'Müşteri adı boş olamaz')
      }
      if (date && new Date(date) < new Date()) {
        return req.reject(400, 'Sipariş tarihi geçmişte olamaz')
      }
    })

    // READ sonrası hesaplanan alan
    this.after('READ', OrderItems, (items) => {
      items.forEach(item => {
        item.total = (item.quantity ?? 0) * (item.price ?? 0)
      })
    })

    // Custom action
    this.on('closeOrder', async (req) => {
      const { orderID } = req.data
      const count = await UPDATE(Orders)
        .set({ status: 'CLOSED' })
        .where({ ID: orderID, status: { '!=': 'CLOSED' } })
      if (!count) req.reject(404, `Sipariş bulunamadı: ${orderID}`)
      return SELECT.one(Orders).where({ ID: orderID })
    })

    return super.init()
  }
}

Hybrid Development

CAP'ın en değerli özelliklerinden biri: lokal geliştirmede SQLite (hatta in-memory), üretimde HANA Cloud. Geçiş için tek satır config değişikliği yeterli.

// .cdsrc.json
{
  "requires": {
    "[development]": {
      "db": {
        "kind": "sqlite",
        "credentials": { "url": ":memory:" }
      }
    },
    "[production]": {
      "db": { "kind": "hana-cloud" }
    }
  }
}
# Geliştirme: in-memory SQLite, hot reload
cds watch

# HANA Cloud'a deploy
cds deploy --to hana

# MTA ile BTP'ye deploy (önerilen)
mbt build
cf deploy mta_archives/my-cap-app.mtar

S/4HANA Remote Service Entegrasyonu

BTP'deki CAP uygulamasından S/4HANA verisi okumak için SAP Cloud SDK ve destination hizmeti kullanılır. CAP bu karmaşıklığı büyük ölçüde soyutluyor:

// package.json — cds.requires bölümü
{
  "cds": {
    "requires": {
      "S4SalesOrder": {
        "kind": "odata-v4",
        "model": "srv/external/API_SALES_ORDER_SRV",
        "credentials": {
          "destination": "S4HANA_SYSTEM",
          "requestTimeout": 30000
        }
      }
    }
  }
}
// srv/orders-service.js — remote service kullanımı
const s4 = await cds.connect.to('S4SalesOrder')

this.on('READ', 'RemoteSalesOrders', async (req) => {
  // CAP OData sorgusunu S/4HANA'ya iletir
  return s4.run(req.query)
})

// Hybrid: lokal ve remote verini birleştirme
this.on('READ', Orders, async (req) => {
  const [local, remote] = await Promise.all([
    SELECT.from(Orders).where(req.query.SELECT.where),
    s4.run(SELECT.from('A_SalesOrder'))
  ])
  // merge logic...
})

Yerel test için mocking: cds watch --profile hybrid ile uzak servisleri gerçek sistemden, yerel DB'yi SQLite'dan kullanabilirsiniz. cds mock S4SalesOrder ile de fixture verisiyle tam offline çalışabilirsiniz.

Authorization

CAP, role-based authorization'ı CDS annotation seviyesinde destekler. XSUAA ile entegre çalışır:

// srv/orders-service.cds
service OrdersService @(requires: 'authenticated-user') {

  @restrict: [
    { grant: 'READ',   to: 'OrderViewer'  },
    { grant: ['CREATE','UPDATE'], to: 'OrderEditor'  },
    { grant: '*',      to: 'OrderAdmin'   }
  ]
  entity Orders as projection on orders.Orders;

  @restrict: [{ grant: 'closeOrder', to: 'OrderAdmin' }]
  action closeOrder(orderID: UUID) returns Orders;
}

Multi-Tenancy

Birden fazla müşteriye sunan SaaS uygulamaları için @sap/cds-mtxs paketi ile multi-tenancy etkinleştirilir. Tenant onboarding, schema isolation ve metadata extensibility framework tarafından yönetilir:

// .cdsrc.json
{
  "requires": {
    "multitenancy": true,
    "extensibility": true,
    "cds.xt.SaasProvisioningService": { "kind": "mtx" }
  },
  "mtx": {
    "element-prefix": "Z_",
    "namespace-blocklist": []
  }
}

CAP'ı Olgunlaştıran Özellikler

2024–2025 itibarıyla CAP ekosistemi önemli eklentilerle güçlendi:

CAP, BTP üzerindeki uygulama geliştirmede artık bir tercih değil, defacto standart. Özellikle S/4HANA'yı dışarıdan genişletmek veya tamamen bağımsız BTP uygulamaları geliştirmek için olgunluğu ve ekosistemi her geçen çeyrekte güçleniyor.

Diğer Yazılar

BTP'de CAP projesi mi planlıyorsunuz?

Mimari tasarımdan HANA Cloud yapılandırmasına, S/4HANA entegrasyonundan BTP deployment'a eksiksiz teknik destek sunuyoruz.

İletişime Geçin