abap-rap について
abap-rapは、SAP ABAP RESTful Application Programming Model(RAP)アプリケーションを開発する人向けのベストプラクティス資料です。階層構造、実装方式の選択、主要な成果物、よくある誤りを扱います。
主な機能
- 01RAPの3層アーキテクチャ
- 02マネージドとアンマネージドの実装指針
- 03CDSビューエンティティのパターン
- 04ビヘイビア定義と実装例
- 05プロジェクションとサービスの設定指針
- 06よくある問題と回避策
活用シーン
- 01マネージドまたはアンマネージド方式の選択
- 02RAPビジネスオブジェクトの構成
- 03ビヘイビア定義とハンドラーの確認
- 04RAP実装でよくある誤りの回避
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dankromp/ skilloverflow --skill abap-rapインストール後、Claude Code で/abap-rapを入力して読み込まれたか確認します。
同梱ファイルreferences/quick-reference.mdtemplates/managed-rap-bo.abap
詳細
ABAP RAP — ベストプラクティス
主な参考資料: https://help.sap.com/docs/abap-cloud/abap-rap BDEF 構文: https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/abenbdl_define_beh.htm チートシート: https://github.com/SAP-samples/abap-cheat-sheets
RAP アーキテクチャ — 3 層
RAP ビジネスオブジェクトは、CDS データモデリングビュー、CDS 振る舞い定義、および ABAP クラスによる振る舞い実装を通じて定義します。
Database table (DDIC table — stores active data)
↓
CDS view entity (data model — root + child entities)
↓
Behavior Definition (BDEF — what operations are allowed)
↓
Behavior Implementation (ABAP class — business logic)
↓
Projection view (UI-specific subset + aliases)
↓
Service Definition (what to expose)
↓
Service Binding (OData V2 or V4 protocol)
Managed と Unmanaged — 慎重に選択する
Managed RAP では、フレームワークがすべての CRUD 永続化を自動的に処理するため、開発者はビジネスロジックのみに集中できます。Unmanaged RAP では、開発者が各保存操作を手動で実装する必要があります。Managed RAP は新規開発に最適です。複雑な既存ロジックやレガシーシステムと統合する場合は Unmanaged を選択します。
Managed を使用する場合: グリーンフィールド開発、テーブルへの直接アクセス、新しい S/4HANA または BTP アプリ Unmanaged を使用する場合: 既存の BAPI/関数モジュールとの統合、複雑なレガシーロジック、外部永続化
CDS ビューエンティティ — ルートと子
" Root CDS view entity
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Travel'
define root view entity ZI_Travel
as select from ztravel
composition [0..*] of ZI_TravelItem as _Items
{
key travel_uuid as TravelUUID,
travel_id as TravelID,
agency_id as AgencyID,
customer_id as CustomerID,
begin_date as BeginDate,
end_date as EndDate,
booking_fee as BookingFee,
total_price as TotalPrice,
currency_code as CurrencyCode,
overall_status as OverallStatus,
@Semantics.systemDateTime.createdAt: true
created_at as CreatedAt,
@Semantics.user.createdBy: true
created_by as CreatedBy,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at as LastChangedAt,
@Semantics.user.lastChangedBy: true
last_changed_by as LastChangedBy,
@Semantics.systemDateTime.localInstanceLastChangedAt: true
local_last_changed_at as LocalLastChangedAt,
_Items
}
振る舞い定義 — Draft を使用した Managed
managed implementation in class ZBP_Travel unique;
strict ( 2 );
with draft;
define behavior for ZI_Travel alias Travel
persistent table ztravel
draft table ztravel_d
etag master LocalLastChangedAt
lock master total etag LastChangedAt
authorization master ( global )
{
field ( readonly ) TravelUUID, TravelID, CreatedAt, CreatedBy, LastChangedAt, LastChangedBy;
field ( numbering : managed, readonly ) TravelUUID;
create;
update;
delete;
action ( features : instance ) acceptTravel result [1] $self;
action ( features : instance ) rejectTravel result [1] $self;
validation validateDates on save { field BeginDate, EndDate; }
validation validateStatus on save { field OverallStatus; }
determination calculateTotalPrice on modify { field BookingFee; }
draft action Edit;
draft action Activate optimized;
draft action Discard;
draft action Resume;
association _Items { create; with draft; }
mapping for ztravel {
TravelUUID = travel_uuid;
TravelID = travel_id;
AgencyID = agency_id;
}
}
振る舞い実装 — 主なメソッド
CLASS ZBP_Travel DEFINITION PUBLIC ABSTRACT FINAL FOR BEHAVIOR OF ZI_Travel.
PUBLIC SECTION.
" Validation
METHODS validateDates FOR VALIDATE ON SAVE
IMPORTING keys FOR Travel~validateDates.
" Determination
METHODS calculateTotalPrice FOR DETERMINE ON MODIFY
IMPORTING keys FOR Travel~calculateTotalPrice.
" Action
METHODS acceptTravel FOR MODIFY
IMPORTING keys FOR ACTION Travel~acceptTravel RESULT result.
ENDCLASS.
CLASS ZBP_Travel IMPLEMENTATION.
METHOD validateDates.
READ ENTITIES OF ZI_Travel IN LOCAL MODE
ENTITY Travel
FIELDS ( BeginDate EndDate )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_travel)
FAILED failed.
LOOP AT lt_travel INTO DATA(ls_travel).
IF ls_travel-EndDate < ls_travel-BeginDate.
APPEND VALUE #(
%tky = ls_travel-%tky )
TO failed-travel.
APPEND VALUE #(
%tky = ls_travel-%tky
%state_area = 'VALIDATE_DATES'
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = 'End date must be after begin date' )
%element-EndDate = if_abap_behv=>mk-on )
TO reported-travel.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD calculateTotalPrice.
" Always use IN LOCAL MODE inside behavior handlers
MODIFY ENTITIES OF ZI_Travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( TotalPrice )
WITH VALUE #( FOR key IN keys (
%tky = key-%tky
TotalPrice = '0.00' ) ).
ENDMETHOD.
METHOD acceptTravel.
MODIFY ENTITIES OF ZI_Travel IN LOCAL MODE
ENTITY Travel
UPDATE FIELDS ( OverallStatus )
WITH VALUE #( FOR key IN keys (
%tky = key-%tky
OverallStatus = 'A' ) )
FAILED failed
REPORTED reported.
READ ENTITIES OF ZI_Travel IN LOCAL MODE
ENTITY Travel ALL FIELDS WITH CORRESPONDING #( keys )
RESULT result
FAILED failed.
ENDMETHOD.
ENDCLASS.
Projection ビュー + BDEF
" Projection CDS view (UI-facing)
@EndUserText.label: 'Travel - Projection'
@AccessControl.authorizationCheck: #NOT_REQUIRED
define root view entity ZC_Travel
provider contract transactional_ui
as projection on ZI_Travel
{
key TravelUUID,
TravelID,
@UI.lineItem: [{ position: 10 }]
@UI.selectionField: [{ position: 10 }]
AgencyID,
BeginDate,
EndDate,
@UI.lineItem: [{ position: 50, criticality: 'OverallStatusCriticality' }]
OverallStatus,
_Items : redirected to composition child ZC_TravelItem
}
" Projection BDEF — thin layer, no business logic here
projection;
strict ( 2 );
use draft;
define behavior for ZC_Travel alias Travel
{
use create;
use update;
use delete;
use action acceptTravel;
use action rejectTravel;
use draft action Edit;
use draft action Activate;
use draft action Discard;
use draft action Resume;
use association _Items { create; with draft; }
}
Service Definition + Binding
" Service Definition
@EndUserText.label: 'Travel Service'
define service ZUI_Travel_O4 {
expose ZC_Travel as Travel;
expose ZC_TravelItem as TravelItem;
expose /DMO/I_Agency as Agency;
}
ADT で Service Binding ZUI_Travel_O4_VR を OData V4 UI バインディングとして有効化します。
よくある間違いを避ける
-
❌ Projection BDEF または Projection ビューにビジネスロジックを追加する
-
✅ Projection は薄く保ちます — UI ラベル、フィールドエイリアス、
useステートメントのみとします。インターフェースの振る舞いはビジネスロジック層です。 -
❌ ハンドラー内で
IN LOCAL MODEを指定せずにMODIFY ENTITIESを呼び出す -
✅ 権限チェックやロックの問題を避けるため、内部変更には必ず
IN LOCAL MODEを使用します -
❌ Draft の処理を後回しにする
-
✅ 最初から Draft を有効にします。既存の RAP BO に後から Draft を導入するのは複雑です
-
❌ 新規開発で
strict ( 1 )を使用する -
✅ 最も包括的な BDEF 構文チェックを有効にするため、必ず
strict ( 2 )を使用します -
❌ テーブルに直接アクセスできるのに Unmanaged で CRUD を実装する
-
✅ テーブルに直接アクセスするグリーンフィールド開発では Managed 実装を使用します
-
❌ ルート BDEF で
etag masterとlock masterを指定し忘れる -
✅ 楽観的ロック用の ETag と、Draft 用の lock master を必ず定義します
-
❌ 管理用フィールドに
@Semantics.systemDateTimeと@Semantics.userのアノテーションを指定し忘れる -
✅ Managed RAP でタイムスタンプとユーザーを自動入力するには、これらのアノテーションが必要です