Yalın ve esnek bir mimari ile ODATA hizmetinizi basitleştirin

Herkese merhaba,
OData hizmetini uygulamanın basitleştirilmiş bir yolu ve son 5 yıldır tüm UI5 projelerim (yaklaşık 8+ büyük proje) için aynı OData hizmetini yeniden kullanma deneyimim hakkında yazacağım.

Öncelikle, standart FIORI uygulamaları, akıllı kontroller veya standart bir SAPGUI kullanıcısını hedefleyen çok basit bir honkie-ponkie uygulaması uyguluyorsanız, bu makale size göre olmayabilir.

2015 yılında, nodeJS ve React’i öğrenirken, arka uçtan veri göndermenin ve ön uç katmanında tüketmenin çok kolay olduğunu fark ettim. 2016 yılında nodeJS ve React ile bir HANA Prosedür yedekleme aracı yazdım (makalem burada) ve oradaki veri iletişim metodolojilerini gerçekten sevdim.

SAPUI5’i öğrenmeye başladığımda, tüm veri akışında bir şeylerin doğru olmadığını fark ettim. SEGW’de alanları seçmek, servisi tekrar güncellemek, yeni gereksinimler nedeniyle yapıyı güncellemek, fonksiyon içe aktarımları, ek açıklamalar, derin eklemeler, tüm SEGW proje için gerçekten yüktü. İyi bir ruh hali içinde bir şey uygulayabilmemin bir yolu yoktu. Basit bir REST API veri akışından sonra, OData yapılandırması, ayarlar, iç içe geçmiş verileri yakalamak gereksiz yere karmaşıktı. Bir içerik dinamik hale geldiğinde, işler daha da çirkinleşiyordu.

İşleri daha genel bir şekilde basitleştirmeye karar verdim, bu da kariyerimde ve şirkette birçok şeyi değiştirecekti.

Yalın OData derken, tüm projeleriniz için kullanabileceğiniz genel bir OData hizmetinden bahsediyorum. Aşağıda sadece GET ve POST metotlarının kullanıldığını görebilirsiniz. Sadece iki temel metoda ihtiyacımız var. Aşağıda uç noktalar için filtrelerin nasıl aktığını açıklayacağım.

ODATA’mız sabit sütunlar içermeli, ancak herhangi bir değişiklik yapmadan tüm gereksinimleri kolayca karşılamalıdır. Aşağıda OData servisini bir yapıya dayalı olarak sadece 4 alanla oluşturdum. İstek UI5’ten geldiğinde, parametrelere göre özel bir BADI katmanı tetiklenir.

TMPL_ID : (şablon kimliği) Bu sütun bize bu hizmetin ne hakkında olduğu bilgisini verir. Belirli bir rapor modülü, bir iş akışı, hesaplama, dosya oluşturma veya herhangi bir şeyle ilgili olabilir. Bu, bu modülle ilgili tüm yöntemleri de içeren BADI filtresine atıfta bulunur. Bir örnek FINANCIAL_REPORT olabilir

Rapor oluşturma, oluşturulan raporu indirme (XLSX, PDF veya canlı rapor), pivotu çalıştırmadan önce bağlam seçimleri, önceden oluşturulmuş raporların tümü aynı MODULE_ID’ye aittir. İlgili tüm yöntemler aynı BADI’de bulunmaktadır.

DETAY: Bu, bu hizmetin tam olarak ne yapacağını ifade eder. Bu, çalıştırılması gereken bir hesaplama, bir onay süreci, şablon oluşturma veya grafik oluşturma olabilir. BADI içindeki yöntem adıdır.GET_REPORT, RUN_CONVERSION, GET_SALES_CHART, GET_SELECTIONS vb. örnek olarak verilebilir.

CONTEXT: Bu, BODY’de POST isteği için parametreleri / filtreleri gönderdiğim anahtar sütundur. Ayrıca hem GET/POST istekleri için JSON formatında geri veri almak için kullanılır. JSON içeriği burada akar./

MSG: Get/Post işlemi sırasında herhangi bir hata oluşursa buraya gönderilir. Standart bir hata işleme yöntemine sahip olmak, ön uçta işi kolaylaştırır. Aynı hatayı CONTEXT içinde de gönderebilirim ancak MSG alanı işlenmesini kolaylaştırır.

*GET METHOD     

GET BADI LR_BADI
         FILTERS
        TMPL_ID = LV_TMPL_ID.
        
        CALL BADI LR_BADI->GET
          EXPORTING
            IT_FILTER = IT_FILTER  "Filter coming from URL
            IS_DATA   = IS_DATA    "contains info related to query.
          IMPORTING
            ET_MSG    = LT_MSG
            ER_DATA   = LR_DATA.
        
* RESPONSE

        ER_ENTITY-TMPL_ID = IS_DATA-TMPL_ID.
        ER_ENTITY-ACTION = IS_DATA-ACTION.
        ER_ENTITY-DETAIL = IS_DATA-DETAIL.
        ER_ENTITY-CONTEXT = /ui2/cl_json=>serialize( data = LR_DATA ).  "dynamic json content
        ER_ENTITY-MSG = /ui2/cl_json=>serialize( data = LT_MSG ).   "any message if raised
*POST METHOD

CALL BADI LR_BADI->SAVE
          EXPORTING
            IS_DATA = IS_DATA "contains all info related to query
          IMPORTING
            ET_MSG  = LT_MSG
            ER_DATA = LR_DATA.
* RESPONSE
        ER_ENTITY-CONTEXT = /ui2/cl_json=>serialize( data = LR_DATA ).
        ER_ENTITY-MSG = /ui2/cl_json=>serialize( data = LT_MSG ).

Tasarım Modeli:

Burada, SAP ürünlerinin çoğunda kolayca görebileceğiniz strateji tasarım modelini yoğun bir şekilde kullanıyoruz. Sadece bir örnek vermek gerekirse, tüm BPC mimarisi (standart mod) bu yaklaşımı yoğun bir şekilde kullanmaktadır.

SAP BADI katmanı sadece kalıbı sarmaktadır. Aşağıda bir strateji tasarım modelinin sınıf diyagramı yer almaktadır.

Arayüz bizim BADI arayüzümüzdür ve somut sınıflar BADI uygulamalarıdır.

Aşağıdaki ekran görüntüsü mevcut sistemimden. Biri GET ve diğeri SAVE için olmak üzere 2 ana yöntem var. Bir arayüz yönteminde birleştirebilirdim ama sadece HTTP yöntem türleri arasında bir ayrım istedim.

Her BADI, hangi sınıfın tetikleneceğini tanımlayan FILTER aracılığıyla uygulanır.

Şu anda aynı arayüzden uygulanan yaklaşık 114 BADI var. Yaklaşık 5 büyük projeyi kapsıyor ve 2016’dan beri SEGW t-code’u bir daha hiç ziyaret etmedik. Bu bize proje uygulaması için ihtiyaç duyduğumuz esnekliği ve hızı sağladı. SEGW modellemesine zaman harcamak yerine, bu zamanı daha iyi mimari, yenilikçi işlevler ve son kullanıcılarımız için daha iyi UI/UX için harcıyoruz. Standart FIORI ekranları dürüst olmak gerekirse çok sıkıcıydı.

Projenin herhangi bir aşamasında herhangi bir değişikliği karşılama:

Yukarıdaki mimarinin en büyük avantajı, mantığı kolayca seçebilecek ve birkaç gün içinde geliştirmeye devam edebilecek herhangi bir ABAP geliştiricisini işe alabilmemizdi. SEGW yapılandırmaları, derin eklemeler, ek açıklamalar ve diğer onay kutusu işlevleri hakkında bilgi sahibi olmaları gerekmez. Her şey HTTP yöntemleri, JSON verileri ve performans odaklı temiz ABAP ile ilgili.

Bu bize yıldırım hızı sağladı. İlk projeyi başarılı bir şekilde uyguladıktan sonra, kullanıcının SAP dışı uygulama fikirlerinin çoğu SAP’ye yöneldi. Bunu çok hızlı ve çok sağlam bir şekilde yapıyorduk. Yeni projeler gelmeye devam etti ve bunların çoğunu reddetmek zorunda kaldık.

SCRUM planlamamıza dayanarak projenin herhangi bir aşamasında herhangi bir geç talebi, yeni fikirleri kabul ediyorduk. Her şey sorunsuz ve başarılıydı. Her iki taraf için de kazan-kazan.

GET ve POST yöntemi arasında geçiş yapın:

FILTER’ın çok uzun olabileceği (özellikle analitik raporda) URL uzunluk sınırına ulaşabileceğiniz birkaç durum vardır. Bu gibi durumlarda GET yönteminden POST yöntemine geçiş yapmak çok kolaydır. Tüm mesele parametreleri URL’den İstek GÖVDESİ’ne değiştirmektir.

Yine SEGW etkilenmez.

GET METHOD
ls_param-param1 =  VALUE #( dt-it_filter[ property = `PARAM1` ]-select_options[ 1 ]-low  OPTIONAL ).
ls_param-param2 =  VALUE #( dt-it_filter[ property = `PARAM2` ]-select_options[ 1 ]-low  OPTIONAL ).
POST METHOD
 DATA: BEGIN OF ls_param,
            param1 TYPE t_member,
            param2 TYPE tt_context,
          END OF ls_param,
/ui2/cl_json=>deserialize( EXPORTING json = <params_from_odata_body>  CHANGING data = ls_param )

Yük dağılımı:

Aynı anda birden fazla tablo ve grafik verisini dışa aktardığımız birkaç durum vardır. Bu mimari, sıralı işlem için bir performans sorunu olduğunda size anında bölme seçeneği sunar. Örneğin 2 grafik ekranda paralel olarak tetiklenebilir (veya hepsi aynı anda). Bu tamamen senaryoya bağlıdır.

Kullanıcı ana sayfayı açtığında aşağıdaki 4 yöntem aynı anda tetiklenir. Bunları 4 dahili tablo ile 1 yöntemde birleştirebilir veya 4 yönteme bölebilir ve her yöntem kendi dahili tablosunu dışa aktarabilirim. Bu tamamen tasarım ilkesi ve ağır hizmetler için performans kararıyla ilgilidir.

Her şeyi günlüğe kaydetme:

API Handler katmanının altında, BADI’yi bulduğu ve BADI filtrelerine dayalı olarak belirli sınıf / yöntemleri tetiklediği yer vardır. Bu bize hizmetin tüm gövdesini/parametrelerini/url’sini, kimin tetiklediğini, kaç saniye sürdüğünü, hata olup olmadığını veya bununla ilgili herhangi bir şeyi kaydetme avantajı sağlar.

Kullanıcılar için yıl sonunda bazı iyi istatistiksel raporlar alabilir ve bir kullanım raporu hazırlayabiliriz. Bu, en çok hangi sayfanın kullanıldığını, uç noktalar için ortalama saniyeleri, hataları, başarısız istekleri ve diğer bazı ayrıntıları içerir.

logger->timer_Start( ).

<BADI is triggered here>

logger->timer_end( ).

* log service details. User, time, etc.

logger->log_service_data( ).

async/await kullanarak örnek çağrı

Aşağıda görebileceğiniz gibi, hangi BADI’nin tetikleneceğini tanımlamak için sadece TMPL_ID’mizi ve hangi yöntemin tetikleneceğini tanımlamak için DETAIL’i ayarlıyoruz. URL filtresi duyuru kimliği için gönderilir.

// SERVICE.JS	
	getAnnouncementDetail: function(iv_annoid) {
			var sPath = "/DATASet(TMPL_ID='APP1_ANNO',ACTION='GET',DETAIL='GET_ANNO_DETAIL')";
			var aFilterValues = ["ANNO_ID=" + iv_annoid];
			var params = {
				sModelName: LOCAL_MODELS.ANNOUNCEMENT_EDIT,
				aFilterValues: aFilterValues
			};

			return this.getODataAPI().read(sPath, params);
		},


// controller.js
_GetAnnouncementDetail: async function (iv_annoid) {
            sap.ui.core.BusyIndicator.show(0);
            var oResponse = await this.Service.getAnnouncementDetail(iv_annoid);
// some bindings here
            sap.ui.core.BusyIndicator.hide();
        },

Sonuç:

Bu mimariyi ilk SAPUI5 projemde uyguladım ve daha sonra biraz geliştirdim ve en son müşterimde de kullanmaya başladım. 2016’dan beri servise hiç dokunulmadı ancak aynı motor kullanılarak yeni BADI uygulamaları gelmeye devam ediyor. En büyük avantajı 3 farklı alanda gördük;

  • Geliştirme Hızı
  • Esneklik
  • Üçüncü Taraf Kütüphane Entegrasyonu

Yukarıdakine benzer şekilde, benzer şekilde çalışan bir dosya yükleme çözümü de uyguladım, ancak daha sonra yazabilirim.

2018’den beri bu yaklaşımla ilgili yazmayı planlıyordum ve sonunda yazabildim. Birçok geliştiricinin aynı fikirde olmayabileceğini biliyorum ama biz bunu sevdik ve hala sorunsuz bir şekilde kullanıyoruz.

Aşağıda en son projeden örnek bir ekran görüntüsü var.

  • Duyuru Bölümü (kullanıcının resim yükleyip kırpabileceği ve HTML içeriği ekleyebileceği yer)
  • Yönetici ekranından etkinlik ekleyebilecekleri takvim kontrolü
  • Faaliyetlerle ilgili bazı grafik verileri.
  • Sol menü (auth’a dayalı olarak arka uçtan JSON olarak da geliyor)

GÜNCELLEME :
Küçük bir güncelleme, yakın zamanda büyük bir veri şirketi olan Palantir de REST API mimarisi ile ilgili bir makale yayınladı. Temel olarak şunları söylüyorlar;

İlk olarak, birçok istemcinin Oluştur, Güncelle ve Sil arasında ayrım yapmasına gerek yoktur – çok amaçlı bir “set” işlemi isterler.

Bu da bu yaklaşıma benziyor. Evet, REST ile ilgili ve bu makale ODATA ile ilgili, ancak yukarıdaki yaklaşımla Palantir’in makalelerinde anlattıklarına çok benziyor. Makaleyi aşağıda bulabilirsiniz.

Makale için Bilen Çekiç’e teşekkür ederim.

Kaynak: https://blog.palantir.com/rethinking-crud-for-rest-api-designs-a2a8287dc2af

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir