Kodunuz spagetti mi olsun?

Bu yazımda SAP projelerinde yapılan geliştirmeler hakkındaki görüşlerimi paylaşmak istedim. Projelerde genel olarak kavramsal tasarım aşamasından sonra başlanan uyarlama ve geliştirme aşamasında, geliştirmeler ile ilgili olarak bir kılavuz hazırlanır, veritabanı tablosu, program, fonksiyon gibi geliştirme nesneleri için isimlendirme kuralları konur ve projeye dahil olan ABAP geliştiricilerinin bu kılavuzdaki kurallara uymaları beklenir. Bu tür kılavuzlar geliştiriciler için iyi bir örnek olsa da tek başına genellikle yeterli olmaz. Hatta proje ilerledikçe bu kurallara pek uyulmadığı, her geliştiricinin kendi alışkanlığının  ağır bastığı ve üretilen kodlarda farklılıklar olduğu görülür. Bunun yanında projede görev alan her geliştirici bilgi, beceri ve deneyim açısından aynı düzeyde olmadığından (olmaması da çok doğaldır), isimlendirmedeki farklılıkların yanında kod kalitesinde ve temiz kod yazma yaklaşımlarında da farklılıklar ortaya çıkar. Buna ek olarak, ERP sistemleri şirketlerde uzun soluklu kullanıma sahiptir. Şirketin iş süreçlerinin değişmesi ve gelişmesi ile birlikte geliştirmeler de güncellendiğinden, kod standardının belli seviyede tutulması gittikçe zorlaşır. Projenin canlı kullanıma alınmasından sonra başka geliştiriciler de görev alabilir, eskiler de devam etmeyebilirler. Bir süre sonra bazı programlar iş süreçlerinin değişiminden dolayı tümüyle kullanılmaz hale gelirler. Bu sebeplerle, işin başından itibaren kod standartları, temiz kod ve kod bankası gibi yaklaşımlar korunmazsa, belli bir olgunluğa gelen ERP sistemlerinde ciddi oranda kirli kod oluşmaya başlar. Sürüm yükseltmelerde ve süreçlerdeki değişikliklerin koda yansıtılmasında da yazılım bakım maliyetleri yükselir.

Bu noktada durup, SAP sistemlerindeki gelişmeden kısaca bahsetmek istiyorum. Son yıllarda SAP, gerek dış alımlarla gerekse kendi yeni geliştirmeleri ile yazılım portföyünü oldukça genişletti. Müşteri firmaların belli süreler ile bu yeni ürünlere geçişleri bekleniyor. ERP bileşeni özelinde, firmaların belli seçenekleri var. Birincisi SAP ERP sistemlerini koruyup, veritabanını HANA sistemine çevirmektir. Diğeri ECC sisteminden S/4 HANA sistemine geçiştir. Bunu da kendi içinde iki şekilde yapabilirler. Birincisi sürüm yükseltmedir (teknik olarak oluyor diye biliyorum, düzeltmelere açıktır). İkincisi mevcut ECC sistemini kaldırıp yerine sıfırdan ERP projesi yaparak S/4 HANA’ya geçmektir. Sadece HANA veritabanına geçiş, teknik olarak mümkün ve S/4 HANA projesi yapmaktan daha az maliyetli bir seçenektir. Bu tercih edilirse, veritabanı geçişinden sonra sistemdeki geliştirmelerin belli kurallara göre gözden geçirilmesi, HANA’da değişen bazı durumlar için kod değişikliği yapılması gereklidir. Örneğin tablodan birden çok kayıt okunması sırasında kayıtların birincil anahtarlara göre sıralı geleceği varsayımı HANA’da geçerli değildir, programda tek satır kod değişmese bile, alınan sonuçlar farklı olabilir. Bu zorunlu kontrollerden sonra, özellikle performans problemi olan geliştirmelerde HANA veritabanının performansından gerçek anlamda yararlanmak için kodlarda belli değişikliklerin yapılması gereklidir. CDS görünümlerinin oluşturulması, AMDP olarak isimledirilen ABAP sınıfları ile veritabanı prosedürlerinin yazılması ve programların gerekli yerlerinde yapılan veritabanı erişimlerinin bu yeni yaklaşımlar ile değiştirilmesi faydalı olur. Benzer çalışmaların sürüm yükseltmesi yolu ile S/4 HANA’ya geçilmesinde de yapılması çok yerindedir. Tümüyle sıfırdan S/4 HANA projesinde de yeni yapılacak geliştirmelerde HANA veritabanının bu özelliklerinin öncelikli tercih edilmesi SAP tarafından da önerilmektedir.  Bunlar HANA veritabanı özelinde yapılabilecek çalışmalar. Veritabanı ya da SAP ERP sisteminizin sürümü ne olursa olsun, performans ve kod kalitesi açısından yapılabilecekler de var. Biraz da bunlardan bahsetmek istiyorum. Veritabanı ya da ERP bileşeni açısından herhangi bir sürüm yükseltmesi planlanmasa bile sistemdeki geliştirmelerin bakım maliyetlerini azaltacak yaklaşımlardır. Bunlar, başta bahsettiğim isimlendirme standartlarından daha önemlidir, temiz kod yazma prensiplerini içerir.

Her ERP projesinin olmazsa olmazı rapor geliştirmeleridir. Her modülün mutlaka rapor ihtiyaçları olur. Rapor tipi programlarda genelde bir seçim ekranı olur. Seçim ekranına girilen kriterlere göre veritabanından kayıt okuma kısmı olur ve son olarak da bulunan kayıtları görüntülemek için ALV ekran bileşeni kullanılır. Genellikle de bir rapor programı, hazırlandıktan sonra kendi başına yürütülür ve diğer programlar tarafından da pek çağırılmaz. Bu hali ile rapor programları monolitik yapıya sahiptir, tüm işlevleri kendi içinde tek parça olarak barındırır. Bu noktada yapılabilecek iyileştirme, bu monolitik yapıyı, modüler yapıya dönüştürmektir. Örneğin veri okuma kısmına başka bir programdan ya da başka bir sistemden erişilmesi gerekebilir. Bu durumda, seçim ekranından kriterleri alıp rapor verisini hazırlayan kısım, rapor programının içinden çıkarılıp bağımsız bir RFC fonkiyonu ya da global sınıfa çevirilebilir. Bu yaklaşım yazılım dünyasında MVC (Model-View-Controller) olarak isimlendirilir. Veri hazırlamak için geliştirilen foksiyon ya da sınıf, bu yaklaşımın “model” kısmına karşılık gelir. Rapor programının içindeki ALV ekran bileşeni “view” rolünü üstlenir. Eğer ALV raporunuz kullanıcıdan belli girişleri alıyorsa (seçilen satırlar için örneğin yeni bir belge oluşturma, satıra çift-tıklanınca başka bir programı çağırma gibi), (genellikle) rapor programı içinde tanımlanan ve ALV nesnesinin olaylarını karşılayan sınıf da “controller” görevini üstlenir. Controller bileşeni, model ve view arasındaki etkileşimi yönetir. Veri okuma kısmını rapor programında çıkardığınızda, elinizde mobil ya da diğer harici sistemlerden çağırılabilecek bir kod bileşeni olur. Örneğin ileride raporlarınızı SAPUI5 ya da başka bir web önyüz uygulaması ile geliştirmek isterseniz, OData geliştirmesi yapmanız gerekecektir, burada OData servisi içinde doğrudan kullanabileceğiniz kodunuz hazır olur. Raporun veri okuma ve hesaplama mantığı değiştiği zaman bunu tek bir yerde yapmanız, hem ALV raporunun hem de SAPUI5 uygulamasının aynı anda güncellenmesini sağlar. Özellikle OData servisleri ile daha uyumlu olması açısından birden çok kayıt döndüren fonksiyon ya da sınıflarınızda OData servisinde yerleşik olan sayfalama (pagination) parametrelerinin de olmasını öneririm. OData servislerinin GetList metotlarında “skip”, “top” gibi parametreler ile sıralama ve filtreleme için de parametreler bulunmaktadır. Veri okuma kısmını yapan servisiniz bunları OData’nın GetList metodundan alıp işleyebiliyorsa, Fiori uygulamalarınızı daha doğal biçimde desteklersiniz.

Benzer yaklaşım, SmartForm, SAPscript, Adobe Forms gibi yazılı çıktı amacıyla yapılan geliştirmeler için de geçerlidir. Bazı projelerde, bu çıktı geliştirmelerinin içinde çeşitli hesaplamaların yapıldığını görebiliyoruz. Özellikle çıktının içinde vergi, indirim toplamı gibi kritik bilgiler hesaplanıyorsa bunların mümkün olduğunca çıktıyı üreten yazdırma programı içinde hesaplanması ve hatta tüm çıktı hesaplarını yapan ve çıktıya veri hazırlayan kısmın da ayrı fonksiyon ya da servis olması daha iyi bir tasarım olabilir. Yine harici sistemlerden çıktıdaki bilgilerin birebir aynısı istendiği durumda bunu yine servis olarak çalışan fonksiyon/sınıf ile sağlayabilirsiniz. Burada da MVC prensibinin “view” kısmı çıktının kendisidir (SmartForm vs). “model” kısmı, veri hazırlayan servistir. Uyarlamada çıktı türünün içinde tanımlanan yazdırma programının kendisini de “controller” olarak kabul edebiliriz.

Raporların haricindeki veri girişi sağlayan programlar için de benzer şeyler söylenebilir. Bu programların özelliği, sadece veri okumaları değil, veriyi güncellemeleridir. Bunlar, OData servislerindeki CRUD-Q (Create-Read-Update-Delete-Query) işlemlere karşılık gelir. Bu sebeple, veri girişi sağlayan diyalog program (module pool) geliştiriyorsanız, programın PAI kısımlarında yeralan veri kontrolu ve güncelleme kısmlarını da servis yaklaşımı ile hazırlayabilirsiniz. Sistem standardında bulunan BAPI fonksiyonlarını incelediyseniz, her iş nesnesi için (satınalma sipariş, müşteri siparişi, malzeme ana verisi gibi), BAPI_XXX_CREATE, BAPI_XXX_CHANGE, BAPI_XXX_DELETE, BAPI_XXX_GET_DETAIL ve BAPI_XXX_GET_LIST gibi fonksiyonların olduğunu görmüşsünüzdür. Kendi uygulamalarınız da bu şekilde hazırlarsanız, Fiori/mobil uygulama geçişlerine hazırlıklı olursunuz.

Kod kirliliğinin en çok görüldüğü yerlerden biri de standart uygulamalar içindeki kullanıcı-çıkışları ve Badi’lerdir. Özellikle satış süreçleri şirketlerde çok dinamik olduğundan ve farklılıklar gösterdiğinden, Satış ve Dağıtım modülünün kullanıcı çıkışları zamanla hem çok fazla kod barındırmaya başlar, hem de her süreç için kullanıcı-çıkışının metotlarında ya da FORM rutinlerinde  büyük kod blokları oluşmaya başlar, kullanım yoğunluğuna göre benzer durum diğer modüller için de geçerlidir. Özellikle IF-ELSE-ENDIF koşul blokları bazı durumlarda takip edilemez büyüklüğe gelebilir. Ayrıca belli bir süreç için yazılmış olan kodda CHECK komutu varsa, bunu takip eden diğer süreçlerin kodları tümüyle çalıştırılamaz duruma gelir. Kullanıcı çıkışı kodu, birden fazla ABAP geliştiricisinin aynı anda çalışması izin vermez (kodu sadece biri değiştirebildiği için) ve değişiklikleri canlı sisteme taşımak da sorunlu olabilir (birbirini bekleyen geliştirmelerden dolayı). Buna çözüm olarak genelde her süreç için ya ayrı FORM rutinleri ya da INCLUDE programlar hazırlanıyor ve asıl kullanıcı-çıkışı rutini içinde bu rutinler PERFORM ile çağırılıyor ya da INCLUDE programlar doğrudan bunun içine dahil ediliyor. Bu iki yaklaşım da yapılan geliştirmeleri tümüyle birbirlerinden izole edemez. Bunun yerine her kullanıcı çıkışında, amaca uygun olarak ana programdan ya da ilgili kullanıcı-çıkışından gerekli değişkenleri alıp çalışabilen arayüzler tasarlanabilir. Bu arayüzleri SE24 işlemi ile tanımlayabilirsiniz. Sonra yine SE24 işlemi ile her farklı süreç için bu arayüzü uygulayan bir sınıf oluşturabilirsiniz. Her sınıf diğerinden bağımsız bir ana program olacaktır. Arayüzün metodu içinde sadece kendisinin sorumlu olduğu kodu içerecektir. Asıl kullanıcı-çıkışı rutininde de bu arayüzü uygulayan sınıflar sistemden bulunur, her bir sınıf için bir nesne üretilip arayüzü metodu çağırılabilir. Böylece kullanıcı-çıkışına yeni bir süreç için kd eklenmek gerekirse, SE24 işlem kodu ile yeni bir global sınıf tanımlamak ve aynı arayüzü uygulamasını sağlamak yeterli olacaktır. Sınıfların içindeki kodların geliştirmesini farklı programcılar üstlenebilir, bunlar farklı zamanlarda ve transport requestler ile canlı sisteme taşınabilir.

Kullanıcı-çıkışlarındaki yaklaşımı, sıfırdan uygulama geliştirirken de kullanabilirsiniz. Nesne tabanlı programlamanın temeli aslında tek bir prensibe dayanır: Uygulamada değişebilecek yerleri diğer kısımlardan gizle ve bunları ana programın yapısını değiştirmek zorunda kalmadan başka kod parçaları ile değişebilecek şekilde tasarla. Tasarım kalıplarında anlatılan tüm problem tiplerinin temelinde bu yatar. Örneğin bir uygulamanız var, karmaşık veri girişi olan bir program olmasına da gerek yok, bir rapor programı da olabilir. Rapor programının içinde örneğin FI belge türüne göre farklı noktalarda IF-ELSE blokları yazmaya başladınız. Örneğin birinci IF-ELSE bloğunda tarih hesaplaması olsun. İkincisinde tutar hesaplansın, üçüncüde de farklı e-postalar gönderilsin. Programa ileride yeni bir belge türü eklenirse ne olacak? Programcı belge türüne göre koşul yazılan her yeri gözden geçirmek zorundadır. Farklı IF-ELSE kod bloklarının sayısı artarsa, hata yapma olasılığı da artar, programcı bunların bir kısmını atlayabilir, yeni belge türü için kod eklerken yanlışlıkla mevcut kodu bozabilir. Bunun yerine, belge türü bazında, farklı kod bloklarına karşılık gelecek şekilde metotlar içeren bir arayüz yapılabilir. Her belge türü için bir sınıf oluşturulabilir ve bu sınıf, arayüz metotlarını uygular. Hatta belge türü bazında sınıf adını içeren yeni bir Z’li tablo yapılabilir ve programa dahil edilen belge türleri için bu tabloya kayıt eklenir, karşılık gelen sınıf isimleri ile birlikte. Sonra ana rapor programında belge türünün kontrol edildiği yerlerde bu tablodan belge türüne göre okuma yapılır, belge türüne karşılık gelen sınıf bulunur ve bundan bir nesne üretilir (CREATE OBJECT ya da NEW ile). Sonra ilgili IF-ELSE bloğu arayüzde hangi metoda karşılık geliyorsa, nesne üzerinden o metot çağırılır. Böylece program içindeki IF-ELSE kısımlarını sadece tek bir noktada toplamış olursunuz, belge türüne göre ilgili sınıf için tek bir yerde  nesne üretilir ve diğer tüm IF-ELSE bloklarında sadece bu nesnenin ilgili metodu çağırılır. İleride yeni bir belge türü rapora dahil edilirse, SE24 işlemi ile yeni bir sınıf hazırlanır, arayüz metotları kodlanır ve belge türü ile yeni sınıf adı Z’li uyarlama (parametre) tablosuna eklenir. Rapor programında tek satır kod değiştirmek zorunda kalmazsınız.

Genel olarak projelerde çözüm üretirken kullandığım yaklaşımlar bunlar, yeni çözüm örnekleri ürettikçe paylaşacağım. Umarım sizlere de faydalı olur. Herkese projelerinde kolaylıklar dilerim.

Yorum bırakın