Softtech 2021 Teknoloji Raporu

 



PDF olarak buradan indirebilirsiniz.

Siteyi buradan ziyaret edebilirsiniz.

Java Object Sınıfı ve Metodları - wikibooks.org çevirisi

 

Java Programming/API/java.lang.Object



java.lang.Object


Object sınıfı, tüm Java sınıflarının üst sınıfıdır. Bu sınıftan tüm Java sınıfları miras alınır. Bu, tüm Java sınıflarında mevcut olan metodlara sahip olmamızı mümkün kılar. Bu, durumun söz konusu olmadığı 
C ++ ile karşılaştırıldığında işleri basitleştirir.

Object sınıfı metodları ve açıklamaları

boolean equals( Object o ); :  Nesneleri karşılaştırmak için genel bir yol sağlar

Class getClass();             :  Class sınıfı bize nesne hakkında daha fazla bilgi verir

int hashCode();                  :  Bir koleksiyondaki nesneleri aramak için kullanılan bir hash değeri döndürür

void notify();                      :  synchronizing thread'lerde kullanılır

void notifyAll();                 :  synchronizing thread'lerde kullanılır

String toString();                :  Nesneyi String'e dönüştürmek için kullanılabilir

void wait();                         :  synchronizing thread'lerde kullanılır

protected Object clone() throws CloneNotSupportedException ; : Mevcut nesneyle tamamen aynı olan yeni bir nesne döndür.

protected void finalize() throws Throwable; : Bu metod garbage collection nesneyi toplamadan hemen önce çalışır.

equals() Metodu


Boolean equals (Object o) yöntemi, nesneleri eşitlik açısından karşılaştırmak için genel bir yol sağlar. Sınıfınızda onu override etmeniz gerekir. O zaman şu şekilde yazabilirsiniz:
  public boolean isCustomerExist( Customer newCustomer )
{
   boolean isRet = false;
   Iterator iter = _collAllCustomer.iterator();
   while ( iter.hasNext() )
   {
      if ( newCustomer.equals( (Customer) iter.next() )
      {
         // -- Customer was found ---
         isRet = true;
      }
   }
  return isRet;
}
  
  
equals() öğesini geçersiz kıldığınızda, her zaman hashCode() öğesini de geçersiz kılmanız gerektiğini unutmayın, böylece iki yöntem tutarlı olur. İki nesne eşitse, aynı hashcode'a sahip olmaları gerekir.

hashCode() Metodu


Çoğu durumda, bu yöntemin varsayılan uygulaması nesne için benzersiz bir sayı döndürdüğünden, bu yöntemi geçersiz kılmamalısınız. Sayı, nesne bir koleksiyona konulduğunda kullanılır. Büyük bir koleksiyonda bir nesneyi bulmak, nesneler tek tek sırayla karşılaştırılırsa biraz zaman alabilir. Aramayı hızlandırmak için, nesneler bir tamsayı karma kod ile ağırlıklandırılan bir ağaç yapısına yerleştirilebilir. Ağaçta gezinirken karma kodu karşılaştırarak, nesne karşılaştırma sayısı azaltılabilir.

   _______ A _____
   |              |  
__ B__          __C__
|     |        |     |
D     E        F     G
...  ...      ...   ...
Size nasıl çalıştığı hakkında genel bir fikir vermek için yukarıdaki şemaya bakın. G nesnesini aradığımızı varsayalım. Ağacın her bir 'düğümünde' hangi yöne gideceğimize karar verebilirsek, 3 adımda G nesnesine ulaşırız.

Doğrusal bir aramada yorumla:

A --- B  ----- C  ---- C  ---- D  ---- E ---- F ---- G
G nesnesine ulaşmak için 8 adıma ihtiyacımız var.

Böylece ağaç yapısı ile arama daha hızlı olacaktır. Ancak yeni bir nesne eklemek daha yavaş olacaktır çünkü ağaç yapısının korunması gerekir. Yeni nesnenin ağaçtaki yeri ilk önce bulunmalıdır.

getClass() Metodu

Programınızdaki her sınıf için bir Class nesnesi vardır. Her dizi, aynı öğe türüne ve boyut sayısına sahip tüm diziler tarafından paylaşılan bir Class nesnesi olarak yansıtılan bir sınıfa da aittir. İlkel Java türleri (boolean, byte, char, short, int, long, float ve double) ve void anahtar sözcüğü de Class nesneleri olarak temsil edilir. Class'ın genel kurucusu yoktur. Bunun yerine Class nesneleri, sınıflar yüklenirken Java Sanal Makinesi tarafından otomatik olarak oluşturulur.

Class'ın en popüler kullanımı, çalışma zamanı sırasında nesnenin sınıf adını bulmaktır.
  ,
  import com.yourCompany.Customer;
...
Object obj = new Customer();
...
System.out.println( "Name:" + obj.getClass().getName() );
  
 

toString() Metodu


Bu yöntem, bir nesneyi bir String'e dönüştürmek için kullanılabilir. Nesneleri String'e dönüştürmek için birçok yerde otomatik olarak kullanılır; örneğin: PrintStream'de, StringBuffer'da ve nesnelerde kullanıldığında string concatenation operatörü için.

Varsayılan uygulama, sınıf adı ve hash kodu ile garip bir dize döndürür.

Örneğin:


  
  String str = "This customer is " + objCust;
  
ToString () yöntemi objCust nesnesinde çağrılır. ToString () yöntemi, debugging için de kullanılabilir:
  
  public class Customer
{
   private String _name;
   private String _address;
   private String _age;
...
   public String toString()
   {
       StringBuffer buf = new StringBuffer();
       buf.append( "Name   = " );  buf.append( _name );     buf.append( "\n" );
       buf.append( "Address= " );  buf.append( _address );  buf.append( "\n" );
       buf.append( "Age    = " );  buf.append( _age );      buf.append( "\n" );
    ...
       return buf.toString();
   }
...
}
  
Bundan sonra, kodunuzda ne zaman bir müşteri nesnesinin ne olduğunu görmek istersiniz, sadece şunu kullanabilirsiniz:
  
  System.out.println( objCustomer );
  

Synchronizing Thread'lerin Metodları


Çok iş parçacıklı bir ortamda, birden fazla iş parçacığı bir kaynağa erişip değiştirebildiğinde, sonuç tahmin edilemez olabilir. Örneğin, birden fazla iş parçacığı tarafından artırılan bir sayaç değişkenimiz olsun.

Dikkat! Senkronizasyon belirsiz bir terimdir. Aynı kod bölümünü aynı anda yürüten tüm iş parçacıklarını yapmaktan ibaret değildir. Tam tersi. Herhangi iki iş parçacığının aynı kod bölümünü aynı anda yürütmesini engeller. Bir işlemin sonunu ikinci bir işlemin başlangıcıyla senkronize eder.





Yukarıdaki kod, aşağıdaki alt işlemlerle oluşturulmuştur:

Read; değişken sayacını oku
Add; değere 1 ekle
Save ; yeni değeri değişken sayaca kaydet
Diyelim ki iki iş parçacığının bu kodu çalıştırması gerekiyor ve eğer sayaç değişkeninin başlangıç değeri sıfır ise, işlemlerden sonra değerin 2 olmasını bekliyoruz.


Yukarıdaki durumda Thread 1 işlemi kaybolur, çünkü Thread 2 kendi değerinin üzerine yazar. Thread 2'nin, Thread 1 işlemi bitirene kadar beklemesini istiyoruz. Aşağıya bakınız:



Kritik Bölüm (Critical Section)
Yukarıdaki örnekte, sayaç+=1 kodu herhangi bir zamanda bir ve yalnızca bir iş parçacığı tarafından yürütülmelidir. Buna kritik bölüm denir. Programlama sırasında, çok iş parçacıklı bir ortamda, kritik bir bölüme ait olan tüm kod parçalarını tanımlamalı ve herhangi bir zamanda yalnızca bir iş parçacığının bu kodları çalıştırabildiğinden emin olmalıyız. Buna senkronizasyon denir.

Konuları senkronize etme (Synchronizing threads)
Kritik bir bölüm koduna iş parçacığı erişimi, iş parçacıkları arasında senkronize edilmelidir, yani herhangi bir zamanda yalnızca bir iş parçacığının onu çalıştırabilmesini sağlamak için.

Nesne monitörü (Object monitor)
Her nesnenin bir Nesne monitörü vardır. Temel olarak, bir iş parçacığı tarafından kritik bir bölüm kodunun yürütülüp yürütülmediğini gösteren bir semafordur. Kritik bir bölümün yürütülebilmesi için iş parçacığının bir Nesne izleyicisi edinmesi gerekir. Bir seferde yalnızca bir iş parçacığı o nesnenin monitörüne sahip olabilir.


Bir iş parçacığı, üç yoldan biriyle nesnenin monitörünün sahibi olur

Bu nesnenin synchronized instance method yürüterek. Senkronize edilmiş anahtar kelimeye bakın.

Nesne üzerinde senkronize olan synchronized statement gövdesini yürüterek. Senkronize edilmiş anahtar kelimeye bakın.

Class türündeki nesneler için, o sınıfın synchronized static method yürüterek.

Object Monitor senkronizasyonla ilgilenir, öyleyse neden "wait() ve notify() yöntemlerine" ihtiyacımız var?

Senkronizasyon için onlara gerçekten ihtiyacımız yok, ancak bazı durumlarda bunları kullanmak güzel. Güzel ve düşünceli bir thread onları kullanacaktır. Kritik bir bölümün yürütülmesi sırasında iş parçacığı sıkışmış olabilir, devam edemeyebilir. Bir IO ve diğer kaynakları beklediği için olabilir. Her durumda, iş parçacığının nispeten uzun bir süre beklemesi gerekebilir. İş parçacığının nesne monitörüne tutunması ve diğer iş parçacıklarının işini yapmasını engellemesi bencillik olurdu. Böylece iş parçacığı, nesne üzerinde wait() yöntemini çağırarak bir 'bekleme' durumuna gider. İş parçacığının nesne monitörünü aldığı aynı nesne olmalıdır.
Öte yandan, bir iş parçacığı, yalnızca, kaynak kullanılabilir olduğunda notify() yöntemini çağıracak en az bir başka iş parçacığı varsa, wait() yöntemini çağırmalıdır, aksi takdirde iş parçacığı, bir parametre olarak zaman aralığı belirtilir.
Bir benzetme yapalım. Bazı eşyaları almak için bir dükkana girersiniz. Tezgahta sıraya girersiniz, satış memurunun dikkatini çekersiniz - onun "nesne-monitörünü" alırsınız. İstediğiniz ürünü soruyorsunuz. Bir depodan bir ürün getirilmesi gerekiyor. Beş dakikadan fazla sürecektir, bu nedenle satış memurunu serbest bırakırsınız ("nesne monitörünü" ona geri verin), böylece diğer müşterilere hizmet verebilir. Bekleme durumuna geçersiniz. Diyelim ki bekleyen beş müşteri daha var. Depodan eşyaları getiren başka bir satış memuru var. Bunu yaparken, ilk satış memurunun dikkatini çeker, nesne monitörünü alır ve bekleyen bir veya tüm müşteriyi/müşterileri bilgilendirir, böylece bekleyen müşteri(ler) uyanır ve müşterinin dikkatini çekmek için tekrar sıraya girer. ilk satış memuru.
Bekleyen müşteri ile ürünleri getiren satış memuru arasındaki senkronizasyona dikkat edin. Bu bir tür üretici-tüketici senkronizasyonudur.
Ayrıca, ilk satış memuruna ait yalnızca bir nesne monitörü olduğunu unutmayın. Beklemeden ve bir bildirimin gerçekleşebilmesi için önce bu nesne-monitörünün/memurun notify edilmesi gerekir.

final void wait() yöntemi
Geçerli iş parçacığı, bu nesnenin monitörüne sahip olmalıdır. İş parçacığı, bu monitörün sahipliğini serbest bırakır ve başka bir iş parçacığı, bu nesnenin monitöründe bekleyen iş parçacıklarının, notify yöntemine veya notifyAll yöntemine yapılan bir çağrı yoluyla uyanmasını bildirene kadar bekler. İş parçacığı daha sonra monitörün sahipliğini yeniden elde edene ve yürütmeye devam edene kadar bekler.
final void wait (long time)
Bekleme ile aynıdır, ancak bir bildirim olup olmadığına bakılmaksızın belirtilen süre geçtikten sonra iş parçacığı uyanır.
final void notify()
Bu yöntem yalnızca bu nesnenin monitörünün sahibi olan bir iş parçacığı tarafından çağrılmalıdır. Bu nesnenin monitöründe bekleyen tek bir iş parçacığını uyandırır. Bu nesnenin monitöründe çok sayıda iş parçacığı bekliyorsa, bunlardan biri uyandırılmak üzere seçilir. Seçim keyfidir ve uygulamanın takdirine bağlı olarak gerçekleşir. Bir iş parçacığı, bekleme yöntemlerinden birini çağırarak bir nesnenin monitöründe bekler.
Uyandırılan iş parçacığı, mevcut iş parçacığı bu nesne üzerindeki kilidi bırakana kadar ilerleyemeyecektir. Uyandırılan iş parçacığı, bu nesne üzerinde eşzamanlamak için aktif olarak rekabet edebilecek diğer iş parçacıklarıyla olağan şekilde rekabet edecektir; örneğin, uyanmış iş parçacığı, bu nesneyi kilitleyecek bir sonraki iş parçacığı olma konusunda güvenilir bir ayrıcalığa veya dezavantaja sahip değildir.
final void notifyAll()
notify() ile aynıdır, ancak bu nesnenin monitöründe bekleyen tüm iş parçacıklarını uyandırır.

sleep() ve wait() yöntemleri arasındaki farklar nelerdir?
Thread.sleep(milis)
Bu, Thread sınıfının statik bir yöntemidir. Şu anda yürütülmekte olan iş parçacığının belirtilen milisaniye sayısı boyunca uyumasına (yürütmeyi geçici olarak durdurmasına) neden olur. İş parçacığı herhangi bir monitörün sahipliğini kaybetmez. Bu, iş parçacığının bir nesne izleyicisi varsa, bu monitöre ihtiyaç duyan diğer tüm iş parçacıklarının engellendiği anlamına gelir. Bu yöntem, iş parçacığının herhangi bir monitörü olup olmadığına bakılmaksızın çağrılabilir.
wait()
Bu yöntem, Object sınıfından miras alınır. İş parçacığı, wait() yöntemini çağırmadan önce o nesnenin nesne monitörünü almış olmalıdır. Nesne izleyicisi wait() yöntemiyle serbest bırakılır, bu nedenle bu nesne izleyicisini isteyen diğer bekleyen iş parçacıklarını engellemez.



Mikro Servislerin 6 Avantajı - Hazelcast Makalesi Çevirisi

 Mikro Servislerin Altı Avantajı

İşletmeler giderek daha karmaşık çözümler ürettikçe mikro servisler konusu önemli bir gündem olmaya devam ediyor. Mikro servislerin birçok avantajı vardır ve bu makale, bir mikro servis mimarisinin sizin için neden iyi çalışabileceğini tartışmak için bunları altı bölümde toplamaktadır.

Giriş

Mikro servisler, daha büyük bir çözüm oluşturmak için birbirleriyle çalışan, sınırlı kapsam için tasarlanmış bir dizi yazılım uygulamasıdır. Her mikro servis, adından da anlaşılacağı gibi, yüksek düzeyde modülerleştirilmiş bir genel mimari oluşturmak adına minimum yeteneklere sahiptir. Bir mikro servis mimarisi, her bir mikro servisin montaj hattındaki bir istasyon gibi olduğu bir üretim montaj hattına benzer.

Her istasyonun belirli bir görevden sorumlu olması gibi, aynı şey mikro servisler için de geçerlidir. Her istasyon/mikro servis, ilgili sorumluluklarda uzmanlığa sahiptir, böylece iş akışında ve çıktılarda verimliliği, tutarlılığı ve kaliteyi destekler. Bunu, her istasyonun tüm ürünün kendisinden sorumlu olduğu bir üretim ortamıyla karşılaştırın. Bu, tüm görevleri aynı süreç içinde gerçekleştiren monolitik bir yazılım uygulamasına benzer.




Mikro servisler, genel bir çözüm sunmak için birlikte çalışan ayrı bileşenlerdir.

Açık olmak gerekirse, mikro servisler ve montaj hatlarının kesinlikle seri hale getirilmiş bir sırada çalışması gerekmediğinden, montaj hattı analojisi tek bir doğrusal akış anlamına gelmez. Mikro servislerle, veriler kolayca kopyalanabilir ve daha sonra veri hattının bir parçası olarak birden çok konuma dağıtılabilir ve böylece birden çok yol alabilir ve yönlendirilmiş bir döngüsel olmayan grafikte (DAG) olduğu gibi farklı şekillerde işlenebilir. Bu, veri hattını nasıl tanımlayacağınız konusunda size daha fazla esneklik sağlar ve ayrıca akışta daha fazla çıktı oluşturmak istemeniz durumunda pipelinenızı genişletme çabasını basitleştirir.



Bir mikro servis mimarisindeki veri akışı, bir DAG ile temsil edilebilir.

Mikro servisler son zamanlarda popülerlik kazanmış olsa da, arkasındaki kavramlar yeni değil. Modüler programlama, endişelerin ayrılması ve servis odaklı mimari (SOA) gibi konuların tümü, bir mikro servis mimarisinin hedefleriyle uyumlu ilkelere sahiptir. Bu, mikro servislerin yıllar içinde kullandığımız en iyi uygulamalara dayandığı ve bu nedenle kullanımlarının kolayca doğrulanabileceği anlamına gelir. Bazı geliştirme ekipleri, mikro servis mimarisini zorunlu olarak adlandırmadan zaten benimsemiş olabilir. Big Data pipelineları tipik olarak verileri bir seferde bir adım olarak işler ve bu da mikro servis yaklaşımıyla iyi uyum sağlar. Akış tabanlı bir mimariniz varsa (bununla ilgili daha fazla bilgi daha sonra), muhtemelen bu çerçevede mikro servisler çalıştırıyorsunuzdur. Halihazırda uyguladığınız teknikleri kullanarak mikro servislere daha bilinçli bir yaklaşımla, dağıtımlarınızdan potansiyel olarak daha fazla değer elde edebilirsiniz. Ve mikro servisler, bulut ortamları için çok uygundur; bu nedenle, bir Kubernetes kümeniz varsa ve/veya genel bulutta çalışıyorsanız, mikro servisler, bulut özelliklerinden yararlanmak için harika bir mimariyi temsil eder.

Mikro Servis Mimarisi Gereksinimler

Uygulamalarınızı ve çözümlerinizi bir mikro servis mimarisinde oluşturmak, herhangi bir özel bilgi veya deneyim gerektirmez. Stratejinin çoğu tanıdık gelecek ve muhtemelen daha önce uyguladığınız modülerlik gibi kavramlardan yararlanacak. Elbette, mikro servisler arası iletişim mimarinin önemli bir parçasıdır ve REST servisleri veri alışverişi için yaygın olarak kullanılır. Bununla birlikte, sizin için yeni olabilecek, veri alışverişi için giderek daha popüler bir tasarım modeli, olay güdümlü bir mimaride merkezi, hafif bir mesajlaşma veriyolunun kullanılmasıdır. Her bir mikro servis, daha büyük bir çözümün parçası olarak birbiriyle ilişkili olduğundan, durumunu izlemenin ve bireysel mikro servisler arasında veri aktarmanın kolay bir yolunun olması gerekir. Diğer veri alışverişi araçlarına göre mikro servislerle mesajlaşma veri yolu kullanmanın avantajları olduğundan, bu makale bu tasarım modeline odaklanacaktır. Mesajlaşma sistemi için bir seçenek Apache Kafka'dır. Apache Kafka, verileri "topics" adı verilen ayrı kanallarda depolayan bir publish/subscribe (pub/sub) mesajlaşma veriyoludur. Topic, veri öğelerini (veya "mesajları") sırayla izleyen bir veri yapısıdır. Topicler esnektir ve mesajları hemen hemen her biçimde saklayabilir. Kodlama açısından, topiclere mesaj yazmak ve okumak kolaydır. Ve bu mesajlaşma yaklaşımı mikro servisler kodundan ayrıldığından, mikro servislerin birbirleriyle doğrudan iletişim kurmasına kıyasla veri alışverişinde bulunma konusunda daha fazla esneklik elde edersiniz. Dağıtımınız için mesajlaşma sistemini oluşturmak üzere mikro servisleriniz için source (kaynak) ve destinition(hedef) olarak bir dizi topic ayarlayabilirsiniz.


Mikro servisler, verileri sonraki mikro servise geçirmek için Kafka topiclerini kullanabilir.


Her mikro servis, belirlenen kaynak topiclerinden bir veya daha fazlasından gönderilen verileri alır ve verileri işledikten sonra, çıktıyı belirlenen hedef konusuna sıralı bir şekilde yazar (veya serideki son mikro servis ise mesaj yazmaz) . Çıktı mesajı, verilen mikro servis için en anlamlı biçimde gönderilir ve daha sonra, bu mesaj biçiminin ne olduğunu ve nasıl tüketileceğini bilmek akıştaki bir sonraki mikro servise bağlıdır. Genel olarak, herhangi bir mikro servisin yalnızca (en fazla) bir topice yazması gerekir, çünkü birden çok alt akış mikro servisi aynı konudan çakışma olmadan bağımsız olarak okuyabilir. Montaj hattı benzetmesine devam etmek gerekirse, mesajlar devam eden ürünler gibidir ve her mikro servis bu ürünler üzerinde daha fazla iş yapar. Topicler, ürünleri montaj hattı istasyonları arasında taşıyan konveyör bantları gibidir. Kafka genellikle mikro servis mimarileri için kullanıldığından, "streaming architecture (akış mimarisi)" veya "streams based architecture (akış tabanlı mimari)" gibi terimlerin "mikro servis mimarisi" ile yakından ilişkili olduğu (ve bazen birbirinin yerine kullanıldığı) mantıklıdır. Bu farklı terimler, mimarinin farklı bir odağını ifade etse de, temel temel aynıdır. Mesajlaşma sistemi için başka bir seçenek, RAM ve paralelleştirme vurgusu nedeniyle verileri aşırı hızda depolamanıza ve işlemenize izin veren dağıtılmış bir bilgi işlem sistemi olan inmemory data grid'dir (IMDG). Bir IMDG, sabit sürücülere veya katı hal sürücülerine (SSD) dayanan Kafka'nın aksine, veri yapılarını bellekte depolar. Bu, bir IMDG'nin mikro servislerinizin verilere çok daha yüksek hızlarda erişmesine izin verdiği ve bu da genel çözümünüzün performansını önemli ölçüde hızlandırdığı anlamına gelir.

IMDG'ler, mikro servisler arasında mesaj iletmek için kullanabileceğiniz Kafka'ya benzer veri yapıları olarak konuları da içerir. Mapler(yani bir anahtar/değer deposu gibi davranan veri yapıları) gibi IMDG'lerde mesajlaşma için kullanılabilecek başka veri yapıları da vardır, ancak basitlik adına tartışmamız konulara odaklanacaktır. Hıza ek olarak, mikro servisler için IMDG'leri kullanmanın bir başka avantajı da, mikro servisleriniz için mesajların ötesinde veri depolamak ve almak için daha fazla veri yapısından yararlanabilmenizdir. IMDG, akış zenginleştirme için arama verileri sağlayan bir veritabanı gibi davranır ve ayrıca mikro servisleriniz için durumu izlemek için verileri depolayabilir. Bir mikro servis mimarisinde mesajlaşma katmanı olarak bir mesaj veriyolu kullanarak, mikro servisleri oluşturmak için bir akış işleme motoru (Hazelcast Jet gibi) kullanmak da mantıklıdır. Bu, bu makalenin ilerleyen kısımlarında tartışacağımız gibi, mikro servisler iş mantığını kodlamak için herhangi bir teknolojiyi desteklemek için gerçekten harika olduğundan, kullanabileceğiniz bir teknoloji örneğidir.


In-memory data grids, hızlı mesajlaşma ve veri depolama/alma sağlar.

Diğer teknolojiler, mikro servis mesajlaşması için kullanılabilir ancak ideal olmaktan uzaktır. Bazen REST servisleri olarak uygulanan dosya sistemleri ve veritabanları (RDBMS veya NoSQL) kullanılabilir, ancak bunlar yukarıda açıklanan teknolojilere göre önemli bir performans dezavantajından muzdariptir. Ayrıca, bu sistemleri bir mesajlaşma sistemi olarak kullanmak için gereken kodlama ve yönetim yükü, onları, aksi takdirde çevik odaklı bir mikro servis mimarisi için ideal olmaktan uzak kılar. Elbette, bu teknolojiler önceki nesil mikro servisler için kullanılmıştır, ancak uygulama geliştiricileri, aradıkları performans düzeylerini ve sürdürülebilirliği sağlamak için daha uygun teknolojilere olan ihtiyacı kabul etmektedir.

Neden Mikro Servisler?


Mikro servisler, bulut (private,public,hybrid,multi), bulut nesnesi depolama, Docker kapsayıcıları, Kubernetes ve Intel® Optane™ DC Kalıcı Bellek gibi RAM alternatifleri gibi popüler teknolojileri kullanan modern dağıtımlar için idealdir. Aşağıda, mikro servislerden yararlanan bazı teknik kullanım durumları verilmiştir: 
T Makine öğrenimi modellerini operasyonel hale getirme (makine öğrenimi çıkarımı) 
• ​Uçtan buluta Nesnelerin İnterneti veri analizi ve işleme 
• Sahtekarlık/anomali algılama 
• Büyük ölçekli e(xtract transform-load (ETL) T İşlem izleme ve işleme (çevrimiçi ödemeler ve e-ticaret gibi) 
Bu kullanım örnekleri arasındaki ortak tema, karşılanması gereken çok büyük miktarda veri, servis düzeyi sözleşmesi (SLA) gereksinimleri ve önemli bir kod miktarı. Bu tür kullanım durumlarını monolitik bir yaklaşımla ele almaya çalışmak yerine, mikro servislerin bir avantaj sağlamasının aşağıdaki altı nedenini göz önünde bulundurun:

1. Mikro Servislerin Oluşturulması ve Geliştirilmesi Daha Kolaydır

Mikro servisler, daha fazla odaklanmayı teşvik ederek daha yüksek kaliteli kod sağlar. Bireysel mikro servisler, tanım gereği, monolitik uygulamalardan daha küçük olduğundan, daha az kapsam ve daha az koda sahiptirler. Bu, artımlı kod güncellemeleriyle deneme ve test yapmayı çok daha kolay hale getirir. Tüm bir mikro servis tabanlı çözüm için toplam kod miktarı, işlevsel olarak karşılaştırılabilir bir monolitik uygulamaya benzer olsa da, zorunlu kod ayrımı, her bir parçanın yönetimini kolaylaştırır. Mikro servis düzeyinde daha az kod, daha az karmaşıklık, daha düşük test çalışması, daha kolay birim testi ve daha düşük sorun riski anlamına gelir. Ve tüm bu özellikler, kodu korumanın ve geliştirmenin daha kolay olduğu anlamına gelir, böylece size daha fazla çeviklik ve daha yüksek kalite sunar. Geliştirme ekibinin yeni üyelerinin her bir mikro servisin hedeflerini anlaması ve daha kolay katkıda bulunması daha kolaydır ve uygulamaların kaynak kodunun içine giremeyecek kadar karmaşık olduğu "kara kutular" haline gelme riski daha düşüktür.

Mikro servisler bağlamında düşünmek, iyi geliştirme uygulamalarını güçlendirmeye yardımcı olabilir. Çözümünüzü bir dizi küçük görev olarak tanımlayabilirseniz, her bir görevin süreçteki bir sonraki görevi nasıl etkileyeceği konusunda daha az endişe duyarak her görevi doğru yapmaya daha fazla odaklanabilirsiniz. Kafka veya Hazelcast IMDG gibi ayrıştırılmış bir mesajlaşma sisteminin de mikro servis geliştirme sürecini basitleştirmeye yardımcı olacağı yer burasıdır. Her mikro servis, akıştaki bir sonraki mikro servisin anlayabileceği kendi standartlaştırılmış biçiminde Kafka/IMDG'ye yazabilir. Mikro servislerin uyması gereken katı bir mesajlaşma formatı yoktur. Örneğin, bir mikro servisin yalnızca commadelimited tamsayılar listesi göndermesi gerekiyorsa, bunu yapabilir ve bir sonraki mikro servis, kendi işlemesini yapmak için bu biçimi okumaktan sorumludur. Bu ayrıştırma, veri hattına yeni kod eklemek kolay olduğundan, aynı ortamdaki farklı kod sürümlerini karşılaştırmak için deneyi ve A-B testini teşvik eder. Her bir mikro servisin basitliği ve modülerliği, yeniden kullanılabilirliğe de katkıda bulunur. Bir mikro servis, CSV'yi JSON'a dönüştürmek gibi genelleştirilmiş bir görevden sorumluysa, bu modül, bu dönüştürmeye ihtiyaç duyan herhangi bir mikro servis dağıtımına kolayca takılabilir. Bu, sonraki tüm mikro servis tabanlı çözümler için hızlı geliştirme ve genişlemeye yardımcı olur. Tabii ki, mikro servisin her tür CSV girdisini işlemek için yazılması gerekir, bu nedenle geliştirme sırasında genelleştirmeyi ve yeniden kullanılabilirliği aklınızda tutmanız yeterlidir; bu, sınırlı kapsamlı görevleri kodlarken yapılması daha kolay olma eğilimindedir. Bir mikro servis mimarisi mutlaka bir ya hep ya hiç çabası değildir. Bu uygulamaya daha fazla yetenek eklemek istiyorsanız, mevcut bir monolitik uygulamaya aşamalı olarak mikro servisler ekleyebilirsiniz. Ayrıca, bu belgede açıklanan tüm avantajları elde etmek için monolitik bir uygulamayı mikro servis mimarisine aşamalı olarak geçirebilirsiniz. Bu kulağa SOA gibi gelse de, temel fark, bir mikro servis mimarisinin uygulamalarınızın her birini küçük ve yönetilebilir hale getirmesi, SOA ise büyük ölçüde büyük uygulamaların birbirleriyle iletişim kurmasını sağlamasıdır. Bir SOA zihniyetiyle, verileri daha az karmaşık bir şekilde paylaşabileceksiniz, ancak yine de oluşturulması daha kolay olmayan karmaşık uygulamalarla uğraşacaksınız.

Bir mikro servis mimarisi, daha geniş bir yetenek havuzundan yararlanmanızı da sağlar, çünkü her bir mikro servis, diğerlerinden bağımsız olarak hemen hemen her programlama diline ve ortamına dayanabilir. Her bir mikro servisin nasıl oluşturulduğuna dair bir gereklilik yoktur. Her mikro servis için en uygun teknoloji yığınını seçebilirsiniz. Farklı becerilere sahip ayrı uygulama geliştirme ekipleri, teknolojiler ve beceri düzeyinde ortaklığa ihtiyaç duymadan bir mikro servis mimarisi üzerinde birlikte çalışabilir. Bu aynı zamanda gelecekte daha yeni teknolojileri mimarinize daha iyi dahil edebileceğiniz anlamına gelir. Genel olarak teknoloji standardizasyonu ile mücadele etmek yerine, yalnızca daha hızlı ve/veya daha verimli hale getirilebilecek mikro servisleri güncellemek için yeni teknolojilerden kademeli olarak yararlanabilirsiniz. Bu size, diğer kapasitelerde ne kadar iyi çalışabileceklerini görmek için yeni teknolojileri kontrollü bir şekilde deneme fırsatı verir.

2. Mikro Servislerin Dağıtımı Daha Kolay

Mikro servislerin dağıtımı, monolitik uygulamalardan daha kolaydır, çünkü daha küçüktürler ve bu nedenle daha az çevresel bağımlılığa sahiptirler. Monolitik uygulamaların dağıtımıyla ilgili yaygın bir sorun, geliştirme ortamları ile üretim ortamları arasındaki bilinmeyen tutarsızlıklardır. İşletim sistemi sürümlerine, kitaplık sürümlerine, RAM miktarına vb. bağımlılıklar vardır ve çok sayıda bağımlılığa sahip karmaşık bir uygulamanız varsa, saptaması zor dağıtım sorunlarıyla karşılaşabilirsiniz. Mikro servisleri artımlı bir şekilde dağıtırsanız, daha küçük kapsamları daha az bağımlılığa sahip oldukları anlamına geldiğinden, her bir mikro servisle karşılaştığınız sorunları izlemeniz daha kolay olacaktır.

Geliştirmeden üretime bağımlılık tutarsızlıklarının azaltılması da kapsayıcı mimarinin önemli bir avantajıdır. Bireysel mikro servislerin minimalist doğası, bunların bir kapsayıcıda ve diğer sanallaştırılmış teknolojilerle dağıtılmasını daha da kolaylaştırır ve bunu yapmak, bağımlılık çakışmaları potansiyelini daha da azaltmanıza olanak tanır. Konteyner tabanlı bir dağıtımın orkestrasyon sistemi olarak Kubernetes ile birleştirilmesi, mikro servisleri mevcut kaynaklara verimli bir şekilde tahsis ederek dağıtımı daha da basitleştirir.

Yine basit olacak şekilde tasarlandıkları için mikro servislerin çeşitli dağıtım seçeneklerinde dağıtılması kolaydır. Şirket içinde, bulutta, uçta, sanallaştırılmış ortamlarda (özellikle Docker kapsayıcılarında), sunucusuz ortamlarda, bir düğümde veya bir düğüm kümesinde devreye alınabilirler. Aslında, mikro servisler, mevcut kaynaklarınızdan yararlanmak için aynı kapsamlı uygulamaların bir parçası olarak birkaç farklı konuma dağıtılabilir.

Ayrıca, diğer mikro servislere yönelik güncellemeleri beklemeden mikro servislerin yeni sürümlerini dağıtabilirsiniz. Mesajlaşma biçiminiz olduğu gibi kaldığı sürece, mikro servislerin güncel sürümlerini sisteminizin geri kalanını olumsuz etkilemeden yeniden dağıtabilirsiniz. Yeni mikro servisinizle ilgili bir sorun ortaya çıkarsa, sistemin yeniden tam olarak çalışmasını sağlamak için önceki mikro servisi değiştirebilirsiniz.

3. Mikro Servislerin Bakımı, Sorun Gidermesi ve Genişletilmesi Daha Kolaydır

Mikro servisler, herhangi bir büyük ölçekli yazılım ortamında büyük zorluklar olan sürekli bakım ve hata toleransını daha sorunsuz bir şekilde sağlar. Monolitik bir uygulamada bir sorun ortaya çıktığında sorun giderme zordur çünkü verilerin dahili iş akışı içinde nasıl işlendiği her zaman açık değildir. Sorunun kaynağını izole etmek, tüm kod tabanının iyi anlaşılmasını gerektirir. Öte yandan, her bir mikro servisin kendi çıktıları olduğundan, hangi çıktının bir sorunu olduğunu ve dolayısıyla hangi mikro servisin ilgilenilmesi gerektiğini belirlemek daha kolaydır. Örneğin, mikro servis tabanlı bir sistemdeki son hesaplamaların yanlış olduğunu keşfederseniz, sorunlu hesaplamayı bulmak için sistemdeki verilerin kökenini, her seferinde bir mikro servis olmak üzere geriye doğru izleyebilirsiniz. Her bir mikro servisin sınırlı kapsamı ve ayrıca mikro servislerin otomatikleştirilmiş, standartlaştırılmış test senaryolarının çalıştırılmasını kolaylaştırması, bunların hatalarının ayıklanmasını ve dolayısıyla daha yüksek kaliteli kod oluşturulmasını kolaylaştırır.

Mikro servislerin birden çok bilgisayar sunucusu ve kaynağı arasında dağıtılmış yapısı, 7x24 dağıtımları etkinleştirmek için hata toleransı stratejilerine yardımcı olur. Herhangi bir mikro servis, tek bir arıza kaynağı olmaması için yedekli olarak dağıtılabilir. Her mikro servis, diğer örneklerden bağımsız olarak çalışır ve herhangi biri başarısız olursa, diğerleri boşluğu alır. Bu, ayrıştırılmış mesajlaşma katmanının değerli olduğu başka bir alandır, çünkü her bir mikro servis bağımsız bir tüketici olarak hareket eder, bu nedenle bir mikro servis hatası mesajlaşma sistemini bozmaz. Kubernetes kümesindeki Docker kapsayıcılarında mikro servisleri dağıtmak, Kubernetes bazı hata toleransı gereksinimlerini yönetebildiğinden ve arızalı mikro servisleri yeniden başlatabildiğinden bakımı da basitleştirir.

Yüksek modüler mimari nedeniyle uygulamanızda artımlı güncellemeler yapmak kolaydır ve nispeten düşük risklidir. Mevcut işlevselliği geliştirmek için güncellenmiş kodu ekleyebilir veya daha fazla çıktı sağlamak üzere sisteminizi genişletmek için yeni kod ekleyebilirsiniz. Mesajlaşma katmanının ayrıştırılması, yeni güncellemelerin sorunlu olduğu tespit edilirse veri kaybı (veya diğer önemli arızalar) riskini azaltır. Uygulamanızda kapsamlı değişiklikler yapmanız gerekse bile, bunu monolitik bir uygulamaya kıyasla çok daha az riskle yapabilirsiniz. Örneğin, güncellenmiş bir mikro serviste mesajlaşma biçiminizi değiştirirseniz, konuları ayırmak için hem eski biçimi hem de yeni biçimi aynı anda gönderebilirsiniz. Sıradaki mevcut mikro servisler, orijinal konudaki eski formatı kullanmaya devam edecek ve bu arada, yeni eklenen konudan yeni formattan yararlanacak güncellenmiş bir mikro servis yazabilirsiniz. Bu, sorunlara karşı ekstra bir savunma önlemi olarak eski kodu yeni kodun yanında çalıştırmanıza olanak tanır.

Ve daha sonra öngörülemeyen bir hata meydana gelirse, veri akışını geçici olarak durduracak olan belirli bir görevi kapatabilirsiniz, ancak mesajlar mesajlaşma katmanında sıraya alınır ve söz konusu görev geri yüklendiğinde okunacaktır. Mesaj yolu, tüm mikro servisler tekrar çalışır hale gelene kadar tüm verilerin korunmasını sağlar.

4. Mikro Servisler Ekipler Arası Koordinasyonu Basitleştirir

Herhangi bir büyük ölçekli yazılım geliştirme çabasının zorluklarından biri, entegrasyon noktalarını aşırı karmaşık hale getirme riskidir. Bir mikro serviste dahili iş akışı, bir kaynaktan veri okumak, veriler üzerinde bir eylem gerçekleştirmek ve ardından çıktıları bir hedefe göndermek kadar basit olabilir. SOA'da olduğu gibi hangi verilerin hangi entegrasyon noktalarında paylaşılacağı konusunda kapsamlı bir planlamaya gerek yoktur. Mikro servisler tipik olarak bir seferde küçük miktarlardaki verilerle ilgilenir, bu nedenle veriler işlendikten sonra çıktıyı yönetmek ve paylaşmak daha kolaydır. İşleme ve veri kapsamındaki bu basitlik, aktarımda basitliğe yol açar.

Bu basitleştirmeyi desteklemek için mikro servisler, mikro servislerin iletişim kurmasını sağlamak için hafif bir mesajlaşma sisteminden yararlanarak geliştirme ekipleri arasındaki koordinasyonu basitleştirmelidir. Her mikro servis, istediğiniz biçimde mesaj/veri gönderebilir, bu da sizi doğal olarak mümkün olan en basit biçimi benimsemeye teşvik eder. Gelecekteki iletişimi ironik bir şekilde karmaşıklaştırma eğiliminde olan evrensel bir mesajlaşma standardı etrafında bir gereklilik yoktur. Her bir biçim belgelendikten sonra, ardışık düzendeki bir sonraki mikro servisin geliştirme ekibi mesajları kabul etmek için bu biçimi kullanabilir. Ve her bir mikro servis, veriler üzerinde sınırlı bir görevi yerine getirdiğinden, karmaşık çıktılara gerek yoktur. Kapsamlı bir mesajın paylaşılması gerekmediğinden, yıllar öncesinin karmaşık mesajlaşma biçimlerine ihtiyaç yoktur. Mesajlaşmadaki bu basitlik, bilgi türü değiştiğinde yardımcı olur. Biçimdeki güncellemeler büyük olasılıkla basit olacak ve böylece bir sonraki mikro servisin bu güncellemeleri barındırmasını kolaylaştıracak.

Bu hafif mesajlaşma modeli, iletişim topolojisini aşırı derecede karmaşık hale getirmekten kaçınmaya yardımcı olur. Tasarımlarınızın ekibinizin iletişim yapılarının kopyaları haline geleceğini belirten Conway Yasasında ifade edilenler gibi riskler hala mevcuttur, ancak yalnızca tek tip bir zihniyete sahip olursanız. Örneğin, her geliştiricinin kendi modüllerinin diğerleriyle entegre olması gerektiğine inandığı tek bir büyük ekip oluşturursanız, baştan sona birçok entegrasyon, API ve veri yapısı içeren karmaşık bir mimariyle sonuçlanırsınız. Öte yandan, bir mimar bir mikro servis mimarisinin bileşenlerini tanımlayabilir ve ardından ekip liderlerinin mimariyi yansıtacak ekip yapılarını tanımlamasını sağlayabilirse, sistemin iletişimini uygulama çabasını basitleştireceksiniz.

5. Mikro Servisler Performans ve Ölçek Sağlar

Mikro servislerin dağıtılmış mimarisi, performansı artırmak ve ölçeği genişletmek için fırsatlar sunar. Her mikro servisin hata toleransı için yedekli olarak çalıştırılabilmesi gibi, bu yedeklilik de performans ve ölçek ekleyen daha fazla paralellik sağlar. Daha fazla mikro servis örneği, ilgili görevlere ayrılan daha fazla bilgi işlem kaynağı anlamına gelir. Aynı zamanda, bu yaklaşım daha büyük bir ölçek sağlar, böylece artan paralellik yoluyla daha fazla veri işlenebilir. Bu, esasen, daha fazla CPU gücü ve RAM'den yararlanmak için işi daha fazla mikro servise yaydığınız, verilerinizi işlemeye yönelik bir böl ve yönet yaklaşımıdır. İşlem hattında bir darboğaz ortaya çıktığında, yük dengeleme sağlamak için daha fazla bulut sunucusu (manuel olarak veya Kubernetes ile kapsayıcılı bir ortamda) dağıtılabilir. Yine, ayrılmış mesajlaşma katmanı, ek mikro servislerin kolay artımlı dağıtımlarına yardımcı olur.

Performans ve ölçek ekleme yeteneği, yalnızca SLA'ları gelecekteki büyümeyi göz önünde bulundurarak karşılamakla ilgili değildir. Aynı zamanda, yeni yetenekleri denemek için fazladan boşluk payına sahip olmakla da ilgilidir. Bu, denemenin dağıtım yaşam döngüsünün doğasında olduğu üretim makine öğrenimi dağıtımlarında özellikle kritiktir. Bir makine öğrenimi modeli nadiren yeterince iyi kabul edilir ve doğruluğu ancak modeller canlı veriler üzerinde çalıştırılarak tam olarak değerlendirilebilir. Deneme yeteneğiyle, aynı anda iyileştirme için yeni fırsatları keşfederken, günlük operasyonlarınızın güvenilir bir şekilde çalışmasını sağlayabilirsiniz.

Elbette performans ve ölçek avantajları yalnızca mikro servis dağıtımının özel olarak bir parçası olan kod için geçerlidir. Başka bir deyişle, ardışık düzenin parçası olarak adlandırılan uzak, üçüncü taraf API gibi harici kaynaklar varsa, mikro servis mimarisinin size orada yardımcı olması gerekmez. Yedekli mikro servislerinizin bunu paralel bir şekilde kullanabilmesi için harici kaynağın da ölçeklenebildiğinden emin olmanız gerekir.

6. Mikro Servisler Gerçek Zamanlı İşlemeyi Basitleştirir

Aciliyet talebi artmaya devam ediyor ve işletmelerin rekabet avantajını korumak için eskisinden daha hızlı yanıt vermesi gerekiyor. Bu nedenle akış mimarileri ve gerçek zamanlı işleme bugün popüler konulardır ve bunlar mikro servislerle uyumludur.

Gerçek zamanlı işleme, performans, ölçek, güvenilirlik ve sürdürülebilirlik ile ilgili birçok zorluğu beraberinde getirir ve mikro servisler yaklaşımı bu zorluğun hafifletilmesine yardımcı olabilir. Gerçek zamanlı işleme, genellikle bir dizi Nesnenin İnterneti cihazından gelen gibi sürekli bir veri akışıyla başlar. Gelen verilerin genellikle zenginleştirme, toplama ve filtreleme gibi çeşitli işleme görevlerinden geçmesi gerekir. Bu adımların her biri, verileri diğerine aktaran net bir görev ayrımı oluşturmak için bir mikro servis tarafından yapılabilir. Mesajlaşma katmanı, bu gerçek zamanlı akışı kolaylaştırmaya yardımcı olur ve gelen akış verileri akışıyla mükemmel şekilde hizalanır.

Bazı durumlarda, işlem, tipik olarak daha küçük ayak izi donanımı gerektiren sınırlı fiziksel alanın olduğu uçta hemen yapılmalıdır. Bu donanım sistemleri, veri merkezlerindeki veya buluttaki sunuculardan daha az güçlü olma eğiliminde olduğundan, azaltılmış bilgi işlem gücüyle çalışmak için hafif, verimli yazılım paketleri gereklidir. Hazelcast Jet gibi teknolojiler, tüm verileri merkezi bir veri merkezine aktarmaya çalışmak yerine bazı işlemleri uç bilgisayarlara boşaltmanıza izin vermek için uçta dahil olmak üzere çok çeşitli dağıtımlar için tasarlanmıştır. Bu dağıtım modeli, temel olarak, bazı görevlerin veri kaynağına yakın bir yerde gerçekleştirildiği ve daha sonra, daha yüksek güçlü bilgisayarlarda ek işlemler gerçekleştirmek için verilerin buluta veya başka bir veri merkezine teslim edildiği, yaygın olarak dağıtılmış bir mikro servis mimarisidir.

Sonuç

Bu yazıda tartışıldığı gibi, bir mikro servis mimarisini benimsemek için birkaç iyi neden vardır. Artık her zamankinden daha fazla veriyle uğraştığımıza göre, bu verilerin nasıl ele alınacağı konusunda daha stratejik düşünmek bugün önemli bir öncelik. Bugün yeni teknoloji yeniliklerini araştırmak, mikro servislerle yolculuğunuza daha fazla yardımcı olacaktır.

Yenilik, bizi yeni bir donanım performansı düzeyine getiren Intel Optane teknolojisinden daha önce bahsettiğimiz gibi, yazılım teknolojileriyle sınırlı değildir. Bellek içi RAM'den yararlanmak geçmişte pahalı bir teklifti, bu nedenle Optane ile bellek içi hızlara ekonomik olarak daha erişilebilir. Geçici bellek modunda, Optane karşılaştırılabilir hızlarda (iş yüklerine ve veri modellerine bağlı olarak) ancak yaklaşık yarı maliyetle RAM gibi davranır. Bu, daha fazla işletmeyi uygulama performansını hızlandırmak için bellek içi teknolojilere yönelmeye teşvik eder. Tüm bellek içi teknolojiler, kullanıma hazır Optane'den yararlanamaz, ancak Optane'i (diğer Intel teknolojileriyle birlikte) çok daha yüksek için kullanmak üzere onaylanan Hazelcast teknoloji paketini (Hazelcast IMDG ve Hazelcast Jet) göz önünde bulundurmalısınız. (disk veya SSD tabanlı sistemlerinize kıyasla hızlı işleme.)

Mikro Servisler için İlgili Teknolojiler

Bu makalede tartışıldığı gibi, mikro servis mimarileri bellek içi, bulut ve akış teknolojilerinden yararlanabilir. Hazelcast, IBM ve Intel, verilerle ilgili günümüzün zorlu gereksinimlerini karşılayan çözümler sunmak için birlikte çalışıyor. Özellikle bulutta yerel ortamlarda performans, ölçek, güvenlik, güvenilirlik ve çeviklik, başarılı, iş açısından kritik sistemleri devreye almanın temel bileşenleridir. Hazelcast'in bellek içi bilgi işlem platformu, Intel'in yenilikçi donanımı ve IBM'in uçtan buluta kurumsal çaptaki çözümleri ile veriye dayalı işletmeler, mevcut ve gelecekteki dijital stratejileri için sağlam teknoloji seçeneklerine sahipler.

Hazelcast Bellek İçi Bilgi İşlem Platformu

Hazelcast, Global 2000 kuruluşlarına zamana duyarlı, bulutta yerel uygulamalar için ultra yüksek performans sağlayan sektör lideri bellek içi bilgi işlem platformunu sunar.

Hazelcast Bellek İçi Bilgi İşlem Platformu, en yaygın olarak kullanılan bellek içi veri grid olan Hazelcast IMDG ve endüstrinin en gelişmiş bellek içi akış işleme çözümü olan Hazelcast Jet'ten oluşur. Bu teknoloji, bilgi işlem içgörülerini daha hızlı elde etmenize, eylemleri daha kısa sürelerde etkinleştirmenize ve yeni verilerle gelme hızında etkileşime geçmenize olanak sağlamak için benzersiz bir şekilde tasarlanmıştır. Ek olarak, dağıtılmış bir önbelleğe alma mimarisi, yüzlerce terabayta kadar ölçeklendirmenize ve uzak veri veya uç işleme ile uğraşırken maksimum verimlilik için ölçeği genişletmenize olanak tanır.

Aşırı ölçekte ultra hızlı işleme için tasarlanan Hazelcast'in bulut yerel bellek içi veri ızgarası ve olay akışı işleme teknolojileri, JPMorgan Chase, Charter Communications, Ellie Mae, UBS ve National Australia Bank gibi önde gelen şirketler tarafından işi hızlandırmak için güveniliyor. kritik uygulamalar Dünyanın en büyük e-ticaret siteleri, Kara Cuma, Siber Pazartesi veya Bekarlar Günü ile ilişkili büyük hacim artışlarını desteklemek için milisaniyeden kısa yanıt süreleri için Hazelcast Platformuna güveniyor.

Intel® Optane™ DC Kalıcı Bellek

Performans gereksinimlerinin çoğu bellek içi işlemeye bağlı olduğundan, ortaya çıkan en büyük engel rastgele erişimli belleğin (RAM) maliyetidir. Çoğu durumda, RAM ağırlıklı donanım sunucularına yapılan yatırım haklıdır ve RAM fiyatları düşmeye devam ettikçe, bellek içi işleme kullanımı daha erişilebilir hale gelir.

Son yenilikler, bellek içi işlemenin benimsenmesini daha da pratik hale getiriyor. Intel Optane DC Kalıcı Bellek teknolojisi, bellek içi işlemenin daha uygun maliyetli olabileceği iki yol sunar. İlk yol, Optane yongalarının RAM'e alternatif olarak hareket ettiği ve neredeyse aynı hızda ancak çok daha düşük maliyet ve çok daha yüksek kapasitelerde çalıştığı geçici bellek modundadır. Bu, işletmelerin bellek içi teknolojileri daha kolay gerekçelendirmesine ve böylece bellek içi işlemenin sunduğu performans avantajlarından yararlanmasına olanak tanır.

Optane'in bellek içi teknolojileri desteklediği ikinci yol kalıcı moddur. Bu modda Optane, katı hal sürücülerine (SSD'ler) daha hızlı bir alternatif olarak kullanılabilir. Örneğin, Hazelcast, bellekteki verilerin kalıcı bellekte kalıcı olduğu bir çalışırken yeniden başlatma özelliği sağlar, böylece bir düğüm geçici olarak çökerse, etkin yeniden başlatma deposundan veri okuyarak hızla geri yüklenebilir. Çalışırken yeniden başlatma verileri kalıcılık modunda Optane'de depolanırsa, bu düğümün kurtarılması SSD'lerin kullanılmasından 3,5 kata kadar daha hızlı olabilir.

IBM Cloud Paks ve Edge Application Manager

Daha fazla işletme genel, özel, çoklu veya hibrit bulut etrafında bulut stratejileri izledikçe, doğru temel yazılım bu yolculukta yardımcı olacaktır. Bulut dağıtımları, uçta bilgi işlem içeren ve sistem dağıtımını daha karmaşık hale getiren genel bir dağıtılmış bilgi işlem sisteminin yalnızca bir parçasıdır.

IBM, işletmelerin dağıtılmış verilerinden daha fazla değer elde etmelerine yardımcı olmak için uçtan buluta zorlukları çözme konusunda önemli girişimlere sahiptir. Altı Bulut Pak'ı (Uygulamalar, Veriler, Entegrasyon, Otomasyon, Çoklu Bulut Yönetimi, Güvenlik) ile IBM, kurumsal kullanıma hazır, bulut tabanlı bir yazılım yığını aracılığıyla temel iş uygulamalarını herhangi bir bulut ortamına taşımak için daha hızlı ve daha güvenli bir yol sağlar. Red Hat OpenShift üzerinde Kubernetes ile oluşturulan Cloud Paks, müşterilere dijital dönüşüm stratejilerini yönlendirmek için esneklik ve çeviklik sağlar. Hazelcast, Pak'ta dağıtılan tüm uygulamalara bulutta yerel bir çerçevede bellek içi hızlar sağlamak için Cloud Paks'e dahil edilmiştir.

IBM Edge Application Manager (EAM), uçta bilgi işlemi etkinleştirerek iş verilerinin erişimini genişletir. Uç bilgi işlem tipik olarak sınırlı fiziksel alana ve dolayısıyla sınırlı bilgi işlem gücüne sahip uzak, erişilmesi zor konumlar gerektirdiğinden, başarılı bir dağıtım için en önemli faktörlerden bazıları verimlilik, güvenilirlik ve güvenliktir. IBM EAM ile birlikte Hazelcast, uç bilgi işlemi etkinleştirmek ve işletmelerin verileri oluşturuldukları yerde işlemesine, analiz etmesine, toplamasına ve/veya filtrelemesine izin vermek için taşınabilir ve hafif ancak güçlü bellek içi ve akış işleme teknolojileri sağlar.








Irregular Words


Same at Three Tenses:

  • cost - cost - cost
The new coat costs $45. 

The new coat cost $45 last week. 

The coat has cost $45 since last month.
  • cut - cut - cut
The cook usually cuts the meat. 

The cook cut the meat one hour ago. 

The cook has just cut the meat into small pieces.
  • hit - hit - hit
  • hurt - hurt- hurt
  • let- let - let
  • put - put - put
  • read - read - read
  • set - set - set
  • shut - shut -shut



Same at present and past tense

  • beat - beat - beaten


Same at present and perfect tense

  • become became become
Many people become U.S. citizens every year. 
My sister became a U.S. citizen last month. 
My sister has just become a U.S. citizen.
  • come came come
Fadumo comes to class early every morning. 

Fadumo came to class early yesterday morning. 

Fadumo has come to class early since September.
  • run ran run


    Same at past and perfect tense

    • bring brought brought
    We bring our books to class every night. 

    We brought our books to class last night. 

    We have brought our books to class since last fall.
    • build built built
    • burn burnt/burned burnt / burned
    • buy bought bought
    They buy a new car every summer. 

    They bought a new car last summer. 

    They have just bought a new car.
    • catch caught caught
    The boy always catches the ball. 

    The boy caught the ball yesterday. 

    The boy has caught the ball for three hours

    • deal dealt dealt
    • dig dug dug
    • dream dreamt / dreamed dreamt / dreamed
    • feed fed fed 
    • feel felt felt
    • fight fought fought
    • find found found
    • get got got / gotten 
    • have had had 
    • hear heard heard
    • hold held held
    • keep kept kept
    • lead led led
    • learn learnt / learned learnt / learned
    • leave left left 
    • lend lent lent
    • light lit lit 
    • lose lost lost
    • make made made
    • mean meant meant
    • meet met met
    • pay paid paid
    • say said said
    • sell sold sold 
    • send sent sent
    • shine shone shone
    • shoot shot shot
    • sit sat sat 
    • sleep slept slept 
    • smell smelt/smelled smelt / smelled
    • spend spent spent
    • spill spilt / spilled spilt / spilled 
    • stand stood stood 
    • sting stung stung
    • teach taught taught 
    • tell told told
    • think thought thought
    • understand understood understood 
    • win won won  


    Different at three tenses

    • be was, were been
    I am in class now. 
    I was in class yesterday. 
    I have been in class since 6:30pm.
    • begin began begun
    We begin class at 8:30am every day. 

    We began class at 8:30am yesterday 

    We have just begun class. 

    • bite bit bitten 
    Some dogs bite people 

    A dog bit a man yesterday. 

    Those dogs have bitten people a few times.
    • blow blew blown 
    The dishwasher usually breaks a couple of glasses. 

    The dishwasher broke a couple of glasses today. 

    The dishwasher has broken three glasses this week.
    • break broke broken
    • choose chose chosen
    You choose to take ESL classes. 

    You chose to take classes at this school last month. 

    You have already chosen to take classes at this school.
    • do did done 
    You do your laundry every weekend. 

    You did your laundry last weekend. 

    You have done your laundry every weekend since you moved to Minnesota
    • draw drew drawn 
    The young girl draws a picture in class 

    The young girl drew a beautiful picture last week. 

    The young girl has drawn many pictures.
    • drink drank drunk 
    I drink orange juice for breakfast every morning. 

    I drank orange juice for breakfast yesterday. 

    I have drunk orange juice for many years.
    • drive drove driven 
    • eat ate eaten 
    • fall fell fallen 
    • fly flew flown 
    • forget forgot forgotten 
    • forgive forgave forgiven 
    • freeze froze frozen 
    • get got got / gotten 
    • give gave given 
    • go went gone / been 
    • grow grew grown
    • hide hid hidden
    • know knew known
    • lie lay lain
    • ride rode ridden 
    • ring rang rung 
    • rise rose risen
    • see saw seen
    • shake shook shaken
    • show showed shown
    • sing sang sung 
    • speak spoke spoken 
    • steal stole stolen
    • swim swam swum 
    • take took taken 
    • tear tore torn
    • throw threw thrown
    • wake woke woken 
    • wear wore worn 
    • write wrote written

    3- Domain-Driven Design ve Hexagonal(Altıgen) Mimari - Petter Holmström - Çevirsi

    "Bu makale dizisinde, Domain Driven Desgin (etki alnına dayalı tasarım)'ın ne olduğunu ve projenize - veya bir kısmının - projelerinize nasıl uygulanacağını öğreneceksiniz." diyor Petter Holmström. Ben de elimden geldiğince bu yazı dizisini Türkçe'ye çevirmeye çalışacağım. Umarım İngilizce okumada zorluk çeken arkadaşlar için yararlı olur.

    Yazı Dizisinin Orjinali
    Örnek DDD projesi

    Serinin diğer yazıları :
    1 - Strategic Domain Driven Design (Stratejik DDD)
    2 - Tactical Domain Driven Design (Taktiksel DDD)

    Önceki iki makalede, stratejik ve taktiksel alan odaklı tasarım hakkında bilgi sahibi olduk. Şimdi bir domain modelini çalışan yazılıma nasıl dönüştüreceğinizi, daha spesifik olarak bunun altıgen mimariyi kullanarak nasıl yapılacağını öğrenmenin zamanı geldi.

    Önceki iki makale, kod örnekleri Java ile yazılmış olsa da oldukça geneldi. Bu makaledeki pek çok teori diğer ortamlarda ve dillerde de uygulanabilir olsa da, bunu açıkça Java ve Vaadin ile yazdım.

    Yine içerik, Eric Evans'ın Domain-Driven Design: Tackling Complexity in the Heart of Software and Implementing Domain-Driven Design by Vaughn Vernon kitaplarına dayanıyor ve ikisini de okumanızı şiddetle tavsiye ediyorum. Ancak önceki makalelerde de kendi düşüncelerimi, fikirlerimi ve deneyimlerimi sunmuş olsam da, bu daha da güçlü bir şekilde düşündüklerim ve inandıklarımla renkleniyor. Bununla birlikte, beni DDD ile başlatan şey Evans ve Vernon'un kitaplarıydı ve burada yazdıklarımın kitaplarda bulacaklarınızdan çok da uzak olmadığını düşünmek istiyorum.

    Bu, bu makalenin ikinci versiyonu. İlkinde, port kavramını yanlış anlamıştım. Bu, minnettar olduğum bir okuyucu tarafından yapılan bir yorumda belirtildi. Şimdi bu hatayı düzelttim ve örnekleri ve diyagramları buna göre güncelledim. Bu mimari tarz ve DDD hakkındaki yorumlarım hakkındaki yorumlar her zaman memnuniyetle karşılanacaktır.


    Neden Altıgen Deniyor?


    Altıgen mimari adı, bu mimarinin genellikle tasvir edilme biçiminden gelir:




    Bu makalenin ilerleyen bölümlerinde neden altıgenlerin kullanıldığına döneceğiz. Bu mimari aynı zamanda portlar ve adaptörler (arkasındaki ana fikri daha iyi açıklayan) ve onion(soğan) mimarisi (nasıl katmanlı olduğu için) adlarına da giriyor.

    Aşağıda "soğana" daha yakından bakacağız. Çekirdekle başlayacağız - domain modeli - ve sonra kendimizi, her seferinde bir katman olarak, adaptörlere ve onlarla etkileşime giren sistemlere ve istemcilere ulaşana kadar çalışacağız.

    Altıgen ve Geleneksel Katmanlar


    Altıgen mimarinin derinliklerine indiğimizde, daha geleneksel katmanlı mimariye birkaç benzerliği olduğunu göreceksiniz. Aslında, altıgen mimariyi katmanlı mimarinin bir evrimi olarak düşünebilirsiniz. Bununla birlikte, özellikle bir sistemin dış dünya ile nasıl etkileşime girdiğine dair bazı farklılıklar vardır. Bu farklılıkları daha iyi anlamak için katmanlı mimarinin bir özetiyle başlayalım:




    İlke, sistemin birbiri üzerine yığılmış katmanlardan oluşmasıdır. Daha yüksek bir katman, daha düşük bir katmanla etkileşime girebilir ancak tersi olamaz. Tipik olarak, domaine dayalı katmanlı bir mimaride, en üstte UI katmanına sahip olursunuz. Bu katman da, bir domain katmanında yaşayan domain modeliyle etkileşime giren bir application servis katmanıyla etkileşime girer. En altta, veritabanı gibi harici sistemlerle iletişim kuran bir altyapı katmanımız var.

    Altıgen sistemde, uygulama katmanının ve domain katmanının hemen hemen aynı olduğunu göreceksiniz. Bununla birlikte, UI katmanı ve altyapı katmanı çok farklı bir şekilde ele alınır. Nasıl olduğunu öğrenmek için okumaya devam edin.

    Etki Alanı Modeli (Domain Model)

    Altıgen mimarinin tam merkezinde, önceki makalede ele aldığımız taktik DDD yapı taşlarını kullanarak uygulanan alan modeli yatıyor. Tüm iş kararlarının alındığı, iş mantığının yaşadığı yer burasıdır. Bu aynı zamanda yazılımın en az değişeceğini umduğumuz en kararlı parçasıdır (tabii işin kendisi değişmedikçe).

    Alan modeli, bu dizinin önceki iki makalesinin konusu olmuştur, bu yüzden artık burada ele almayacağız. Bununla birlikte, domain modeli tek başına, onunla etkileşim kurmanın bir yolu yoksa herhangi bir değer sağlamaz. Bunu yapmak için, "soğan" da bir sonraki katmana geçmemiz gerekiyor.

    Uygulama Hizmetleri(Application Services)


    Bir application servis, müşterilerin alan modeliyle etkileşime gireceği bir cephe görevi görür. Applicaiton servisleri aşağıdaki özelliklere sahiptir:

    Durum bilgisi turmazlar (stateless)

    Sistem güvenliğini uygularlar

    Veritabanı işlemlerini kontrol ederler

    İş operasyonlarını düzenlerler ancak herhangi bir iş kararı almazlar (yani herhangi bir iş mantığı içermezler)

    Bunun ne anlama geldiğine daha yakından bakalım.

    Altıgen ve Entity-Kontrol-Sınırı(Hexagonal vs. Entity-Control-Boundary)

    Entity-Kontrol-Sınır modelini daha önce duyduysanız, altıgen mimariyi tanıdık bulacaksınız. Aggregate'lerinizi entity'ler(entities), domain servisleri , factory'leri ve repository'leri controller'lar(controllers) olarak ve applicaiton servislerini sınırlar(boundries) olarak düşünebilirsiniz.

    Durum Bilgisizlik (Statelessness)


    Bir application servis, istemcilerle etkileşime girerek değiştirilebilecek herhangi bir iç durumu korumaz. Bir işlemi gerçekleştirmek için gereken tüm bilgiler, applicaiton servis yönteminin girdi parametreleri olarak mevcut olmalıdır. Bu, sistemi daha basit, hata ayıklamayı ve ölçeklendirmeyi kolaylaştıracaktır.

    Kendinizi tek bir iş süreci bağlamında birden çok application servis çağrısı yapmanız gereken bir durumda bulursanız, iş sürecini kendi sınıfında modelleyebilir ve uygulamanın servis metoduna bir girdi parametresi olarak bir örneğini iletebilirsini. Metod daha sonra sihrini yerine getirir ve diğer application servis yöntemlerine girdi olarak kullanılabilen iş süreci nesnesinin güncellenmiş bir örneğini döndürür:

    Girdi Argümanı Olarak İş Süreci.

    public class MyBusinessProcess {
        // Current process state
    }
    
    public interface MyApplicationService {
    
        MyBusinessProcess performSomeStuff(MyBusinessProcess input);
    
        MyBusinessProcess performSomeMoreStuff(MyBusinessProcess input);
    }
    
    

    Ayrıca, iş süreci nesnesini mutable hale getirebilir ve application servis yönteminin(applicaiton service) nesnenin durumunu doğrudan değiştirmesine izin verebilirsiniz. Kişisel olarak bu yaklaşımı tercih etmiyorum çünkü istenmeyen yan etkilere yol açabileceğine inanıyorum, özellikle transaction rollback yapıldığında. Bu, application servisinin istemci tarafından nasıl çağrıldığına bağlıdır ve bu konuya daha sonra bağlantı noktaları(ports) ve bağdaştırıcılar(adaptors) hakkındaki bölümde bahsedilecektir.

    Daha karmaşık ve uzun süreli iş süreçlerinin nasıl uygulanacağına dair ipuçları için Vernon'un kitabını okumanızı tavsiye ederim.

    Güvenlik yaptırımı(Security Enforcement)


    Application servis, mevcut kullanıcının söz konusu işlemi gerçekleştirmesine izin verilmesini sağlar. Teknik olarak, bunu her bir application servis yönteminin üst kısmında manuel olarak yapabilir veya AOP gibi daha karmaşık bir şey kullanabilirsiniz. Domain modeli içinde değil, applicaiton servis katmanında olduğu sürece güvenliğin nasıl uygulandığı önemli değildir. 

    Şimdi, bu neden önemli?

    Bir uygulamada güvenlik hakkında konuştuğumuzda, yetkili erişime izin vermektense yetkisiz erişimi önlemeye daha fazla vurgu yapma eğilimindeyiz. Bu nedenle, sisteme eklediğimiz herhangi bir güvenlik kontrolü, esasen kullanımını zorlaştıracaktır. Bu güvenlik kontrollerini domain modeline eklersek kendimizi güvenlik kontrolleri eklendiğinde aklımıza gelmediği için önemli bir işlemi yapamayacağımız bir durumda bulabiliriz. Tüm güvenlik kontrollerini domain modelinin dışında tutarak, domain modeli ile istediğimiz şekilde etkileşim kurabildiğimiz için daha esnek bir sistem elde ediyoruz. Tüm istemcilerin zaten bir application servisinden geçmesi gerektiğinden sistem yine de güvenli olacaktır. Yeni bir application servisi oluşturmak, domain modelini değiştirmekten çok daha kolaydır.

    Kod Örnekleri


    Aşağıda, bir application servisinde güvenlik uygulamasının nasıl görünebileceğine dair iki Java örneği verilmiştir. Kod test edilmemiştir ve gerçek Java kodundan daha çok sözde kod olarak ele alınmalıdır.

    Bildirime Dayalı(Declarative) Güvenlik Uygulaması


    @Service
    class MyApplicationService {
    
        @Secured("ROLE_BUSINESS_PROCESSOR") // 
        public MyBusinessProcess performSomeStuff(MyBusinessProcess input) {
            var customer = customerRepository.findById(input.getCustomerId()) // 
                .orElseThrow( () -> new CustomerNotFoundException(input.getCustomerId()));
            var someResult = myDomainService.performABusinessOperation(customer); // 
            customer = customerRepository.save(customer);
            return input.updateMyBusinessProcessWithResult(someResult); // 
        }
    }
    
    

    1. Anatasyon, framework'e yalnızca ROLE_BUSINESS_PROCESSOR rolüne sahip kimliği doğrulanmış kullanıcıların yöntemi çağırmasına izin vermesi talimatını verir.
    2. Application servis(applicaiton service), domain modelindeki bir repodan bir domain model arar.
    3. Application servis, aggregate'i domain modelindeki bir domain servisine(domain service) geçirerek sonucu (ne olursa olsun) depolar.
    4. Application servisi, iş süreci nesnesini güncellemek için domain servisinin sonucunu kullanır ve aynı uzun süreli işleme katılan diğer  applicaiton servis yöntemlerine aktarılabilmesi için onu döndürür.

    Manuel Güvenlik Uygulaması


    @Service
    class MyApplicationService {
    
        public MyBusinessProcess performSomeStuff(MyBusinessProcess input) {
            // We assume SecurityContext is a thread-local class that contains information
            // about the current user.
            if (!SecurityContext.isLoggedOn()) { // 
                throw new AuthenticationException("No user logged on");
            }
            if (!SecurityContext.holdsRole("ROLE_BUSINESS_PROCESSOR")) { // 
                throw new AccessDeniedException("Insufficient privileges");
            }
    
            var customer = customerRepository.findById(input.getCustomerId())
                .orElseThrow( () -> new CustomerNotFoundException(input.getCustomerId()));
            var someResult = myDomainService.performABusinessOperation(customer);
            customer = customerRepository.save(customer);
            return input.updateMyBusinessProcessWithResult(someResult);
        }
    }
    
    

    1. Gerçek bir uygulamada, muhtemelen bir kullanıcı oturum açmamışsa istisnayı atan yardımcı yöntemler oluşturursunuz. Neyin kontrol edilmesi gerektiğini göstermek için bu örneğe yalnızca daha ayrıntılı bir versiyon ekledim.
    2. Önceki durumda olduğu gibi, yalnızca ROLE_BUSINESS_PROCESSOR rolüne sahip kullanıcıların yöntemi çağırmasına izin verilir.

    Transaction yönetimi (Transaction Management)


    Her  applicaiton servis yöntemi, temeldeki veri deposunun işlemleri kullanıp kullanmadığına bakılmaksızın, kendi başına tek bir işlem oluşturacak şekilde tasarlanmalıdır. Bir application servis yöntemi başarılı olursa, işlemi tersine çeviren başka bir application servisini açıkça çağırmak dışında bunu geri almanın bir yolu yoktur (böyle bir yöntem varsa bile).

    Kendinizi aynı işlem içinde birden çok application servisi yöntemini çağırmak istediğiniz bir durumda bulursanız, application servisinizin ayrıntı düzeyinin doğru olup olmadığını kontrol etmelisiniz. Belki de application servisinizin yaptığı bazı şeyler aslında bunun yerine domain servislerde olmalıdır? Sisteminizi güçlü tutarlılık yerine nihai tutarlılığı kullanacak şekilde yeniden tasarlamayı da düşünmeniz gerekebilir (bununla ilgili daha fazla bilgi için lütfen taktik domaine dayalı tasarım hakkındaki önceki makaleye bakın).

    Teknik olarak, işlemleri application servisi yöntemi içerisinde manuel olarak halledebilir veya Spring ve Java EE gibi frameworkler ve platformlar tarafından sunulan anatasyonları kullanabilirsiniz.

    Kod Örnekleri:


    İşte bir application servisindeki işlem yönetiminin nasıl görünebileceğine dair iki Java örneği. Kod test edilmemiştir ve gerçek Java kodundan daha çok sözde kod olarak ele alınmalıdır.

    Bildirime Dayalı(Declarative) Transaction Yönetimi


    @Service
    class UserAdministrationService {
    
        @Transactional // 
        public void resetPassword(UserId userId) {
            var user = userRepository.findByUserId(userId); // 
            user.resetPassword(); // 
            userRepository.save(user);
        }
    }
    
    

    1. Framework, tüm yöntemin tek bir işlem içinde çalıştığından emin olacaktır. Bir istisna atılırsa, işlem geri alınır. Aksi takdirde, yöntem geri döndüğünde kesinleşir.
    2. Application servisi, Kullanıcı aggregate kökünü bulmak için domain modelinde bir repository çağırır.
    3. Application servisi, Kullanıcı aggreagte kökünde bir iş yöntemini çağırır.


    Manuel Transaction Management





    @Service
    class UserAdministrationService {
    
        @Transactional
        public void resetPassword(UserId userId) {
            var tx = transactionManager.begin(); // 
            try {
                var user = userRepository.findByUserId(userId);
                user.resetPassword();
                userRepository.save(user);
                tx.commit(); // 
            } catch (RuntimeException ex) {
                tx.rollback(); // 
                throw ex;
            }
        }
    }
    
    1. Tranaction  yöneticisi application servisine enjekte edilmiştir, böylece servis yöntemi açıkça yeni bir işlem başlatabilir.
    2. Her şey çalışırsa, işlem parola sıfırlandıktan sonra gerçekleştirilir.
    3. Bir hata oluşursa, işlem geri alınır ve istisna yeniden oluşturulur.

    Orkestrasyon

    İyi bir application servisi tasarlamanın belki de en zor kısmı, düzenlemeyi doğru yapmaktır. Bunun nedeni, yalnızca düzenleme yaptığınızı düşünseniz bile application servisine kazara iş mantığı eklemediğinizden emin olmanız gerektiğidir. Peki bu bağlamda orkestrasyon ne anlama geliyor?

    Düzenleme ile, doğru domain nesnelerini doğru sırada aramak ve çağırmak, doğru girdi parametrelerini geçirmek ve doğru çıktıyı döndürmek demek istiyorum. En basit haliyle, bir application servisi bir kümeyi bir kimliğe göre arayabilir, bu kümede bir yöntemi çağırabilir, kaydedebilir ve geri dönebilir. Bununla birlikte, daha karmaşık durumlarda, yöntemin birden çok kümeye bakması, domain servisleriyle etkileşime girmesi, girdi doğrulaması yapması vb. Gerekebilir. Kendinizi uzun application servisi yöntemleri yazarken bulursanız, kendinize aşağıdaki soruları sormalısınız:

    Yöntem bir iş kararı mı veriyor yoksa domain modelinden karar vermesini mi istiyor?

    Kodun bir kısmı domain olay dinleyicilerine (domain event listeners) taşınmalı mı?

    Bununla birlikte, bir application servisi yönteminde biten bir iş mantığına sahip olmak dünyanın sonu değil. Alan modeline hala oldukça yakındır ve iyi bir şekilde kapsüllenmiştir ve daha sonra alan modelini yeniden düzenlemek oldukça kolay olacaktır. Sizin için hemen anlaşılmamışsa, bir şeyin alan modeline mi yoksa application servistine mi girmesi gerektiğini düşünerek çok fazla değerli zaman kaybetmeyin.

    Kod Örnekleri


    İşte tipik bir orkestrasyonun nasıl görünebileceğine dair bir Java örneği. Kod test edilmemiştir ve gerçek Java kodundan daha çok sözde kod olarak ele alınmalıdır.

    Birden Çok domain Nesnesini İçeren Düzenleme


    @Service
    class CustomerRegistrationService {
    
        @Transactional // 
        @PermitAll // 
        public Customer registerNewCustomer(CustomerRegistrationRequest request) {
            var violations = validator.validate(request); // 
            if (violations.size() > 0) {
                throw new InvalidCustomerRegistrationRequest(violations);
            }
            customerDuplicateLocator.checkForDuplicates(request); // 
            var customer = customerFactory.createNewCustomer(request); // 
            return customerRepository.save(customer); // 
        }
    }
    
    
    1. Application servisti yöntemi bir transaction içinde çalışır.
    2. Application servisi yöntemine herhangi bir kullanıcı tarafından erişilebilir.
    3. Gelen kayıt talebinin gerekli tüm bilgileri içerip içermediğini kontrol etmek için bir JSR-303 doğrulayıcı çağırıyoruz. İstek geçersizse, kullanıcıya geri bildirilecek bir istisna atarız.
    4. Veritabanında aynı bilgilere sahip bir müşteri olup olmadığını kontrol edecek bir domain servisini çağırıyoruz. Durum böyleyse, domain servisi kullanıcıya geri yayılacak bir istisna (burada gösterilmemiştir) atar.
    5. Kayıt talebi nesnesinden gelen bilgilerle yeni bir Müşteri agrregate'i oluşturacak bir domain factory'si çağırıyoruz.
    6. Müşteriyi kaydetmek için bir domain repository'si çağırırız ve yeni oluşturulan ve kaydedilen müşteri aggregate root döndürürüz.

    Domain Olay Dinleyicileri (Domain Event Listeners)

    Taktiksel domaine dayalı tasarımla ilgili bir önceki makalede, domain olayları ve domain olay dinleyicileri hakkında konuştuk. Bununla birlikte, domain olay dinleyicilerinin genel sistem mimarisine nerede uyduğundan bahsetmedik. Önceki makaleden hatırlıyoruz ki, bir domain olay dinleyicisi, olayı ilk etapta yayınlayan yöntemin sonucunu etkilememelidir. Pratikte bu, bir domain olay dinleyicisinin kendi işlemi içinde çalışması gerektiği anlamına gelir.

    Bu nedenle, domain olay dinleyicilerini bir istemci tarafından değil, bir domain olayı tarafından başlatılan özel bir applicaiton servis türü olarak görüyorum. Başka bir deyişle: domain olay dinleyicileri, domain modelinin içine değil applicaiton servis katmanına aittir. Bu aynı zamanda bir domain olay dinleyicisinin herhangi bir iş mantığı içermemesi gereken bir düzenleyici olduğu anlamına gelir. Belirli bir domain olayı yayınlandığında ne olması gerektiğine bağlı olarak, birden fazla ileriye giden yol varsa onunla ne yapılacağına karar veren ayrı bir domain servisi oluşturmanız gerekebilir.

    Şöyle ki, önceki makaledeki agregalarla ilgili bölümde, bazen aynı işlem içinde birden fazla agregayı değiştirmenin, toplam tasarım yönergelerine aykırı olsa da, haklı görülebileceğinden bahsetmiştim. Ayrıca bunun tercihen domain olayları aracılığıyla yapılması gerektiğinden de bahsetmiştim. Bu gibi durumlarda, domain olay dinleyicileri mevcut işleme katılmak zorunda kalacak ve bu nedenle etkinliği yayınlayan yöntemin sonucunu etkileyerek hem domain olayları hem de applicaiton servisleri için tasarım yönergelerini bozabilirler. Bunu bilerek yaptığınız ve gelecekte karşılaşabileceğiniz sonuçların farkında olduğunuz sürece bu dünyanın sonu değildir. Bazen sadece pragmatik olmanız gerekir.

    Giriş ve çıkış (Input and Output)


    Applicaiton servislerini tasarlarken önemli bir karar, hangi verilerin (metod parametreleri) tüketileceğine ve hangi verilerin döndürüleceğine karar vermektir. Üç seçeneğiniz var:

    1. Entityleri ve değer nesnelerini doğrudan domain modelinden kullanın.
    2. Ayrı Veri Aktarım Nesneleri (DTO'lar) kullanın.
    3. Yukarıdaki ikisinin birleşimi olan domain payload Nesnelerini (DPO'lar) kullanın.

    Her alternatifin kendi artıları ve eksileri vardır, bu nedenle her birine daha yakından bakalım.

    Varlıklar ve Agregalar (Entities and Aggregates)


    İlk alternatifte, applicaiton servisleri tüm agregaları (veya bunların parçalarını) döndürür. Client onlarla istediğini yapabilir ve değişiklikleri kaydetme zamanı geldiğinde, agregalar(veya bunların parçaları) parametre olarak applicaiton servislere geri gönderilir.

    Bu alternatif, domain modeli anemik olduğunda (yani yalnızca veri içerdiğinde ve iş mantığı olmadığında) ve agregalar küçük ve kararlı olduğunda (yakın gelecekte pek değişmesi muhtemel olmadığı gibi) en iyi şekilde çalışır.

    Ayrıca, istemcinin sisteme REST veya SOAP üzerinden erişmesi durumunda da çalışır ve agregalar kolayca JSON veya XML ve geri serileştirilebilir. Bu durumda, istemciler aslında agregalarınıza doğrudan değil, tamamen farklı bir dilde uygulanabilecek bir JSON veya toplamın XML temsiliyle etkileşimde bulunacaktır. Clientin bakış açısından, agregalar sadece DTO'lardır.

    Bu alternatifin avantajları şunlardır:
    • Zaten sahip olduğunuz sınıfları kullanabilirsiniz
    • Domian nesneleri ve DTO'lar arasında dönüştürme yapmaya gerek yoktur.
    Dezavantajlar:
    • Domain modelini doğrudan istemcilerle eşleştirir. Domain modeli değişirse, clientleriniz de değiştirmeniz gerekir.
    • Kullanıcı girişini nasıl doğrulayacağınıza dair kısıtlamalar getirir (bununla ilgili daha sonra daha fazla bahsedeceğiz).
    • Agregalarınız, clientin agregayı tutarsız bir duruma getiremeyeceği veya izin verilmeyen bir işlemi gerçekleştiremeyeceği şekilde tasarlamalısınız.
    • Bir toplu (JPA) içindeki entitylerin geç yüklenmesiyle ilgili sorunlarla karşılaşabilirsiniz.
    Şahsen ben bu yaklaşımdan elimden geldiğince kaçınmaya çalışıyorum.

    Veri Aktarım Nesneleri (Data Transfer Objects)

    İkinci alternatifte, applicaiton servisleri veri aktarım nesnelerini tüketir ve döndürür. DTO'lar, domain modelindeki entitylere karşılık gelebilir, ancak daha sık olarak belirli bir applicaiton servisti veya hatta belirli bir applicaiton servisi yöntemi (istek ve yanıt nesneleri gibi) için tasarlanmıştır. applicaiton servisi daha sonra verileri DTO'lar ve domain nesneleri arasında ileri geri taşımaktan sorumludur.

    Bu alternatif, domain modeli iş mantığı açısından çok zengin olduğunda, agregalar karmaşık olduğunda veya istemci API'sini olabildiğince istikrarlı tutarken domain modelinin çok değişmesi beklendiğinde en iyi şekilde çalışır.

    Bu alternatifin avantajları şunlardır:

    • İstemciler, domain modelinden ayrıştırılarak istemcileri değiştirmek zorunda kalmadan onu geliştirmeyi kolaylaştırır.
    • Yalnızca gerçekte ihtiyaç duyulan veriler istemciler ve uygulama hizmetleri arasında aktarılır, bu da performansı artırır (özellikle istemci ve uygulama hizmeti dağıtılmış bir ortamda bir ağ üzerinden iletişim kuruyorsa).
    • Domain modeline erişimi kontrol etmek, özellikle yalnızca belirli kullanıcıların belirli agrega yöntemlerini başlatmasına veya belirli toplu öznitelik değerlerini görüntülemesine izin veriliyorsa daha kolay hale gelir.
    • Yalnızca applicaiton servisleri, aktif işlemlerin içindeki agregalarla etkileşime girecektir. Bu, bir agrega (JPA) içindeki entitylerin  lazy yüklemesini kullanabileceğiniz anlamına gelir.
    • DTO'lar arayüzlerse ve sınıflar değilse, daha da fazla esneklik elde edersiniz.

    Dezavantajlar:
    • Bakım için yeni bir DTO sınıfı seti alırsınız.
    • Verileri DTO'lar ve agregalar arasında ileri geri taşımanız gerekir. DTO'lar ve entitiyler yapı olarak neredeyse benzer ise, bu özellikle sıkıcı olabilir. Bir takımda çalışıyorsanız, DTO'lar ve agregaların neden ayrılması gerektiğine dair iyi bir açıklamaya ihtiyacınız vardır.
    Şahsen, çoğu durumda işe başladığım yaklaşım budur. Bazen, bakacağımız bir sonraki alternatif olan DTO'larımı DPO'lara dönüştürmek oluyor.

    Domain Yük Nesneleri(Domain Payload Objects)

    Üçüncü alternatifte, applicaiton servisleri domain yük nesnelerini tüketir ve döndürür. Domain yük nesnesi, domain modelinin farkında olan ve domain nesnelerini içerebilen bir veri aktarım nesnesidir. Bu, esasen ilk iki alternatifin bir kombinasyonudur.

    Bu alternatif, domain modelinin anemik olduğu, agregaların  küçük ve kararlı olduğu ve birden çok farklı agregayı içeren bir işlem uygulamak istediğiniz durumlarda en iyi sonucu verir. Kişisel olarak, DPO'ları girdi nesnelerinden çok çıktı nesneleri olarak kullandığımı söyleyebilirim. Bununla birlikte, DPO'larda domain nesnelerinin kullanımını, yalnızca mümkünse nesnelere değer vermek için sınırlandırmaya çalışıyorum.

    Bu alternatifin avantajları şunlardır:

    • Her şey için DTO sınıfları oluşturmanıza gerek yoktur. Bir domain nesnesini doğrudan istemciye iletmek yeterince iyidir, bunu yaparsınız. Özel bir DTO'ya ihtiyacınız olduğunda, bir tane oluşturursunuz. İkisine de ihtiyacınız olduğunda ikisini de kullanırsınız.

    Dezavantajlar:

    • İlk alternatifle aynı. Dezavantajlar, yalnızca DPO'ların içine immutable değer nesnelerini dahil ederek hafifletilebilir.

    Kod Örnekleri

    Sırasıyla DTO'ları ve DPO'ları kullanmanın iki Java örneği. DTO örneği, varlığı doğrudan döndürmektense bir DTO kullanmanın daha mantıklı olduğu bir kullanım durumunu gösterir: Entity özelliklerinin yalnızca bir kısmına ihtiyaç vardır ve entityde var olmayan bilgileri eklememiz gerekir. DPO örneği, DPO kullanmanın mantıklı olduğu bir kullanım durumunu gösterir: Bir şekilde birbiriyle ilişkili birçok farklı agregayı eklememiz gerekir.

    Kod test edilmemiştir ve gerçek Java kodundan daha çok sözde kod olarak ele alınmalıdır.

    Veri Aktarım Nesnesi Örneği

    public class CustomerListEntryDTO {  
        private CustomerId id;
        private String name;
        private LocalDate lastInvoiceDate;
    
         Getters and setters omitted
    }
    @Service
    public class CustomerListingService {
    
        @Transactional
        public List  getCustomerList() {
            var customers = customerRepository.findAll();  
            var dtos = new ArrayList();
            for (var customer : customers) {
                var lastInvoiceDate = invoiceService.findLastInvoiceDate(customer.getId()); 
                dto = new CustomerListEntryDTO();
                dto.setId(customer.getId());
                dto.setName(customer.getName());
                dto.setLastInvoiceDate(lastInvoiceDate);
                dtos.add(dto);
            }
            return dto;
        }
    }
    • Veri Aktarım Nesnesi, herhangi bir iş mantığı içermeyen bir veri yapısıdır. Bu özel DTO, yalnızca müşteri adını ve son fatura tarihini göstermesi gereken bir kullanıcı arabirimi liste görünümünde kullanılmak üzere tasarlanmıştır.
    • Veritabanından tüm müşteri kümelerini arıyoruz. Gerçek dünyadaki bir uygulamada, bu yalnızca müşterilerin bir alt kümesini döndüren sayfalandırılmış bir sorgu olacaktır.
    • Son fatura tarihi müşteri varlığında saklanmaz, bu nedenle bizim için aramak için bir domain servisini çağırmamız gerekir.
    • DTO örneğini oluşturuyoruz ve onu verilerle dolduruyoruz.
    Domain Yük Nesnesi Örneği


    public class CustomerInvoiceMonthlySummaryDPO { // 
        private Customer customer;
        private YearMonth month;
        private Collection invoices;
    
        // Getters and setters omitted
    }
    
    @Service
    public class CustomerInvoiceSummaryService {
    
        public CustomerInvoiceMontlySummaryDPO getMonthlySummary(CustomerId customerId, YearMonth month) {
            var customer = customerRepository.findById(customerId); // 
            var invoices = invoiceRepository.findByYearMonth(customerId, month); // 
            var dpo = new CustomerInvoiceMonthlySummaryDPO(); // 
            dpo.setCustomer(customer);
            dpo.setMonth(month);
            dpo.setInvoices(invoices);
            return dpo;
        }
    }
    
    1. Domain Yük Nesnesi, hem domain nesnelerini (bu durumda entityler) hem de ek bilgileri (bu durumda yıl ve ay) içeren herhangi bir iş mantığı olmayan bir veri yapısıdır.
    2. Müşterinin agrega kökünü repodan alıyoruz.
    3. Müşterinin belirtilen yıl ve aya ait faturalarını alıyoruz.
    4. DPO örneğini oluşturup verilerle dolduruyoruz.

    Giriş Doğrulama (Input Validation)

    Daha önce bahsettiğimiz gibi, bir agrega her zaman tutarlı bir durumda olmalıdır. Bu, diğer şeylerin yanı sıra, bir agreganın durumunu değiştirmek için kullanılan tüm girdileri uygun şekilde doğrulamamız gerektiği anlamına gelir. Bunu nasıl ve nerede yapıyoruz?

    Bir kullanıcı deneyimi perspektifinden, kullanıcı arayüzü, verilerin geçersiz olması durumunda kullanıcının bir işlemi bile gerçekleştirememesi için doğrulama içermelidir. Ancak, altıgen bir sistemde sadece kullanıcı arabirimi doğrulamasına güvenmek yeterince iyi değildir. Bunun nedeni, kullanıcı arayüzünün sisteme potansiyel olarak birçok giriş noktasından biri olmasıdır. Bir REST uç noktası, domain modeline herhangi bir çöpün geçmesine izin veriyorsa, kullanıcı arabiriminin verileri doğru şekilde doğrulamasına yardımcı olmaz.

    Girdi doğrulama hakkında düşünürken aslında iki farklı doğrulama türü vardır: format doğrulama ve içerik doğrulama. Formatı doğrularken, belirli türlerdeki belirli değerlerin belirli kurallara uygun olup olmadığını kontrol ederiz. Örneğin. bir sosyal güvenlik numarasının belirli bir modelde olması beklenmektedir. İçeriği doğruladığımızda, zaten iyi biçimlendirilmiş bir veri parçasına sahibiz ve bu verilerin mantıklı olup olmadığını kontrol etmekle ilgileniyoruz. Örneğin. İyi biçimlendirilmiş bir sosyal güvenlik numarasının gerçekte gerçek bir kişiye karşılık gelip gelmediğini kontrol etmek isteyebiliriz. Bu doğrulamaları farklı şekillerde uygulayabilirsiniz, bu sebeblerden dolayı daha yakından bakalım.

    Biçim Doğrulaması (Format Validation)

    Domain modelinizde ilkel türlerin (dizeler veya tamsayılar gibi) etrafına sarılmış çok sayıda değer nesnesi kullanıyorsanız (bunu kişisel olarak yapma eğilimindeyim), o zaman biçim doğrulamasını doğrudan değer nesnesi oluşturucunuza oluşturmak mantıklıdır. . Başka bir deyişle, örneğin oluşturulması mümkün olmamalıdır. iyi biçimlendirilmiş bir bağımsız değişkeni iletmeden bir EmailAddress veya SocialSecurityNumber örneği. Bu, geçerli verileri girmenin bilinen birden çok yolu varsa (örneğin, bir telefon numarası girerken bazı kişiler sayıyı gruplara ayırmak için boşluklar veya kısa çizgiler kullanabilirken diğerleri oluşturucu içinde bazı ayrıştırma ve temizleme işlemleri yapabileceğiniz gibi ek bir avantaja sahiptir hiçbir boşluk kullanmayın).

    Şimdi, değer nesneleri geçerli olduğunda, onları kullanan entityleri nasıl doğrularız? Java geliştiricileri için kullanılabilen iki seçenek vardır.

    İlk seçenek, doğrulamayı kurucularınıza, fabrikalarınıza ve ayarlayıcı yöntemlerinize eklemektir. Buradaki fikir, bir kümeyi tutarsız bir duruma getirmenin bile mümkün olmaması gerektiğidir: tüm gerekli alanlar kurucuda doldurulmalıdır, gerekli alanların ayarlayıcıları boş parametreleri kabul etmeyecektir, diğer ayarlayıcılar yanlış bir değeri kabul etmeyecektir. format veya uzunluk, vb. İş mantığı açısından çok zengin olan alan modelleriyle çalışırken kişisel olarak bu yaklaşımı kullanma eğilimindeyim. Domain modelini çok sağlam kılar, ancak aynı zamanda pratik olarak sizi istemciler ve applicaiton servisleri arasında DTO'ları kullanmaya zorlar çünkü bir UI'ye düzgün bir şekilde bağlanmak hemen hemen imkansızdır.

    İkinci seçenek, Java Bean Doğrulamasını (JSR-303) kullanmaktır. Tüm alanlara ek açıklamalar yerleştirin ve applicaiton servisinizin, onunla başka bir şey yapmadan önce agregayı Doğrulayıcı aracılığıyla çalıştırdığından emin olun. Ben kişisel olarak bu yaklaşımı anemik alan modelleriyle çalışırken kullanma eğilimindeyim. Agreganın kendisi kimsenin onu tutarsız bir duruma sokmasını engellemese de, bir depodan alınmış veya doğrulamadan geçmiş tüm agregaların tutarlı olduğunu güvenle varsayabilirsiniz.

    Domain modelinizdeki ilk seçeneği ve gelen DTO'larınız veya DPO'larınız için Java Bean Doğrulamasını kullanarak her iki seçeneği de birleştirebilirsiniz.

    İçerik Doğrulama (Content Validation)

    İçerik doğrulamasının en basit durumu, aynı agregada iki veya daha fazla birbirine bağlı özniteliğin geçerli olduğundan emin olmaktır (örneğin, bir attribute ayarlanmışsa, diğeri boş olmalıdır ve bunun tersi de geçerlidir). Bunu doğrudan entity sınıfının kendisine uygulayabilir veya sınıf düzeyinde bir Java Bean Doğrulama kısıtlaması kullanabilirsiniz. Bu tür içerik doğrulaması, aynı mekanizmaları kullandığı için format doğrulaması yapılırken ücretsiz olarak gelecektir.

    Daha karmaşık bir içerik doğrulama durumu, bir yerde bir arama listesinde belirli bir değerin var olduğunu (veya olmadığını) kontrol etmek olabilir. Bu büyük ölçüde applicaiton  servsinin sorumluluğundadır. Herhangi bir iş veya kalıcılık işleminin devam etmesine izin vermeden önce, application servisi aramayı gerçekleştirmeli ve gerekirse bir istisna atmalıdır. Bu, entityler taşınabilir domain nesneleri olduğundan, arama için gereken nesneler tipik olarak statik olduğundan entitylerinize yerleştirmek isteyeceğiniz bir şey değildir (hareketli ve statik nesneler hakkında daha fazla bilgi için taktik DDD hakkındaki önceki makaleye bakın).

    İçerik doğrulamasının en karmaşık durumu, tüm bir agregayı bir dizi iş kuralına göre doğrulamak olacaktır. Bu durumda sorumluluk, domain modeli ve application servisi arasında bölünür. Doğrulamanın kendisinin gerçekleştirilmesinden bir domain servisi sorumlu olabilir, ancak domain servisini başlatmaktan application servisi sorumlu olacaktır.


    Kod Örnekleri
    Burada, doğrulama işleminin üç farklı yoluna bakacağız. İlk durumda, bir değer nesnesinin (bir telefon numarası) yapıcısının içinde biçim doğrulaması gerçekleştirmeye bakacağız. İkinci durumda, yerleşik doğrulama olan bir varlığa bakacağız, böylece nesneyi ilk etapta tutarsız bir duruma sokmak mümkün olmaz. Üçüncü ve son durumda, aynı varlığa bakacağız ancak JSR-303 doğrulaması kullanılarak uygulanacağız. Bu, nesneyi tutarsız bir duruma getirmeyi mümkün kılar, ancak bu şekilde veritabanına kaydetmemeyi mümkün kılar.

    Biçim Doğrulamalı Değer Nesnesi


    public class PhoneNumber implements ValueObject {
        private final String phoneNumber;
    
        public PhoneNumber(String phoneNumber) {
            Objects.requireNonNull(phoneNumber, "phoneNumber must not be null"); // 
            var sb = new StringBuilder();
            char ch;
            for (int i = 0; i lt phoneNumber.length(); ++i) {
                ch = phoneNumber.charAt(i);
                if (Character.isDigit(ch)) { // 
                    sb.append(ch);
                } else if (!Character.isWhitespace(ch) && ch != '(' && ch != ')' && ch != '-' && ch != '.') { // 
                    throw new IllegalArgument(phoneNumber + " is not valid");
                }
            }
            if (sb.length() == 0) { // 
                throw new IllegalArgumentException("phoneNumber must not be empty");
            }
            this.phoneNumber = sb.toString();
        }
    
        @Override
        public String toString() {
            return phoneNumber;
        }
    
        // Equals and hashCode omitted
    }
    
    İlk olarak, giriş değerinin boş olmadığını kontrol ederiz.

    Gerçekte sakladığımız son telefon numarasına yalnızca rakamları dahil ediyoruz. Uluslararası telefon numaraları için, ilk karakter olarak bir '+' işaretini de desteklemeliyiz, ancak bunu okuyucuya bir alıştırma olarak bırakacağız.

    İnsanların telefon numaralarında sıklıkla kullandıkları beyaz boşluklara ve belirli özel karakterlere izin veririz, ancak yok sayarız.

    Son olarak tüm temizlik bittiğinde telefon numarasının boş olmadığını kontrol ediyoruz.

    Yerleşik Doğrulamalı Entity


    public class Customer implements Entity {
    
        // Fields omitted
    
        public Customer(CustomerNo customerNo, String name, PostalAddress address) {
            setCustomerNo(customerNo); // 
            setName(name);
            setPostalAddress(address);
        }
    
        public setCustomerNo(CustomerNo customerNo) {
            this.customerNo = Objects.requireNonNull(customerNo, "customerNo must not be null");
        }
    
        public setName(String name) {
            Objects.requireNonNull(nanme, "name must not be null");
            if (name.length() lt 1 || name.length > 50) { // 
                throw new IllegalArgumentException("Name must be between 1 and 50 characters");
            }
            this.name = name;
        }
    
        public setAddress(PostalAddress address) {
            this.address = Objects.requireNonNull(address, "address must not be null");
        }
    }
    1. Setter yöntemlerinde uygulanan doğrulamayı gerçekleştirmek için kurucudan setterları çağırırız. Bir alt sınıfın bunlardan herhangi birini geçersiz kılmaya karar vermesi durumunda, bir kurucudan geçersiz kılınabilen yöntemleri çağırmanın küçük bir riski vardır. Bu durumda, setter yöntemlerini final olarak işaretlemek daha iyi olacaktır, ancak bazı kalıcılık çerçevelerinin bununla ilgili bir sorunu olabilir. Sadece ne yaptığınızı bilmeniz lazım.
    2. Burada bir stringin uzunluğunu kontrol ediyoruz. Her müşterinin bir adı olması gerektiğinden, alt sınır bir iş gereksinimidir. Bu durumda veritabanı, yalnızca 50 karakterlik stringleri depolamasına izin veren bir şemaya sahip olduğundan, üst düzey bir veritabanı gereksinimidir. Doğrulamayı buraya zaten ekleyerek, veritabanına çok uzun stringler eklemeye çalıştığınızda daha sonraki bir aşamada can sıkıcı SQL hatalarını önleyebilirsiniz.

    JSR-303 Doğrulamalı Entity

    public class Customer implements Entity {
    
        @NotNull (1)
        private CustomerNo customerNo;
    
        @NotBlank (2)
        @Size(max = 50) (3) 
        private String name;
    
        @NotNull
        private PostalAddress address;
    
        // Setters omitted
    }
    
    1. Bu anatasyon, entity kaydedildiğinde müşteri numarasının null olmamasını sağlar.
    2. Bu anatasyon, entity kaydedildiğinde adın boş veya null olmamasını sağlar.
    3. Bu anatasyon, entity kaydedildiğinde adın 50 karakterden uzun olmamasını sağlar.

    Boyut Önemli mi?

    Portlara ve adaptörlere geçmeden önce kısaca bahsetmek istediğim bir şey daha var. Tüm facede'lerde olduğu gibi, çok fazla şey bilen ve çok şey yapan büyük tanrı sınıflarına dönüşen application servsilerin her zaman mevcut bir riski vardır. Bu tür sınıfları okumak ve sürdürmek genellikle çok büyük oldukları için zordur.

    Peki application servsilerini nasıl küçük tutarsınız? İlk adım, elbette çok büyük büyüyen bir servisi daha küçük servislere bölmek. Ancak bunda da bir risk var. Durumların, geliştiricilerin aralarındaki farkın ne olduğunu veya hangi servise hangi yöntemin girmesi gerektiğini bilmediği kadar benzer iki servis olduğunu gördüm. Sonuç, servis yöntemlerinin iki ayrı servis sınıfına dağılmış olması ve hatta bazen iki kez (her servisde bir kez), ancak farklı geliştiriciler tarafından uygulanmasıydı.

    application servsilerini tasarlarken, onları olabildiğince tutarlı hale getirmeye çalışıyorum. CRUD uygulamalarında bu, agrega başına bir application servsi anlamına gelebilir. Daha fazla domaine dayalı uygulamalarda bu, iş süreci başına bir application servisi veya hatta belirli kullanım durumları veya kullanıcı arabirimi görünümleri için ayrı servisler anlamına gelebilir.

    Adlandırma, application servsleri tasarlarken çok iyi bir kılavuzdur. Application servslerinizi, ilgilendikleri agregaların aksine yaptıklarına göre adlandırmaya çalışın. Örneğin. EmployeeCrudService veya EmploymentContractTerminationUsecase, EmployeeService'ten çok daha iyi isimlerdir ve bu da herhangi bir anlama gelebilir. Ayrıca adlandırma kurallarınızı düşünmek için biraz zaman ayırın: tüm servislerinizi gerçekten Service son ekiyle sonlandırmanız gerekiyor mu? Bazı durumlarda Usecase veya Orchestrator gibi son ekleri kullanmak veya hatta soneki tamamen dışarıda bırakmak daha mantıklı olur mu?

    Son olarak, komut tabanlı uygulama servislerinden bahsetmek istiyorum. Bu durumda, her application servis modelini, karşılık gelen bir komut işleyiciye sahip bir komut nesnesi olarak modellersiniz. Bu, her application servisinin tam olarak bir komutu işleyen tam olarak bir yöntem içerdiği anlamına gelir. Özel komutlar veya komut işleyicileri oluşturmak için polimorfizmi kullanabilirsiniz. Bu yaklaşım, çok sayıda küçük sınıfla sonuçlanır ve özellikle kullanıcı arabirimleri doğası gereği komut güdümlü olan veya istemcilerin bir ileti kuyruğu (MQ) veya kurumsal hizmet veriyolu gibi bir tür ileti mekanizması aracılığıyla uygulama hizmetleriyle etkileşime girdiği uygulamalarda yararlıdır. ESB).

    Kod Örnekleri

    Size Tanrı sınıfının neye benzediğine dair bir örnek vermeyeceğim çünkü bu çok fazla yer kaplar. Ayrıca, bir süredir bu meslekte olan çoğu geliştiricinin bu tür sınıflardan adil bir pay aldığını düşünüyorum. Bunun yerine, komut tabanlı bir uygulama hizmetinin nasıl görünebileceğine dair bir örneğe bakacağız. Kod test edilmemiştir ve gerçek Java kodundan daha çok sözde kod olarak ele alınmalıdır.

    Komut Tabanlı Uygulama Hizmetleri



    public interface Command { // 
    }
    
    public interface CommandHandler, R/> { // 
    
        R handleCommand(C command);
    }
    
    public class CommandGateway { // 
    
        // Fields omitted
    
        public , R/> R handleCommand(C command) {
            var handler = commandHandlers.findHandlerFor(command)
                .orElseThrow(() -> new IllegalStateException("No command handler found"));
            return handler.handleCommand(command);
        }
    }
    
    public class CreateCustomerCommand implements Command { // 
        private final String name;
        private final PostalAddress address;
        private final PhoneNumber phone;
        private final EmailAddress email;
    
        // Constructor and getters omitted
    }
    
    public class CreateCustomerCommandHandler implements CommandHandler { // 
    
        @Override
        @Transactional
        public Customer handleCommand(CreateCustomerCommand command) {
            var customer = new Customer();
            customer.setName(command.getName());
            customer.setAddress(command.getAddress());
            customer.setPhone(command.getPhone());
            customer.setEmail(command.getEmail());
            return customerRepository.save(customer);
        }
    }
    
    1. Komut arayüzü, komutun sonucunu (çıktısını) da gösteren bir işaret arayüzüdür. Komutun çıkışı yoksa, sonuç Void olabilir.
    2. CommandHandler arabirimi, belirli bir komutu nasıl işleyeceğini (gerçekleştireceğini) ve sonucu nasıl döndüreceğini bilen bir sınıf tarafından uygulanır.
    3. İstemciler, tek tek komut işleyicileri aramak zorunda kalmamak için bir CommandGateway ile etkileşime girer. Ağ geçidi, mevcut tüm komut işleyicileri ve herhangi bir komuta göre doğru olanı nasıl bulacağını bilir. İşleyicileri aramak için kullanılan kod, işleyicileri kaydetmeye yönelik temel mekanizmaya bağlı olduğundan, örnekte dahil edilmemiştir.
    4. Her komut, Komut arayüzünü uygular ve komutu gerçekleştirmek için gerekli tüm bilgileri içerir. Komutlarımı yerleşik doğrulama ile değişmez hale getirmeyi seviyorum, ancak bunları değiştirilebilir ve JSR-303 doğrulamasını da kullanabilirsiniz. Hatta komutlarınızı arayüz olarak bırakabilir ve maksimum esneklik için istemcilerin kendilerinin uygulamasına izin verebilirsiniz.
    5. Her komutun, komutu gerçekleştiren ve sonucu döndüren kendi işleyicisi vardır.

    Bağlantı Noktaları ve Adaptörler (Ports And Adaptors)

    Şimdiye kadar domaini ve onu çevreleyen ve onunla etkileşime giren application servisleri tartıştık. Ancak, istemcilerin onları çağırmasının bir yolu yoksa ve portların ve adaptörlerin resme girmediği bu yerde, bu application servisleri tamamen yararsızdır.

    Port Nedir?

    Port, belirli bir amaç veya protokol için tasarlanmış, sistem ile dış dünya arasındaki bir arayüzdür. Portları yalnızca dış istemcilerin sisteme erişmesine izin vermek için değil, aynı zamanda sistemin harici sistemlere erişmesine izin vermek için de kullanılır.

    Artık portlar ağ portları olarak ve protokolleri HTTP gibi ağ protokolleri olarak düşünmeye başlamak kolaydır. Bu hatayı ben kendim yaptım ve hatta Vernon bunu kitabındaki en az bir örnekte yapıyor. Ancak, Vernon'un bahsettiği Alistair Cockburn'ün yazdığı makaleye daha yakından bakarsanız, durumun böyle olmadığını göreceksiniz. Aslında bundan çok daha ilginç.

    Port, uygulamayla belirli bir etkileşim türü (dolayısıyla "protokol" kelimesi) için tasarlanmış, teknolojiden bağımsız bir uygulama programlama arabirimidir (API). Bu protokolü nasıl tanımlayacağınız tamamen size bağlıdır ve bu yaklaşımı heyecan verici kılan da budur. İşte sahip olabileceğiniz farklı portlara birkaç örnek:

    • Bir veritabanına erişmek için uygulamanız tarafından kullanılan bir port
    • Uygulamanız tarafından e-posta veya kısa mesaj gibi mesajlar göndermek için kullanılan bir port
    • İnsan kullanıcılar tarafından uygulamanıza erişmek için kullanılan bir port
    • Uygulamanıza erişmek için diğer sistemler tarafından kullanılan bir port
    • Uygulamanıza erişmek için belirli bir kullanıcı grubu tarafından kullanılan bir port
    • Belirli bir kullanım durumunu ortaya çıkaran bir port
    • İstemcileri sorgulamak için tasarlanmış bir port
    • Müşterilere abone olmak için tasarlanmış bir port
    • Senkronize iletişim için tasarlanmış bir port
    • Eşzamansız iletişim için tasarlanmış bir port
    • Belirli bir cihaz türü için tasarlanmış bir port
    Bu liste hiçbir şekilde kapsamlı değildir ve eminim daha birçok örneği kendiniz de bulabilirsiniz. Bu türleri de birleştirebilirsiniz. Örneğin, yöneticilerin senkronize olmayan iletişim kullanan bir istemci kullanarak kullanıcıları yönetmesine olanak tanıyan bir porta sahip olabilirsiniz. Diğer bağlantı noktalarını veya domain modelini etkilemeden sisteme istediğiniz veya ihtiyaç duyduğunuz kadar port ekleyebilirsiniz.

    Altıgen mimari şemasına tekrar bakalım:



    İç altıgenin her bir tarafı bir portu temsil eder. Bu mimarinin genellikle bu şekilde tasvir edilmesinin nedeni budur: Farklı portlar için kullanabileceğiniz kutudan çıkar çıkmaz altı tarafa ve ihtiyacınız olduğu kadar çok sayıda adaptör çekmeniz için bolca alana sahip olursunuz. Ama adaptör nedir?

    Adaptör nedir?

    Bağlantı noktalarının (portların) teknolojiden bağımsız olduğundan bahsetmiştim. Yine de, bazı teknolojiler aracılığıyla sistemle etkileşime girersiniz - bir web tarayıcısı, bir mobil cihaz, özel bir donanım cihazı, bir masaüstü istemcisi vb. Adaptörlerin devreye girdiği yer burasıdır.

    Bir adaptör, belirli bir teknolojiyi kullanarak belirli bir port üzerinden etkileşime izin verir. Örneğin:

    Bir REST adaptörü, REST istemcilerinin bazı portlar aracılığıyla sistemle etkileşime girmesine izin verir

    Bir RabbitMQ adpter, RabbitMQ istemcilerinin bazı portlar üzerinden sistemle etkileşime girmesine izin verir.

    Bir SQL adaptörü, sistemin bazı portlar üzerinden bir veritabanı ile etkileşime girmesine izin verir

    Bir Vaadin adaptörü, insan kullanıcıların bazı portlar aracılığıyla sistemle etkileşime girmesine olanak tanır

    Tek bir port için birden çok adaptöre veya birden çok port  için tek bir adaptöre sahip olabilirsiniz. Diğer adaptörleri, portları veya domain modelini etkilemeden sisteme istediğiniz veya ihtiyaç duyduğunuz kadar adaptör ekleyebilirsiniz.

    Koddaki Portlar ve Adaptörler


    Şimdiye kadar, kavramsal düzeyde bir portun ve adaptörün ne olduğu hakkında bir fikriniz olmalı. Peki bu kavramları koda nasıl dönüştürürsünüz? Bir bakalım!

    Portlar çoğu durumda kendilerini kodunuzda arayüzler olarak somutlaştırır. Dış sistemin uygulamanıza erişmesine izin veren portlar için bu arabirimler, application servisi arabirimlerinizdir:



    Arayüzünüzün uygulanması, application servis katmanınızın içinde bulunur ve adaptörler, servisi yalnızca arayüzü aracılığıyla kullanır. Bu, adaptörün applicaiton katmanınızı kullanan başka bir istemci olduğu klasik katmanlı mimariye çok uygundur. Temel fark, port kavramının daha iyi uygulama arabirimleri tasarlamanıza yardımcı olmasıdır, çünkü arabirimlerinizin istemcilerinin ne olacağını gerçekten düşünmeniz gerekir ve farklı istemcilerin tek boyutta uyum sağlamak yerine farklı arabirimlere ihtiyaç duyabileceğini kabul etmeniz gerekir. 

    Uygulamanızın bir adaptör aracılığıyla harici bir sisteme erişmesine izin veren bir porta baktığımızda işler daha ilginç hale geliyor:



    Bu durumda, arayüzü uygulayan adaptördür. Application servisi daha sonra bu arabirim aracılığıyla adaptörle etkileşime girer. Arabirimin kendisi uygulama hizmet katmanınızda (factory arabirimi gibi) veya domain modelinizde (repository arabirimi gibi) bulunur. Arayüz bir üst katmanda ("uygulama katmanı" veya "domain katmanı") bildirileceği, ancak daha düşük bir katmanda ("altyapı katmanı") uygulanacağı için bu yaklaşıma geleneksel katmanlı mimaride izin verilmezdi.

    Lütfen bu iki yaklaşımda da bağımlılık oklarının arayüzü işaret ettiğine dikkat edin. Uygulama her zaman adaptörden ayrılmış olarak kalır ve adaptör her zaman uygulamanın gerçekleştirilmesinden ayrı kalır.

    Bunu daha da somut hale getirmek için bazı kod örneklerine bakalım.

    Örnek 1: REST API

    İlk örnekte Java uygulamamız için bir REST API oluşturacağız:




    Port, REST aracılığıyla açığa çıkmaya uygun bazı application servisidir. REST controller'ı adaptör görevi görür. Doğal olarak, POJO'lar (Düz Eski Java Nesneleri) ve XML / JSON arasında hem sunucu uygulaması hem de eşleştirme sağlayan Spring veya JAX-RS gibi bir frameork kullanıyoruz. Sadece aşağıdakileri yapacak olan REST controller'ını uygulamalıyız:

    1. Input olarak ham XML / JSON veya serileştirilmemiş POJO'ları alın,
    2. Application servislerini çağırın,
    3. Frameork tarafından serileştirilecek ham XML / JSON veya POJO olarak bir yanıt oluşturun ve
    4. Response'u client'e iletin.
    İstemciler, bir tarayıcıda çalışan istemci tarafı web uygulamaları veya kendi sunucularında çalışan diğer sistemler olup olmadıklarına bakılmaksızın, bu belirli altıgen sistemin bir parçası değildir. Sistem ayrıca, port ve adaptörlerin desteklediği protokole ve teknolojiye uydukları sürece istemcilerin kim olduğuna da aldırmak zorunda değildir.

    Örnek 2: Sunucu Tarafı Vaadin Kullanıcı Arayüzü

    İkinci örnekte, farklı bir adaptör türüne, yani sunucu tarafı Vaadin UI'ye bakacağız:


    Port, bir web kullanıcı arayüzü aracılığıyla gösterilmeye uygun bazı application servisidir. Adaptör, gelen kullanıcı eylemlerini applicaiton servisi yöntemi çağrılarına ve çıktıyı tarayıcıda işlenebilen HTML'ye çeviren Vaadin UI'dir. Kullanıcı arayüzünü yalnızca başka bir adaptör olarak düşünmek, iş mantığını kullanıcı arayüzünün dışında tutmanın mükemmel bir yoludur.

    Örnek 3: İlişkisel Veritabanı ile İletişim Kurma

    Üçüncü örnekte, işleri tersine çevireceğiz ve sistemimizin harici bir sisteme, daha özel olarak ilişkisel bir veritabanını çağırmasını sağlayan bir adaptöre bakacağız:


    Bu sefer, Spring Data kullandığımız için, port, domain modelinden bir repository arayüzüdür (Spring Data kullanmadıysak, port muhtemelen repository uygulamalarına, transaction yönetimine erişim sağlayan bir tür veritabanı ağ geçidi arayüzü olacaktır. ve bunun gibi).

    Adaptör Spring Data JPA'dır, bu yüzden gerçekten kendimiz yazmamız gerekmez, sadece doğru şekilde kurmamız gerekir. Uygulama başladığında ara yüzü proxy kullanarak otomatik olarak uygulayacaktır. Spring konteyneri, proxy'yi onu kullanan applicaiton servisine inject etmeyle ilgilenir.

    Örnek 4: REST Üzerinden Harici Bir Sistemle İletişim Kurma

    Dördüncü ve son örnekte, sistemimizin REST üzerinden harici bir sisteme istek yapmasını sağlayan bir adaptöre bakacağız:


    Application servisinin harici sisteme ulaşma ihtiyacı olduğu için bunun için kullanmak istediği bir arayüz ilan etmiştir. Bunu bir anti-corruption katmanının ilk bölümü olarak düşünebilirsiniz (ne olduğu konusunda tazelemeye ihtiyacınız varsa geri dönün ve stratejik DDD hakkındaki makaleyi okuyun).

    Adaptör daha sonra bu arabirimi uygulayarak anti-corruption katmanının ikinci bölümünü oluşturur. Önceki örnekte olduğu gibi, bağdaştırıcı, Spring gibi bir tür bağımlılık enjeksiyonu kullanılarak uygulama hizmetine enjekte edilir. Daha sonra, harici sisteme çağrı yapmak için bazı dahili HTTP istemcilerini kullanır ve alınan yanıtları, entegrasyon arabiriminin belirlediği şekilde domain nesnelerine çevirir.

    Çoklu Sınırlı Bağlamlar(Multiple Bounded Contexts)

    Şimdiye kadar sadece tek bir sınırlı bağlama uygulandığında altıgen mimarinin nasıl göründüğüne baktık. Ancak birbirleriyle iletişim kurmanız gereken birden fazla sınırlı bağlamınız olduğunda ne olur?

    Bağlamlar ayrı sistemlerde çalışıyorsa ve bir ağ üzerinden iletişim kuruyorsa, bunun gibi bir şey yapabilirsiniz: Yukarı akış sistemi için bir REST sunucu adaptörü ve aşağı akış sistemi için bir REST istemci adaptörü oluşturun:


    Farklı bağlamlar arasındaki eşleştirme, aşağı akış sisteminin adaptöründe gerçekleşecektir.

    Bağlamlar tek bir monolitik sistem içinde modüller olarak çalışıyorsa, yine de benzer bir mimari kullanabilirsiniz, ancak yalnızca tek bir adaptöre ihtiyacınız vardır:





    Her iki bağlam da aynı sanal makinenin içinde çalıştığından, her iki bağlamla doğrudan etkileşime giren yalnızca bir adaptöre ihtiyacımız var. Adaptör, aşağı akış bağlamının bağlantı noktası arabirimini uygular ve yukarı akış bağlamının bağlantı noktasını çağırır. Herhangi bir bağlam eşlemesi adaptörün içinde gerçekleşir.

    Sonraki: Domaine Dayalı Tasarım ve Spring Boot

    Bu dizinin bir sonraki ve son makalesinde, domaine dayalı tasarımı ve altıgen mimariyi kullanarak uygulamalar oluşturmak için Spring Boot'u nasıl kullanacağımızı öğreneceğiz.

    Çeviri için izin veren  Petter Holmström'e teşekkürler.



    Bonus :

    Barış Velioğlu
    Domain Driven Design Kimdir?

    Rastgele İçerik

    DonanımHaber

    © tüm hakları saklıdır
    made with by templateszoo