SAP ABAP’ta Exception Handling: Temiz, Güvenilir ve Sürdürülebilir Hata Yönetimi
Yıllar içinde onlarca SAP projesinde çalışırken fark ettiğim en kritik sorunlardan biri şu: Geliştiricilerin büyük çoğunluğu hata yönetimine sonradan düşünüyor. Önce kodu yazıyor, işler tıklandığında bir CATCH bloğu ekliyor ve oradan devam ediyor. Bu yaklaşım, kısa vadede işe yarasa da uzun vadede bakımı imkânsız, hatayı gizleyen ve production ortamında sessizce çöken sistemlere yol açıyor.
Bu makalede ABAP’ta exception handling’e mimari bir perspektiften bakacağız. Klasik SY-SUBRC kontrolünden sınıf tabanlı exception’lara, özel exception sınıfı tasarımından hata zincirlerine (exception chaining) kadar gerçek dünyada uygulanabilir, kopya-yapıştır hazır örneklerle ilerleyeceğiz.
💡 Bu makaleyi okumalısınız çünkü: ABAP’ta hata yönetimini doğru tasarlamak, hem son kullanıcı deneyimini hem de geliştirici verimliliğini doğrudan etkiler. Bir sonraki kod incelemenizde bu konuyu farklı gözlerle değerlendireceksiniz.
ABAP’ta Hata Yönetiminin Evrimi: SY-SUBRC’den Class-Based Exceptions’a
ABAP’ın tarihi incelendiğinde iki farklı hata yönetimi paradigması görülür:
1. Klasik Yöntem: SY-SUBRC ve EXCEPTIONS
Eski ABAP kodlarında —özellikle 2000’ler öncesi yazılmış Function Module’lerde— hata yönetimi şu şekilde yapılırdı:
" Klasik yaklaşım — KAÇININ!
CALL FUNCTION 'BAPI_SALESORDER_GETLIST'
EXPORTING
customer_number = lv_kunnr
TABLES
sales_orders = lt_orders
EXCEPTIONS
not_found = 1
OTHERS = 2.
IF sy-subrc = 1.
" Müşteri bulunamadı
WRITE 'Kayıt yok'.
ELSEIF sy-subrc = 2.
" Bilinmeyen hata
WRITE 'Bir hata oluştu'.
ENDIF.
Bu yaklaşımın temel sorunları:
Hata bilgisi sayısal bir kode indirgeniyor, bağlam kayboluyor
Kontrolü atlamak çok kolay — sy-subrc‘yi hiç kontrol etmeyebilirsiniz
Hata zinciri kurmak imkânsız (bir exception başka bir exception’ı neden olduğunda bunu ifade edemezsiniz)
Stack trace yok, debug etmek kâbus
2. Modern Yöntem: Class-Based Exceptions (TRY-CATCH-CLEANUP)
ABAP OOP’un getirdiği en büyük avantajlardan biri sınıf tabanlı exception mekanizmasıdır. Temel yapı:
TRY.
" İş mantığı buraya
DATA(lo_order) = NEW zcl_sales_order( iv_order_id = lv_order_id ).
lo_order->process( ).
CATCH zcx_order_not_found INTO DATA(lx_not_found).
" Spesifik hata — müşteriye anlamlı mesaj ver
MESSAGE lx_not_found->get_text( ) TYPE 'E'.
CATCH zcx_business_error INTO DATA(lx_business).
" İş kuralı ihlali
cl_demo_output=>display( lx_business->get_longtext( ) ).
CATCH cx_root INTO DATA(lx_unexpected).
" Beklenmedik sistem hatası — log'la ve yöneticiye bildir
zcl_error_logger=>log_exception( lx_unexpected ).
RAISE EXCEPTION TYPE zcx_system_error
EXPORTING
previous = lx_unexpected
textid = zcx_system_error=>unexpected_error.
CLEANUP.
" Kaynak temizliği — her durumda çalışır
IF lo_order IS BOUND.
lo_order->rollback( ).
ENDIF.
ENDTRY.
Burada dikkat edin: CLEANUP bloğu exception yakalanıp yakalanmadığından bağımsız olarak çalışır. Bu, finally bloğunun ABAP karşılığıdır ve kaynak temizliği için kritiktir.
Kendi Exception Sınıflarınızı Tasarlamak: Hiyerarşi ve Semantik
En sık yapılan hatalardan biri, tüm hatalar için cx_sy_arithmetic_error gibi genel sistem exception’larını kullanmak ya da tek bir custom exception sınıfı yaratıp her şeyi oraya tıkmak. Doğru yaklaşım, iş alanınızı yansıtan anlamlı bir hiyerarşi kurmaktır.
Exception Sınıfı Hiyerarşisi Tasarımı
ZCX_BASE_ERROR (Tüm custom exception'ların base class'ı)
├── ZCX_BUSINESS_ERROR (İş kuralı ihlalleri — kullanıcıya gösterilebilir)
│ ├── ZCX_ORDER_NOT_FOUND
│ ├── ZCX_INVALID_MATERIAL
│ └── ZCX_STOCK_INSUFFICIENT
└── ZCX_TECHNICAL_ERROR (Sistem/teknik hatalar — yöneticiye raporlanır)
├── ZCX_DB_CONNECTION_FAILED
└── ZCX_RFC_CALL_FAILED
Bu hiyerarşinin avantajı: Hem spesifik hem de genel yakalama yapabilirsiniz.
" Spesifik yakalama:
CATCH zcx_order_not_found INTO DATA(lx).
" Tüm iş hatalarını yakalama:
CATCH zcx_business_error INTO DATA(lx).
" Tüm custom hatalarınızı yakalama:
CATCH zcx_base_error INTO DATA(lx).
SE24’te Exception Sınıfı Oluşturma
Transaction SE24‘te yeni bir class oluşturun:
Class Name: ZCX_ORDER_NOT_FOUND
Superclass: ZCX_BUSINESS_ERROR (ya da doğrudan CX_STATIC_CHECK)
Instantiation: Public
Exception sınıfınıza özel mesaj metinleri eklemek için Texts sekmesinde Text ID’ler tanımlayın:
" ZCX_ORDER_NOT_FOUND sınıfı — Constants sekmesinde:
CONSTANTS:
order_not_found TYPE sotr_conc VALUE 'ZCX_ORDER_NOT_FOUND ORDER_NOT_FOUND',
order_locked TYPE sotr_conc VALUE 'ZCX_ORDER_NOT_FOUND ORDER_LOCKED'.
Kullanım:
" Exception fırlatma:
RAISE EXCEPTION TYPE zcx_order_not_found
EXPORTING
textid = zcx_order_not_found=>order_not_found
order_id = lv_order_id. " Attribute olarak tanımlanmış
" Yakalama ve mesaj alma:
CATCH zcx_order_not_found INTO DATA(lx).
lv_message = lx->get_text( ). " OTR'dan mesajı otomatik alır
Exception Chaining: Hata Zincirini Koruyun
Büyük sistemlerde bir hata genellikle başka bir hatanın sonucudur. Örneğin: RFC çağrısı başarısız oldu → Sipariş oluşturulamadı → İş süreci durdu. Bu zinciri korumak, debug sırasında hayat kurtarır.
METHOD create_order.
TRY.
" RFC çağrısı
me->call_remote_system( ).
CATCH zcx_rfc_call_failed INTO DATA(lx_rfc).
" Teknik hatayı iş hatasına dönüştür, ama ZINCIRI KORU
RAISE EXCEPTION TYPE zcx_order_creation_failed
EXPORTING
textid = zcx_order_creation_failed=>rfc_error
previous = lx_rfc. " <-- Bu kritik! Orijinal hatayı saklıyoruz
ENDTRY.
ENDMETHOD.
Daha sonra zinciri okumak için:
CATCH zcx_order_creation_failed INTO DATA(lx_order).
" Tam hata zincirini log'la
DATA(lx_current) = CAST cx_root( lx_order ).
WHILE lx_current IS BOUND.
cl_demo_output=>display( lx_current->get_text( ) ).
lx_current = lx_current->previous. " Bir önceki hataya git
ENDWHILE.
Checked vs Unchecked Exceptions: Ne Zaman Hangisini Seçmeli?
ABAP’ta exception sınıflarının üç süperclass seçeneği var ve bu seçim önemli:
Superclass
Tür
Ne Zaman Kullanılır?
CX_STATIC_CHECK
Checked
Çağıran kodun mutlaka handle etmesi gereken, öngörülebilir hatalar. Dosya bulunamadı, kayıt yok vb.
CX_DYNAMIC_CHECK
Yarı-checked
Runtime’da oluşabilecek ama her zaman handle edilmesi gerekmeyen hatalar. Parametre doğrulama hataları.
CX_NO_CHECK
Unchecked
Sistem kaynaklı, programatik olarak handle edilemeyen hatalar. Bellek hatası, sistem çöküşü vb.
Pratik kural: Kendi iş exception’larınız için genellikle CX_STATIC_CHECK‘i tercih edin. Bu, method imzasında exception’ın görünmesini zorlar ve çağıran kodu exception’ı handle etmeye ya da yukarı iletmeye mecbur bırakır. Bu sayede exception’ların görmezden gelinmesini derleme zamanında engellersiniz.
Gerçek Dünya Senaryosu: Katmanlı Mimaride Hata Yönetimi
Bir SAP uygulamasında tipik katmanlar şunlardır: Presentation → Application → Business Logic → Data Access. Her katmanın hata yönetiminden farklı sorumluluğu vardır.
""" DATA ACCESS LAYER — Teknik hataları iş hatalarına dönüştür """
CLASS zcl_order_repository IMPLEMENTATION.
METHOD get_by_id.
SELECT SINGLE * FROM vbak
WHERE vbeln = @iv_order_id
INTO @DATA(ls_order).
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE zcx_order_not_found
EXPORTING
order_id = iv_order_id
textid = zcx_order_not_found=>order_not_found.
ENDIF.
rv_order = ls_order.
ENDMETHOD.
ENDCLASS.
""" BUSINESS LOGIC LAYER — İş kurallarını uygula """
CLASS zcl_order_service IMPLEMENTATION.
METHOD process_order.
TRY.
DATA(ls_order) = mo_repository->get_by_id( iv_order_id ).
" İş kuralı kontrolü
IF ls_order-netwr <= 0.
RAISE EXCEPTION TYPE zcx_invalid_order
EXPORTING
textid = zcx_invalid_order=>zero_value_order
order_id = iv_order_id.
ENDIF.
me->execute_workflow( ls_order ).
CATCH zcx_order_not_found INTO DATA(lx_not_found).
" Bu hata zaten anlamlı — yukarı ilet
RAISE EXCEPTION lx_not_found. " Re-raise: referansı koru
ENDTRY.
ENDMETHOD.
ENDCLASS.
""" PRESENTATION LAYER — Kullanıcıya uygun mesaj göster """
CLASS zcl_order_controller IMPLEMENTATION.
METHOD handle_process_request.
TRY.
mo_service->process_order( iv_order_id = p_order ).
MESSAGE 'Sipariş başarıyla işlendi.' TYPE 'S'.
CATCH zcx_business_error INTO DATA(lx_biz).
" Kullanıcıya anlamlı, teknik detay içermeyen mesaj
MESSAGE lx_biz->get_text( ) TYPE 'E'.
CATCH zcx_technical_error INTO DATA(lx_tech).
" Teknik hatayı log'la, kullanıcıya genel mesaj ver
zcl_error_logger=>log( lx_tech ).
MESSAGE 'Sistem hatası oluştu. Lütfen yöneticinize başvurun.' TYPE 'E'.
ENDTRY.
ENDMETHOD.
ENDCLASS.
Bu yapının güzelliğine dikkat edin: Her katman sadece kendi sorumluluğundaki hatayı ele alıyor. Data access layer teknik hataları, business layer iş kuralı hatalarını, presentation layer ise kullanıcıya ne gösterileceğini biliyor.
Sık Yapılan Hatalar ve Nasıl Kaçınılır?
❌ Hata 1: Boş CATCH Bloğu — Exception’ı Yutmak
" YANLIŞ — Hata sessizce kayboluyor!
TRY.
mo_service->risky_operation( ).
CATCH cx_root. " Hiçbir şey yapılmıyor
ENDTRY.
" DOĞRU — En azından log'la
TRY.
mo_service->risky_operation( ).
CATCH cx_root INTO DATA(lx).
zcl_error_logger=>log( lx ).
" Veya re-raise et:
RAISE EXCEPTION lx.
ENDTRY.
❌ Hata 2: Her Yerde CX_ROOT Yakalamak
Yalnızca en üst düzeyde (entry point’te) cx_root yakalayın. Alt katmanlarda spesifik exception sınıflarını hedefleyin. Geniş yakalama, yanlış hataları gizler.
❌ Hata 3: Exception’da Iş Mantığı Yürütmek
" YANLIŞ — CATCH bloğu iş mantığı için değil, hata yönetimi için
CATCH zcx_order_not_found.
" Yeni sipariş oluşturmaya çalışmak YANLIŞ
mo_service->create_default_order( ).
" DOĞRU — Hata durumunu ele al, iş akışını başka yerde yönet
CATCH zcx_order_not_found INTO DATA(lx).
MESSAGE lx->get_text( ) TYPE 'W'.
RETURN. " Ya da uygun bir işlem yap
Exception Logging: Merkezi Hata Kayıt Altyapısı
Production sistemlerde exception’ların merkezi olarak loglanması kritiktir. SAP’ın standard SLG1 (Application Log) mekanizmasını kullanabilirsiniz:
CLASS zcl_error_logger IMPLEMENTATION.
METHOD log_exception.
DATA: lo_log TYPE REF TO if_bali_log,
lo_item TYPE REF TO if_bali_item_base.
" Application Log oluştur
lo_log = cl_bali_log=>create_with_header(
iv_object = 'ZORDER_PROC'
iv_subobject = 'ERRORS' ).
" Exception'ı log item olarak ekle
lo_item = cl_bali_exception_item=>create( ix_exception ).
lo_log->add_item( lo_item ).
" Log'u kaydet
cl_bali_log_db=>save( io_log = lo_log ).
ENDMETHOD.
ENDCLASS.
Bu şekilde tüm hatalar SLG1 transaction‘ında izlenebilir, filtrelenebilir ve raporlanabilir hale gelir.
Clean Code Perspektifinden Exception Tasarımı
Bu konuda daha kapsamlı best practice’leri ele aldığımız ABAP Clean Code: Okunabilir ve Sürdürülebilir SAP Kodu Yazmanın 10 Altın Kuralı makalemizde de vurgulandığı gibi, iyi bir exception tasarımı kodun okunabilirliğini doğrudan etkiler. İşte exception’lara özgü clean code prensipleri:
Exception sınıfı adları konuşkan olsun: ZCX_ORDER_NOT_FOUND, ZCX_DB_ERROR gibi değil; ZCX_SALES_ORDER_NOT_FOUND_FOR_CUSTOMER gibi niyeti net ifade etsin
Exception mesajları kullanıcı dostu olsun: Stack trace değil, ne olduğunu ve ne yapılması gerektiğini açıklayan mesajlar
Bir exception, bir sorumluluk: Çok fazla farklı durumu kapsayan genel exception’lar yaratmaktan kaçının
Erken fırlat, geç yakala: Hatayı tespit ettiğiniz yerde hemen fırlatın, yakalamayı ise handle edebileceğiniz yere bırakın
Sonuç: Hata Yönetimi Bir Birinci Sınıf Vatandaştır
Exception handling, sonradan eklenen bir detay değil; yazılım mimarisinin ayrılmaz bir parçasıdır. Bunu yıllarca hata bedeliyle öğrendik. Bugün öğrendiklerimizi özetleyelim:
✅ Klasik SY-SUBRC’den class-based exception’lara geçin — bunu ertelemeyin
✅ Anlamlı bir hiyerarşi kurun: iş hataları ve teknik hatalar ayrı dallar
✅ Exception chaining ile hata zincirini koruyun — debug süreleriniz yarı yarıya azalacak
✅ Her katmanın kendi sorumluluğundaki hataları handle etmesini sağlayın
✅ Boş CATCH bloğu asla — en azından logla
✅ Merkezi logging altyapısı kur — production sorunlarını hızlıca tespit et
Bir sonraki adım olarak mevcut kodlarınızdaki SY-SUBRC kontrollerini gözden geçirmenizi ve en kritik iş akışları için özel exception hiyerarşinizi tasarlamaya başlamanızı öneririm. Küçük bir yatırım, büyük bir bakım kolaylığı getirecektir.
Ayrıca OOP tasarım desenlerini ABAP’ta nasıl uygulayabileceğinizi merak ediyorsanız, ABAP OOP ile Tasarım Desenleri: Strategy Pattern Gerçek Dünya Uygulamaları makalemize göz atın — exception sınıfı tasarımınızda da benzer prensipleri kullanacaksınız.
Siz ABAP projelerinizde exception yönetimini nasıl organize ediyorsunuz? Hangi yaklaşım sizin için en iyi sonucu verdi? Aşağıya yorum bırakın, deneyimlerinizi paylaşın — bu konular hakkında daha detaylı bir yazı dizisi hazırlamamı istiyorsanız da belirtin.
Top comments (0)