cds-view-entities について
ABAP CloudでABAP CDSビューエンティティを使い、意味のあるデータモデルを設計するためのガイドです。関連、アノテーション、アクセス制御、RAP向けパターンに加え、SAPデータアクセスのクリーンコア指針も扱います。
主な機能
- 01CDSビューエンティティの設計
- 02関連とコンポジション
- 03式と組み込み関数
- 04UI、セマンティクス、分析アノテーション
- 05CDSアクセス制御のパターン
- 06RAPデータモデルの構成
活用シーン
- 01単独利用またはRAP向けのCDSモデル作成
- 02親子エンティティ関係のモデリング
- 03UIメタデータやセマンティクスアノテーションの追加
- 04データアクセス制限の定義
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ likweitan/ abap-skills --skill cds-view-entitiesインストール後、Claude Code で/cds-view-entitiesを入力して読み込まれたか確認します。
同梱ファイルreferences/annotation-reference.md
詳細
CDS ビューエンティティ
ABAP Cloud で ABAP CDS(Core Data Services)ビューエンティティを使用して、セマンティックデータモデルを構築するためのガイドです。
クリーンコアのコンテキスト:リリース済みの SAP CDS ビュー(
I_*)から選択するのは レベル A です。SAP データベーステーブルへの直接の読み取りアクセスは レベル C(ATC 優先度 2)、直接の書き込みアクセスは レベル D(ATC 優先度 1)です。リリース済み CDS ビューが存在しない場合は、未リリースの SAP ビューまたはテーブルの上にラッパー CDS ビューを構築し、ABAP Cloud 向けにリリースします。ラップするオブジェクトがクラシック API の場合、ラッパーはレベル B、それ以外の場合はレベル C です。SAP CDS ビューを変更するのではなく、CDS 拡張とCDS メタデータ拡張を使用してください。レベルの詳細については、abap-cloudスキルを参照してください。
ワークフロー
-
ユーザーの目的を特定します:
- 新しいデータモデルを作成する(単独、または RAP 用)
- エンティティ間の関係を定義する(アソシエーション、コンポジション)
- UI、セマンティクス、または分析向けのアノテーションを追加する
- アクセス制御を実装する
- CDS で式、関数、またはパラメーターを使用する
-
コンテキストを特定します:
- 単独の CDS ビューか、RAP BO データモデルか
- ルートエンティティか、子エンティティか、プロジェクションビューか
- トランザクション処理(RAP)か、分析用か、読み取り専用の利用か
-
ベストプラクティスを適用します:
- CDS ビューエンティティ(v2 構文の
define view entity)を使用します。従来の CDS ビュー(define view)は使用しません。 - 命名規則に従います(例:インターフェース/BO ビューには
ZR_*、利用/プロジェクションビューにはZC_*、再利用ビューにはZI_*)。 - メタデータの利用者(UI、OData、分析)向けに適切なアノテーションを追加します。
- CDS ビューエンティティ(v2 構文の
CDS ビューエンティティの構文
基本ビューエンティティ
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order'
define view entity ZI_SalesOrder
as select from zsalesorder
{
key order_id as OrderId,
customer_id as CustomerId,
order_date as OrderDate,
net_amount as NetAmount,
currency_code as CurrencyCode,
status as Status,
created_by as CreatedBy,
created_at as CreatedAt,
last_changed_at as LastChangedAt
}
ルートビューエンティティ(RAP 用)
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order Root'
define root view entity ZR_SalesOrder
as select from zsalesorder
composition [0..*] of ZR_SalesOrderItem as _Item
{
key order_uuid as OrderUUID,
order_id as OrderId,
customer_id as CustomerId,
order_date as OrderDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
net_amount as NetAmount,
currency_code as CurrencyCode,
status as Status,
@Semantics.user.createdBy: true
created_by as CreatedBy,
@Semantics.systemDateTime.createdAt: true
created_at as CreatedAt,
@Semantics.user.localInstanceLastChangedBy: true
last_changed_by as LastChangedBy,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
last_changed_at as LastChangedAt,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at as LastChangedAt,
_Item
}
子ビューエンティティ
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order Item'
define view entity ZR_SalesOrderItem
as select from zsalesorder_item
association to parent ZR_SalesOrder as _Order
on $projection.OrderUUID = _Order.OrderUUID
{
key item_uuid as ItemUUID,
order_uuid as OrderUUID,
product_id as ProductId,
quantity as Quantity,
@Semantics.amount.currencyCode: 'CurrencyCode'
unit_price as UnitPrice,
currency_code as CurrencyCode,
@Semantics.user.createdBy: true
created_by as CreatedBy,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
last_changed_at as LastChangedAt,
_Order
}
プロジェクションビュー(利用レイヤー)
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order Projection'
@Metadata.allowExtensions: true
define view entity ZC_SalesOrder
as projection on ZR_SalesOrder
{
key OrderUUID,
OrderId,
CustomerId,
OrderDate,
NetAmount,
CurrencyCode,
Status,
CreatedBy,
CreatedAt,
LastChangedBy,
LastChangedAt,
_Item : redirected to composition child ZC_SalesOrderItem
}
アソシエーションとコンポジション
アソシエーションの種類
| 種類 | 構文 | 使用例 |
|---|---|---|
| 通常のアソシエーション | association [0..1] to ZI_Customer as _Customer on ... | 独立したエンティティ(例:マスターデータの検索) |
| コンポジション | composition [0..*] of ZR_Child as _Child | ライフサイクルに依存する親子関係(RAP BO ツリー) |
| 親へのアソシエーション | association to parent ZR_Parent as _Parent on ... | コンポジション内で子から親への参照 |
アソシエーションの構文
define view entity ZI_SalesOrder
as select from zsalesorder
association [0..1] to ZI_Customer as _Customer
on $projection.CustomerId = _Customer.CustomerId
association [0..*] to ZI_SalesOrderItem as _Item
on $projection.OrderId = _Item.OrderId
{
key order_id as OrderId,
customer_id as CustomerId,
// Expose associations — required for consumption via ABAP SQL or OData
_Customer,
_Item
}
ABAP SQL でのアソシエーションの使用
" Path expression — triggers LEFT OUTER JOIN by default
SELECT FROM zi_salesorder
FIELDS OrderId,
\_Customer-CustomerName,
\_Item-ProductId
WHERE OrderId = @lv_order_id
INTO TABLE @DATA(lt_result).
" Filtering associations
SELECT FROM zi_salesorder
FIELDS OrderId, \_Item[ ProductId = 'PROD01' ]-Quantity
WHERE OrderId = @lv_order_id
INTO TABLE @DATA(lt_filtered).
式と組み込み関数
キャスト式
cast( amount as abap.dec(15,2) ) as ConvertedAmount,
cast( status as abap.char(10) ) as StatusText,
CASE 式
// Simple CASE
case status
when 'N' then 'New'
when 'A' then 'Approved'
when 'R' then 'Rejected'
else 'Unknown'
end as StatusText,
// Searched CASE
case when net_amount > 10000 then 'High'
when net_amount > 1000 then 'Medium'
else 'Low'
end as PriorityCategory,
算術式
quantity * unit_price as TotalPrice,
net_amount + tax_amount as GrossAmount,
文字列関数
concat( first_name, concat( ' ', last_name ) ) as FullName,
substring( postal_code, 1, 2 ) as Region,
length( description ) as DescLength,
upper( country_code ) as CountryUpper,
日付と時刻の関数
dats_days_between( start_date, end_date ) as DurationDays,
dats_add_days( order_date, 30 ) as DueDate,
tstmp_current_utctimestamp() as CurrentTimestamp,
集約式
define view entity ZI_OrderSummary
as select from zsalesorder
{
key customer_id as CustomerId,
count(*) as OrderCount,
sum( net_amount ) as TotalAmount,
avg( net_amount as abap.dec(15,2) ) as AvgAmount,
min( order_date ) as FirstOrderDate,
max( order_date ) as LastOrderDate
}
group by customer_id
入力パラメーター
define view entity ZI_SalesOrderByDate
with parameters
p_date : abap.dats
as select from zsalesorder
{
key order_id as OrderId,
order_date as OrderDate,
net_amount as NetAmount
}
where order_date >= $parameters.p_date
ABAP SQL での使用:
SELECT FROM zi_salesorderbydate( p_date = @lv_date )
FIELDS OrderId, OrderDate, NetAmount
INTO TABLE @DATA(lt_orders).
セッション変数
$session.user as CurrentUser,
$session.client as CurrentClient,
$session.system_date as SystemDate,
$session.system_language as SystemLanguage,
結合
define view entity ZI_OrderWithCustomer
as select from zsalesorder as so
inner join zcustomer as cust
on so.customer_id = cust.customer_id
{
key so.order_id as OrderId,
cust.customer_name as CustomerName,
so.net_amount as NetAmount
}
| 結合の種類 | キーワード | 動作 |
|---|---|---|
| 内部結合 | inner join | 一致する行のみ |
| 左外部結合 | left outer join | 左側のすべての行と、一致する右側の行 |
| 右外部結合 | right outer join | 右側のすべての行と、一致する左側の行 |
| クロス結合 | cross join | デカルト積 |
注:可能な場合は、結合よりもアソシエーションを優先してください。アソシエーションは遅延解決され、パス式をサポートします。
主なアノテーション
セマンティクスアノテーション(RAP の管理対象フィールド用)
@Semantics.user.createdBy: true
created_by as CreatedBy,
@Semantics.systemDateTime.createdAt: true
created_at as CreatedAt,
@Semantics.user.localInstanceLastChangedBy: true
last_changed_by as LastChangedBy,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
local_last_changed_at as LocalLastChangedAt,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at as LastChangedAt,
金額と通貨/数量と単位
@Semantics.amount.currencyCode: 'CurrencyCode'
net_amount as NetAmount,
currency_code as CurrencyCode,
@Semantics.quantity.unitOfMeasure: 'QuantityUnit'
quantity as Quantity,
quantity_unit as QuantityUnit,
UI アノテーション(CDS またはメタデータ拡張内)
@UI.headerInfo: {
typeName: 'Sales Order',
typeNamePlural: 'Sales Orders',
title: { type: #STANDARD, value: 'OrderId' }
}
@UI.lineItem: [{ position: 10 }]
@UI.identification: [{ position: 10 }]
@UI.selectionField: [{ position: 10 }]
order_id as OrderId,
メタデータ拡張(UI アノテーションに推奨)
@Metadata.layer: #CUSTOMER
annotate view ZC_SalesOrder with
{
@UI.facet: [{
id: 'GeneralInfo',
type: #IDENTIFICATION_REFERENCE,
label: 'General Information',
position: 10
}]
@UI.lineItem: [{ position: 10, importance: #HIGH }]
@UI.identification: [{ position: 10 }]
OrderId;
@UI.lineItem: [{ position: 20 }]
@UI.identification: [{ position: 20 }]
@UI.selectionField: [{ position: 10 }]
CustomerId;
@UI.lineItem: [{ position: 30 }]
@UI.identification: [{ position: 30 }]
NetAmount;
}
CDS アクセス制御
@EndUserText.label: 'Access Control for Sales Order'
@MappingRole: true
define role ZR_SalesOrder
{
grant select on ZR_SalesOrder
where ( CustomerId ) = aspect pfcg_auth ( Z_SO_AUTH, CUSTOMER_ID, ACTVT = '03' );
}
アクセス制御パターン
| パターン | 説明 |
|---|---|
pfcg_auth | 標準の権限オブジェクトチェック |
inherit | 関連エンティティの制限を継承する |
aspect user | 現在のユーザーで制限する |
true / false | 制限なし/アクセス不可 |
// Inherit from parent entity
define role ZR_SalesOrderItem {
grant select on ZR_SalesOrderItem
where inheriting conditions from entity ZR_SalesOrder
association _Order;
}
// User-based restriction
define role ZR_MyOrders {
grant select on ZR_SalesOrder
where CreatedBy = aspect user;
}
CDS テーブルエンティティ
define table entity ztab_salesorder {
key client : abap.clnt;
key order_uuid: sysuuid_x16;
order_id : abap.numc(10);
customer : abap.char(10);
amount : abap.dec(15,2);
currency : abap.cuky(5);
}
CDS テーブルエンティティは、従来の DDIC データベーステーブルの代替として利用でき、RAP BDEF では
persistent tableとして使用できます。
RAP のデータモデルパターン
典型的なコンポジションツリー
ZR_Root (define root view entity)
├── composition [0..*] of ZR_Child1 as _Child1
└── composition [0..*] of ZR_Child2 as _Child2
└── composition [0..*] of ZR_GrandChild as _GrandChild
ZR_Child1 (association to parent ZR_Root as _Root)
ZR_Child2 (association to parent ZR_Root as _Root)
└── composition [0..*] of ZR_GrandChild as _GrandChild
ZR_GrandChild (association to parent ZR_Child2 as _Parent)
管理フィールドパターン(マネージド RAP BO)
マネージド RAP BO の各エンティティには、管理フィールドを含める必要があります。
| フィールド | 型 | アノテーション | 用途 |
|---|---|---|---|
CreatedBy | syuname | @Semantics.user.createdBy: true | 作成者 |
CreatedAt | utclong | @Semantics.systemDateTime.createdAt: true | 作成日時 |
LastChangedBy | syuname | @Semantics.user.localInstanceLastChangedBy: true | 最終変更者 |
LocalLastChangedAt | utclong | @Semantics.systemDateTime.localInstanceLastChangedAt: true | 楽観的同時実行制御用 ETag |
LastChangedAt | utclong | @Semantics.systemDateTime.lastChangedAt: true | ドラフト用の合計 ETag |
出力形式
- 新しいビューを作成する場合は、CDS ソースコード全体を提示してください
- 関連するアノテーションをすべて含めてください
- 命名規則に従ってください(インターフェースは
ZR_*、コンシュームはZC_*、再利用はZI_*) - コンシュームで使用するアソシエーションは常に公開してください
- セキュリティに関係する場合は、アクセス制御定義を含めてください
参考資料
- SAP ABAP チートシート — CDS ビューエンティティ
- SAP ヘルプ — ABAP データモデルガイド
- SAP ヘルプ — ABAP CDS リファレンス
- SAP ヘルプ — CDS アノテーション
- CDS 機能マトリックスのブログ
CDS の Clean Core レベルに関する注意事項
| シナリオ | レベル | 注意事項 |
|---|---|---|
リリース済みの SAP CDS ビュー(I_*)に対する SELECT | A | 目標状態 |
| 独自のテーブルを基にしたカスタム CDS ビュー | A | 同じソフトウェアコンポーネント内で許可されます |
クラシック(指定済み)の SAP CDS ビューに対する SELECT | B | クラシック ABAP 開発では許容されます |
| リリースされていない SAP CDS ビューを基にしたラッパー CDS ビュー | B/C | ラップ対象のビューがクラシック API の場合は B、それ以外は C |
| SAP データベーステーブルへの直接的な読み取りアクセス | C | ATC 優先度 2 — リリース済みの CDS ビューまたはクラシック API を使用します |
| SAP データベーステーブルへの直接的な書き込みアクセス | D | ATC 優先度 1 — リリース済みまたはクラシック API を使用します |
SAP CDS ビューに対する CDS extend/メタデータ拡張 | A/B | 変更よりも推奨されます |
| リリース済み拡張インクルードを使用したカスタムフィールド | A | 推奨される手法 |
| リリースされていない拡張インクルードを使用したカスタムフィールド | B | リリース済みのインクルードを監視します |
| クラシックな APPEND を使用したカスタムフィールド | C | 拡張インクルードが利用可能になったら移行を検討します |
ラッパー CDS ビューに関するガイダンス
- 適切な SAP CDS ビューが存在するもののリリースされていない場合は、その上にカスタム CDS ビューを作成し、ABAP Cloud 開発向けにリリースします
- SAP ビューが参照する CDS ビュー(関連付けのターゲット、値ヘルプビュー)にも、それぞれラッパーが必要になる場合があります
- 適切な SAP CDS ビューがまったく存在しない場合は、データベーステーブル上にカスタム CDS ビューを直接作成します
- SAP が同等の CDS ビューをリリースしたら、ラッパーを廃止します
CDS ビューは、Cloudification Repository でオブジェクトタイプ STOB として分類されます。
関連スキル
- abap-sql-amdp:高度な ABAP SQL クエリおよび AMDP プロシージャに使用します
- rap:CDS データモデルに基づく RAP ビジネスオブジェクトの構築に使用します
- authorization-iam:CDS アクセス制御(DCL)の実装に使用します
- odata:CDS ビューを OData サービスとして公開するために使用します
- abap-cloud:Clean Core レベルの分類およびラッパー CDS ビューのパターンに使用します
- abap-cloud-migration:テーブルへの直接アクセスをリリース済み CDS ビューに置き換えるために使用します