DDD, Hexagonal, Onion, Clean, CQRS, … Hepsi bir araya nasıl getirilir

@hgraca tarafından hazırlanan bu makalenin orjinal kaynağını bu linkten okuyabilirsiniz.

Editör’ün Notu: Türkçe çeviriyi onun anlatımı ile yapmaya gayret gösterdik. Makaleyi okurken buna göre okumanızı rica ederiz.

Bu gönderi, Yazılım Mimarisi hakkında bir dizi gönderi olan Yazılım Mimarisi Chronicles’ın bir parçasıdır. Onlarda Yazılım Mimarisi hakkında ne öğrendiğimi, nasıl düşündüğümü ve bu bilgiyi nasıl kullandığımı yazıyorum. Bu yazının içeriği, bu serideki önceki yazıları okursanız daha anlamlı olabilir.

Üniversiteden mezun olduktan sonra birkaç yıl öncesine kadar lise öğretmeni olarak bir kariyer takip ettim ve bunu bırakıp tam zamanlı bir yazılım geliştiricisi olmaya karar verdim.

O andan itibaren, her zaman “kayıp” zamanı geri kazanmam ve mümkün olduğunca çabuk, mümkün olduğunca çok şey öğrenmem gerektiğini hissettim. Bu yüzden, yazılım tasarımı ve mimarisine özel olarak odaklanarak, deneme, okuma ve yazma konusunda biraz bağımlı oldum. Bu yüzden bu yazıları öğrenmeme yardımcı olmak için yazıyorum.

Son yazılarımda, öğrendiğim birçok kavram ve ilke hakkında ve biraz da onlar hakkında nasıl akıl yürüttüğüm hakkında yazdım. Ama ben bunları büyük bir yapbozun parçaları olarak görüyorum.

Bugünkü yazım, tüm bu parçaları nasıl bir araya getirdiğimle ilgili ve görünüşe göre ona bir isim vermem gerekiyor, buna Açık Mimari diyorum . Ayrıca, bu kavramların tümü ” savaş denemelerini geçmiştir ” ve son derece zorlu platformlarda üretim kodunda kullanılmaktadır. Biri dünya çapında binlerce web-mağazası olan bir SaaS e-com platformu, diğeri ise ayda 20 milyondan fazla mesajı işleyen bir mesaj veriyoluna sahip 2 ülkede yaşayan bir pazar yeri.

  • Sistemin temel blokları
  • Aletler
  • Araçları ve teslim mekanizmalarını Uygulama Çekirdeğine bağlama
    • Limanlar
    • Birincil veya Sürüş Adaptörleri
    • İkincil veya Tahrikli Adaptörler
    • Kontrolün tersine çevrilmesi
  • Uygulama Çekirdek Organizasyonu
    • Uygulama katmanı
    • Etki Alanı Katmanı
      • Etki Alanı Hizmetleri
      • Etki Alanı Modeli
  • Bileşenler
    • Bileşenleri ayırma
      • Diğer bileşenlerde mantığı tetikleme
      • Diğer bileşenlerden veri alma
        • Bileşenler arasında paylaşılan veri depolama
        • Bileşen başına ayrılmış veri depolama
  • Kontrol akışı

Sistemin temel blokları

EBI ve Ports & Adapters mimarilerini hatırlayarak başlıyorum. Her ikisi de uygulamada hangi kodun dahili olduğunu, neyin harici olduğunu ve dahili ve harici kodu bağlamak için neyin kullanıldığını açık bir şekilde ayırır.

Ayrıca, Bağlantı Noktaları ve Adaptörler mimarisi, bir sistemdeki üç temel kod bloğunu açıkça tanımlar:

  • Kullanıcı arabiriminin türü ne olursa olsun, bir kullanıcı arabirimini çalıştırmayı mümkün kılan şey ;
  • Kullanıcı arabirimi tarafından bir şeylerin gerçekleşmesi için kullanılan sistem iş mantığı veya uygulama çekirdeği ;
  • Uygulama çekirdeğimizi veritabanı, arama motoru veya 3. taraf API’ler gibi araçlara bağlayan altyapı kodu.
000 - Açık Mimari.svg

Uygulama çekirdeği, gerçekten önemsememiz gereken şeydir. Kodumuzun yapması gerekeni yapmasını sağlayan koddur, bizim uygulamamızdır. Birkaç kullanıcı arayüzü kullanabilir (aşamalı web uygulaması, mobil, CLI, API, …) ancak aslında işi yapan kod aynıdır ve uygulama çekirdeğinde bulunur, hangi UI’nin onu tetiklediği önemli değildir.

Tahmin edebileceğiniz gibi, tipik uygulama akışı, kullanıcı arayüzündeki koddan, uygulama çekirdeğinden altyapı koduna, tekrar uygulama çekirdeğine gider ve son olarak kullanıcı arayüzüne bir yanıt verir.

010 - Açık Mimari.svg

Aletler

Sistemimizdeki en önemli kod olan uygulama çekirdeğinden çok uzakta, uygulamamızın kullandığı araçlara sahibiz, örneğin bir veritabanı motoru, bir arama motoru, bir Web sunucusu veya bir CLI konsolu (son ikisi aynı zamanda teslimat olmasına rağmen). mekanizmalar).

020 - Açık Mimari.svg

Bir CLI konsolunu bir veritabanı motoruyla aynı “kepçeye” koymak garip gelse de ve farklı amaçları olsa da, bunlar aslında uygulama tarafından kullanılan araçlardır. Temel fark, CLI konsolu ve web sunucusu uygulamamıza bir şey yapmasını söylemek için kullanılırken , veritabanı motoruna uygulamamız tarafından bir şey yapmasını söylemesidir . Bu, bu araçları uygulama çekirdeğine bağlayan kodu nasıl oluşturduğumuz konusunda güçlü etkileri olduğu için çok alakalı bir ayrımdır.

Araçları ve teslim mekanizmalarını Uygulama Çekirdeğine bağlama

Araçları uygulama çekirdeğine bağlayan kod birimlerine bağdaştırıcılar ( Bağlantı Noktaları ve Bağdaştırıcılar Mimarisi ) denir. Bağdaştırıcılar, iş mantığının belirli bir araçla iletişim kurmasını sağlayacak kodu etkin bir şekilde uygulayanlardır ve bunun tersi de geçerlidir.

Uygulamamıza bir şey yapmasını söyleyen adaptörlere Birincil veya Sürüş Adaptörleri , uygulamamız tarafından bir şey yapmasını söyleyenlere İkincil veya Tahrikli Adaptörler denir .

Limanlar (Ports)

Ancak bu Bağdaştırıcılar rastgele oluşturulmaz. Bir Port olan Uygulama Çekirdeğine çok özel bir giriş noktasına uyacak şekilde oluşturulurlar . Bağlantı noktası , aracın uygulama çekirdeğini nasıl kullanabileceğinin veya Uygulama Çekirdeği tarafından nasıl kullanıldığının belirtilmesinden başka bir şey değildir . Çoğu dilde ve en basit biçiminde, bu belirtim olan Bağlantı Noktası bir Arayüz olacaktır, ancak aslında birkaç Arayüz ve DTO’dan oluşabilir.

Bağdaştırıcılar dışarıya aitken Bağlantı Noktalarının (Arayüzlerin) iş mantığının içine ait olduğuna dikkat etmek önemlidir . Bu kalıbın olması gerektiği gibi çalışması için, Bağlantı Noktalarının yalnızca araç API’lerini taklit etmek yerine Uygulama Çekirdeği ihtiyaçlarına uyacak şekilde oluşturulması son derece önemlidir.

Birincil veya Sürüş Adaptörleri (Primary or Driving Adapters)

Birincil veya Sürücü Adaptörleri , bir Bağlantı Noktasının etrafına sarılır ve Uygulama Çekirdeğine ne yapacağını söylemek için kullanır. Teslimat mekanizmasından gelen her şeyi Uygulama Çekirdeğinde bir yöntem çağrısına çevirirler.

030 - Açık Architecture.svg

Başka bir deyişle, Sürüş Adaptörlerimiz, sınıfları denetleyici veya konsol komutunun gerektirdiği arabirimi (Port) uygulayan bazı nesnelerle yapıcılarına enjekte edilen Denetleyiciler veya Konsol Komutlarıdır.

Daha somut bir örnekte, bir Bağlantı Noktası, bir denetleyicinin gerektirdiği bir Hizmet arabirimi veya bir Depo arabirimi olabilir. Hizmetin, Deponun veya Sorgunun somut uygulaması daha sonra Denetleyiciye enjekte edilir ve kullanılır.

Alternatif olarak, bir Bağlantı Noktası, Komut Veri Yolu veya Sorgu Veri Yolu arabirimi olabilir. Bu durumda, Komut veya Sorgu Veri Yolu’nun somut bir uygulaması, daha sonra bir Komut veya Sorgu oluşturan ve bunu ilgili Veri Yoluna ileten Kontrolöre enjekte edilir.

İkincil veya Tahrikli Adaptörler (Secondary or Driven Adapters)

Bir bağlantı noktasının etrafını saran Sürücü Adaptörlerinin aksine, Driven Adaptörler bir Bağlantı Noktası , bir arabirim uygular ve ardından bağlantı noktasının gerekli olduğu her yerde (tip ipuçlarıyla) Uygulama Çekirdeğine enjekte edilir.

040 - Açık Mimari.svg

Örneğin, verileri sürdürmesi gereken saf bir uygulamamız olduğunu varsayalım. Bu nedenle, bir dizi veriyi kaydetme yöntemi ve bir tablodaki bir satırı kimliğine göre silme yöntemiyle ihtiyaçlarını karşılayan bir kalıcılık arabirimi oluşturuyoruz . O andan itibaren, uygulamamızın verileri kaydetmesi veya silmesi gerektiğinde, yapıcısında tanımladığımız kalıcılık arabirimini uygulayan bir nesneye ihtiyacımız olacak.

Şimdi bu arayüzü uygulayacak MySQL’e özel bir adaptör oluşturuyoruz. Bir diziyi kaydetme ve bir tablodaki bir satırı silme yöntemlerine sahip olacak ve bunu kalıcılık arayüzünün gerekli olduğu her yere enjekte edeceğiz.

Bir noktada veritabanı satıcısını değiştirmeye karar verirsek, diyelim ki PostgreSQL veya MongoDB’ye, kalıcılık arabirimini uygulayan ve PostgreSQL’e özgü yeni bir bağdaştırıcı oluşturmamız ve eskisi yerine yeni bağdaştırıcıyı enjekte etmemiz yeterlidir.

Kontrolün tersine çevrilmesi (Inversion of control)

Bu modelle ilgili dikkat edilmesi gereken bir özellik, bağdaştırıcıların belirli bir araca ve belirli bir bağlantı noktasına (bir arabirim uygulayarak) bağlı olmasıdır. Ancak iş mantığımız yalnızca iş mantığı gereksinimlerine uyacak şekilde tasarlanmış bağlantı noktasına (arayüze) bağlıdır, bu nedenle belirli bir adaptöre veya araca bağlı değildir.

050 - Açık Mimari.svg

Bu, bağımlılıkların yönünün merkeze doğru olduğu anlamına gelir, bu , mimari düzeyde kontrol ilkesinin tersine çevrilmesidir .

Yine de, Bağlantı Noktalarının Uygulama Çekirdeği gereksinimlerine uyacak şekilde oluşturulması ve yalnızca araç API’lerini taklit etmemesi son derece önemlidir.

Uygulama Çekirdek organizasyonu (Application Core organisation)

Soğan Mimarisi , DDD katmanlarını alır ve bunları Bağlantı Noktaları ve Adaptörler Mimarisi ile birleştirir . Bu katmanlar, iş mantığına, Bağlantı Noktaları ve Adaptörler “altıgeninin” iç kısmına bir düzenleme getirmeyi amaçlamaktadır ve tıpkı Bağlantı Noktaları ve Adaptörlerde olduğu gibi, bağımlılık yönü merkeze doğrudur.

Uygulama katmanı (Application Layer)

Kullanım durumları, uygulamamızdaki bir veya birkaç Kullanıcı Arayüzü tarafından Uygulama Çekirdeğimizde tetiklenebilen süreçlerdir. Örneğin, bir CMS’de ortak kullanıcılar tarafından kullanılan gerçek uygulama kullanıcı arayüzüne, CMS yöneticileri için başka bir bağımsız kullanıcı arayüzüne, başka bir CLI kullanıcı arayüzüne ve bir web API’sine sahip olabiliriz. Bu UI’ler (uygulamalar), bunlardan birine özgü olabilen veya birkaçı tarafından yeniden kullanılabilen kullanım durumlarını tetikleyebilir.

Kullanım durumları, DDD tarafından sağlanan ve Onion Architecture tarafından kullanılan ilk katman olan Uygulama Katmanında tanımlanır.

060 - Açık Mimari.svg

Bu katman, birinci sınıf vatandaşlar olarak Uygulama Hizmetlerini (ve arayüzlerini) içerir, ancak aynı zamanda ORM arayüzlerini, arama motorları arayüzlerini, mesajlaşma arayüzlerini vb. içeren Bağlantı Noktaları ve Adaptörler arayüzlerini (portlar) içerir. Komut Veri Yolu ve/veya Sorgu Veri Yolu kullandığımız durumda, bu katman Komutlar ve Sorgular için ilgili İşleyicilerin ait olduğu katmandır.

Uygulama Hizmetleri ve/veya Komut İşleyicileri, bir kullanım senaryosunu, bir iş sürecini ortaya çıkarma mantığını içerir. Tipik olarak, rolleri:

  1. bir veya birkaç varlığı bulmak için bir havuz kullanın;
  2. bu varlıklara bazı etki alanı mantığı yapmalarını söyleyin;
  3. ve veri değişikliklerini etkin bir şekilde kaydederek varlıkları tekrar sürdürmek için depoyu kullanın.

Komut İşleyicileri iki farklı şekilde kullanılabilir:

  1. Kullanım senaryosunu gerçekleştirmek için gerçek mantığı içerebilirler;
  2. Mimarimizde sadece kablolama parçaları olarak kullanılabilirler, bir Komut alırlar ve bir Uygulama Hizmetinde var olan mantığı basitçe tetiklerler.

Hangi yaklaşımın kullanılacağı bağlama bağlıdır, örneğin:

  • Halihazırda Uygulama Hizmetlerine sahip miyiz ve şimdi bir Komut Veri Yolu ekliyor muyuz?
  • Komut Veri Yolu, işleyici olarak herhangi bir sınıf/yöntem belirlemeye izin veriyor mu, yoksa mevcut sınıfları veya arabirimleri genişletmeleri veya uygulamaları gerekiyor mu?

Bu katman ayrıca , bir kullanım durumunun bazı sonuçlarını temsil eden Uygulama Olaylarının tetiklenmesini de içerir . Bu olaylar, e-posta göndermek, 3. taraf API’yi bilgilendirmek, anında iletme bildirimi göndermek ve hatta uygulamanın farklı bir bileşenine ait başka bir kullanım senaryosu başlatmak gibi bir kullanım durumunun yan etkisi olan mantığı tetikler.

Etki Alanı Katmanı (Domain Layer)

Daha içe doğru, Etki Alanı Katmanına sahibiz. Bu katmandaki nesneler, Etki Alanının kendisine özgü olan verileri ve bu verileri işlemek için mantığı içerir ve bu mantığı tetikleyen iş süreçlerinden bağımsızdır, bağımsızdırlar ve Uygulama Katmanından tamamen habersizdirler.

070 - Açık Mimari.svg

Etki Alanı Hizmetleri (Domain Services)

Yukarıda bahsettiğim gibi, bir Uygulama Hizmetinin rolü şudur:

  1. bir veya birkaç varlığı bulmak için bir havuz kullanın;
  2. bu varlıklara bazı etki alanı mantığı yapmalarını söyleyin;
  3. ve veri değişikliklerini etkin bir şekilde kaydederek varlıkları tekrar sürdürmek için depoyu kullanın.

Ancak, bazen aynı türden veya olmayan farklı varlıkları içeren bir alan mantığıyla karşılaşıyoruz ve bu alan mantığının varlıkların kendilerine ait olmadığını hissediyoruz, bu mantığın doğrudan onların sorumluluğu olmadığını hissediyoruz.

Dolayısıyla ilk tepkimiz, bu mantığı varlıkların dışına, bir Uygulama Hizmetine yerleştirmek olabilir. Ancak bu, etki alanı mantığının diğer kullanım durumlarında yeniden kullanılamayacağı anlamına gelir: etki alanı mantığı, uygulama katmanının dışında kalmalıdır!

Çözüm, bir dizi varlık alma ve bunlar üzerinde bazı iş mantığı gerçekleştirme rolüne sahip bir Etki Alanı Hizmeti oluşturmaktır. Bir Etki Alanı Hizmeti, Etki Alanı Katmanına aittir ve bu nedenle Uygulama Hizmetleri veya Depolar gibi Uygulama Katmanındaki sınıflar hakkında hiçbir şey bilmez. Diğer yandan diğer Domain Servislerini ve tabii ki Domain Model nesnelerini kullanabilir.

Etki Alanı Modeli (Domain Model)

En merkezde, onun dışında hiçbir şeye bağlı olmaksızın, etki alanındaki bir şeyi temsil eden iş nesnelerini içeren Etki Alanı Modeli bulunur. Bu nesnelerin örnekleri, her şeyden önce Varlıklar, ayrıca Değer Nesneleri, Numaralandırmalar ve Etki Alanı Modelinde kullanılan nesnelerdir.

Etki Alanı Modeli ayrıca Etki Alanı Olaylarının “yaşadığı” yerdir. Bu olaylar, belirli bir veri kümesi değiştiğinde tetiklenir ve bu değişiklikleri yanlarında taşırlar. Başka bir deyişle, bir varlık değiştiğinde, bir Etki Alanı Olayı tetiklenir ve değiştirilen özellikleri yeni değerler taşır. Bu olaylar, örneğin Olay Kaynak Kullanımında kullanılmak için mükemmeldir.

Bileşenler (Components)

Şimdiye kadar kodu katmanlara göre ayırdık, ancak bu, ince taneli kod ayrımıdır. Kodun kaba taneli ayrımı en az aynı derecede önemlidir ve bu , Robert C. Martin’in çığır açan mimaride ifade edilen fikirlerini izleyerek kodu alt alanlara ve sınırlı bağlamlara göre ayırmakla ilgilidir . Bu genellikle ” Katmana göre paket ” yerine ” Özelliğe göre paket ” veya ” Bileşene göre paket ” olarak adlandırılır ve Simon Brown tarafından ” Bileşene göre paket ve mimari olarak hizalanmış test ” blog yazısında oldukça iyi açıklanmıştır :

20150308-katman paket
20150308-özelliklere göre paket
20150308-bileşene göre paket

“ Bileşene göre paket ” yaklaşımının bir savunucusuyum ve bileşene göre Paket ile ilgili Simon Brown şemasını alarak , utanmadan aşağıdaki şekilde değiştirirdim:

Bu kod bölümleri, daha önce açıklanan katmanlarla kesişir, bunlar uygulamamızın bileşenleridir . Bileşen örnekleri şunlar olabilir Kimlik Doğrulama, Yetkilendirme, Faturalandırma, Kullanıcı, İnceleme veya Hesap, ancak bunlar her zaman alanla ilgilidir. Yetkilendirme ve/veya Kimlik Doğrulama gibi sınırlı bağlamlar, bir bağdaştırıcı oluşturduğumuz ve bir tür bağlantı noktasının arkasına saklandığımız harici araçlar olarak görülmelidir.

080 - Açık Mimari.svg

Bileşenleri ayırma (Decoupling the components)

Tıpkı ince taneli kod birimleri (sınıflar, arayüzler, özellikler, karışımlar, …) gibi, kaba taneli kod birimleri (bileşenler) de düşük eşleşme ve yüksek uyumdan yararlanır.

Sınıfları ayırmak için, bağımlılıkları sınıf içinde somutlaştırmak yerine bir sınıfa enjekte ederek Bağımlılık Enjeksiyonunu ve sınıfı somut sınıflar yerine soyutlamalara (arayüzlere ve/veya soyut sınıflara) bağımlı hale getirerek Bağımlılık Tersine Çevirmeyi kullanırız. Bu, bağımlı sınıfın kullanacağı somut sınıf hakkında bilgisi olmadığı, bağlı olduğu sınıfların tam nitelikli sınıf adına referansı olmadığı anlamına gelir.

Aynı şekilde, tamamen ayrılmış bileşenlere sahip olmak, bir bileşenin başka bir bileşen hakkında doğrudan bilgisi olmadığı anlamına gelir. Başka bir deyişle, arayüzlerden bile başka bir bileşenden herhangi bir ince taneli kod birimine referansı yoktur! Bu, Dependency Injection ve Dependency Inversion’ın bileşenleri ayırmak için yeterli olmadığı, bir tür mimari yapılara ihtiyacımız olacağı anlamına gelir. Olaylara, paylaşılan bir çekirdeğe, nihai tutarlılığa ve hatta bir keşif hizmetine ihtiyacımız olabilir!

Diğer bileşenlerde mantığı tetikleme (Triggering logic in other components)

Bileşenlerimizden birinin (bileşen B) başka bir bileşende (bileşen A) başka bir şey olduğunda bir şey yapması gerektiğinde, A bileşeninden B bileşenindeki bir sınıfa/yönteme doğrudan bir çağrı yapamayız çünkü o zaman A birleştirilir. B.’ye

Bununla birlikte, B dahil olmak üzere kendisini dinleyen herhangi bir bileşene teslim edilecek bir uygulama olayını göndermek için A’nın bir olay dağıtıcısı kullanmasını sağlayabiliriz ve B’deki olay dinleyicisi istenen eylemi tetikleyecektir. Bu, A bileşeninin bir olay göndericiye bağlı olacağı, ancak B’den ayrılacağı anlamına gelir.

Yine de, olayın kendisi A’da “yaşıyorsa” bu, B’nin A’nın varlığından haberdar olduğu, A ile eşleştiği anlamına gelir. Bu bağımlılığı ortadan kaldırmak için, aralarında paylaşılacak bir dizi uygulama temel işlevine sahip bir kitaplık oluşturabiliriz. tüm bileşenler, Paylaşılan Çekirdek. Bu, bileşenlerin her ikisinin de Paylaşılan Çekirdeğe bağlı olacağı, ancak birbirlerinden ayrılacakları anlamına gelir. Paylaşılan Çekirdek, uygulama ve etki alanı olayları gibi işlevler içerecektir, ancak aynı zamanda Belirtim nesnelerini ve paylaşılması mantıklı olan her şeyi içerebilir; Paylaşımlı Çekirdekte yapılacak herhangi bir değişiklik, tüm bileşenlerini etkileyeceğinden, mümkün olduğunca az olması gerektiğini unutmayın. uygulama. Ayrıca, çok dilli bir sistemimiz varsa, diyelim ki farklı dillerde yazıldığı bir mikro hizmetler ekosistemi varsa, Paylaşılan Çekirdeğin dilden bağımsız olması gerekir ki, hangi dilde yazılmış olursa olsun tüm bileşenler tarafından anlaşılabilsin. Örneğin, bir Event sınıfı içeren Paylaşılan Çekirdek yerine, olay açıklamasını (yani ad, özellikler, Belki de bunlar bir Spesifikasyon nesnesinde daha yararlı olsa da yöntemler) JSON gibi agnostik bir dilde, böylece tüm bileşenler/mikro hizmetler bunu yorumlayabilir ve hatta kendi somut uygulamalarını otomatik olarak oluşturabilir. Takip yazımda bununla ilgili daha fazla bilgi edinin:Konsantrik katmanlardan daha fazlası .

Explicti_arch_layers

Bu yaklaşım hem monolitik uygulamalarda hem de mikro hizmet ekosistemleri gibi dağıtılmış uygulamalarda çalışır. Ancak, olaylar yalnızca eşzamansız olarak iletilebildiğinde, diğer bileşenlerde tetikleme mantığının hemen yapılması gereken bağlamlar için bu yaklaşım yeterli olmayacaktır! Bileşen A’nın, bileşen B’ye doğrudan bir HTTP çağrısı yapması gerekecektir. Bu durumda, bileşenlerin ayrıştırılması için, A’nın istenen eylemi tetiklemek için isteği nereye göndermesi gerektiğini soracağı veya alternatif olarak ilgili hizmete vekalet edebilen ve sonunda istek sahibine bir yanıt geri gönderebilen keşif hizmetine yapılan istek. Bu yaklaşım, bileşenleri keşif hizmetine bağlayacak, ancak birbirlerinden ayrılmalarını sağlayacaktır.

Diğer bileşenlerden veri alma (Getting data from other components)

Gördüğüm kadarıyla, bir bileşenin “sahip olmadığı” verileri değiştirmesine izin verilmiyor, ancak herhangi bir veriyi sorgulaması ve kullanması sorun değil.

Bileşenler arasında paylaşılan veri depolama (Data storage shared between components)

Bir bileşenin başka bir bileşene ait verileri kullanması gerektiğinde, diyelim ki bir faturalandırma bileşeninin hesaplar bileşenine ait müşteri adını kullanması gerektiğinde, faturalama bileşeni, bu veriler için veri deposunu sorgulayacak bir sorgu nesnesi içerecektir. Bu basitçe, faturalandırma bileşeninin herhangi bir veri kümesi hakkında bilgi sahibi olabileceği, ancak “sahip olmadığı” verileri sorgular aracılığıyla salt okunur olarak kullanması gerektiği anlamına gelir.

Bileşen başına ayrılmış veri depolama (Data storage segregated per component)

Bu durumda, aynı kalıp geçerlidir, ancak veri depolama düzeyinde daha fazla karmaşıklığa sahibiz. Bileşenlerin kendi veri depolamasına sahip olması, her veri depolamasının şunları içerdiği anlamına gelir:

  • Sahip olduğu ve değiştirmesine izin verilen tek veri kümesidir, bu da onu gerçeğin tek kaynağı yapar;
  • Kendi başına değiştiremeyeceği, ancak bileşen işlevselliği için gerekli olan ve sahip bileşeninde her değiştiğinde güncellenmesi gereken diğer bileşen verilerinin bir kopyası olan bir veri kümesi.

Her bileşen, gerektiğinde kullanılmak üzere diğer bileşenlerden ihtiyaç duyduğu verilerin yerel bir kopyasını oluşturacaktır. Sahip olduğu bileşendeki veriler değiştiğinde, o sahip bileşen, veri değişikliklerini taşıyan bir etki alanı olayını tetikler. Bu verilerin bir kopyasını tutan bileşenler, o etki alanı olayını dinleyecek ve yerel kopyalarını buna göre güncelleyecektir.

Kontrol akışı (Flow of control)

Yukarıda söylediğim gibi, kontrol akışı elbette kullanıcıdan Uygulama Çekirdeğine, oradan altyapı araçlarına, tekrar Uygulama Çekirdeğine ve son olarak da kullanıcıya geri döner. Fakat sınıflar tam olarak nasıl bir araya geliyor? Hangileri hangisine bağlı? Onları nasıl oluştururuz?

Bob Amca’nın ardından Clean Architecture ile ilgili yazısında kontrol akışını UMLish diyagramları ile açıklamaya çalışacağım…

Komut/Sorgu Veri Yolu Olmadan (Without a Command/Query Bus)

Bir komut veri yolu kullanmamamız durumunda, Denetleyiciler ya bir Uygulama Hizmetine ya da bir Sorgu nesnesine bağlı olacaktır.

EDIT – 2017-11-18 ] Sorgudan veri döndürmek için kullandığım DTO’yu tamamen kaçırdım, bu yüzden şimdi ekledim. Benim için işaret eden MorphineAdministered’a Tkx .

Yukarıdaki şemada, Uygulama Hizmeti için bir arabirim kullanıyoruz, ancak Uygulama Hizmetinin uygulama kodumuzun bir parçası olduğu için gerçekten gerekli olmadığını iddia edebiliriz ve yeniden düzenlememize rağmen başka bir uygulama için değiştirmek istemeyebiliriz. Baştan sona.

Query nesnesi, kullanıcıya gösterilecek bazı ham verileri basitçe döndürecek optimize edilmiş bir sorgu içerecektir. Bu veriler, bir ViewModel’e enjekte edilecek olan bir DTO’da döndürülecektir. ThisViewModel’in içinde bir görünüm mantığı olabilir ve bir Görünümü doldurmak için kullanılacaktır.

Uygulama Hizmeti ise, sadece bazı verileri görüntülemek yerine, sistemde bir şey yapmak istediğimizde tetikleyeceğimiz kullanım senaryosu mantığını içerecektir. Uygulama Hizmetleri, tetiklenmesi gereken mantığı içeren Varlık(lar)ı döndürecek Depolara bağlıdır. Ayrıca, birkaç varlıkta bir etki alanı sürecini koordine etmek bir Etki Alanı Hizmetine bağlı olabilir, ancak durum neredeyse hiç böyle değildir.

Kullanım senaryosunu açtıktan sonra, Uygulama Hizmeti tüm sisteme bu kullanım senaryosunun gerçekleştiğini bildirmek isteyebilir; bu durumda, olayı tetiklemek için bir olay göndericiye de bağlı olacaktır.

Arayüzleri hem kalıcılık motoruna hem de depolara yerleştirdiğimizi belirtmek ilginçtir. Gereksiz görünse de, farklı amaçlara hizmet ederler:

  • Kalıcılık arayüzü, ORM üzerinde bir soyutlama katmanıdır, böylece kullanılan ORM’yi Uygulama Çekirdeğinde hiçbir değişiklik yapmadan değiştirebiliriz.
  • Depo arayüzü, kalıcılık motorunun kendisinde bir soyutlamadır. Diyelim ki MySQL’den MongoDB’ye geçmek istiyoruz. Kalıcılık arabirimi aynı olabilir ve aynı ORM’yi kullanmaya devam etmek istiyorsak kalıcılık bağdaştırıcısı bile aynı kalacaktır. Ancak, sorgu dili tamamen farklıdır, bu nedenle aynı kalıcılık mekanizmasını kullanan, aynı depo arayüzlerini uygulayan ancak sorguları SQL yerine MongoDB sorgu dilini kullanarak oluşturan yeni depolar oluşturabiliriz.

Komut/Sorgu Veri Yolu ile (With a Command/Query Bus)

Uygulamamızın bir Komut/Sorgu Veri Yolu kullanması durumunda, denetleyicinin artık Veri Yoluna ve bir komuta veya bir Sorguya bağlı olması dışında, diyagram hemen hemen aynı kalır. Komutu veya Sorguyu başlatacak ve komutu almak ve işlemek için uygun işleyiciyi bulacak olan Bus’a iletecektir.

Aşağıdaki şemada, Komut İşleyici daha sonra bir Uygulama Hizmeti kullanır. Ancak, bu her zaman gerekli değildir, aslında çoğu durumda işleyici, kullanım senaryosunun tüm mantığını içerecektir. Aynı mantığı başka bir işleyicide yeniden kullanmamız gerekiyorsa, mantığı yalnızca işleyiciden ayrı bir Uygulama Hizmetine çıkarmamız gerekir.

EDIT – 2017-11-18 ] Sorgudan veri döndürmek için kullandığım DTO’yu tamamen kaçırdım, bu yüzden şimdi ekledim. Benim için işaret eden MorphineAdministered’a Tkx .

Bus ile Komut, Sorgu veya İşleyiciler arasında bir bağımlılık olmadığını fark etmiş olabilirsiniz. Bunun nedeni, aslında iyi bir ayrışma sağlamak için birbirlerinden habersiz olmaları gerektiğidir. Bus’ın hangi İşleyicinin hangi Komutu veya Sorguyu işlemesi gerektiğini bilme şekli, yalnızca yapılandırma ile kurulmalıdır.

Gördüğünüz gibi, her iki durumda da uygulama çekirdeğinin sınırını geçen tüm oklar, bağımlılıklar içe dönüktür. Daha önce açıklandığı gibi, bu, Bağlantı Noktaları ve Adaptörler Mimarisi, Soğan Mimarisi ve Temiz Mimari’nin temel kuralıdır.

Çözüm (Conclusion)

Amaç, her zaman olduğu gibi, değişikliklerin kolay, hızlı ve güvenli olması için gevşek bağlı ve yüksek düzeyde uyumlu bir kod tabanına sahip olmaktır.

Planlar değersizdir, ancak planlama her şeydir.

Eisenhower

Bu infografik bir kavram haritasıdır. Tüm bu kavramları bilmek ve anlamak sağlıklı bir mimari, sağlıklı bir uygulama için plan yapmamıza yardımcı olacaktır.

Yine de:

Harita bölge değil.

Alfred Korzybski

Yani bunlar sadece yönergeler! Uygulama, bilgimizi uygulamamız gereken bölge, gerçeklik, somut kullanım durumudur ve gerçek mimarinin nasıl görüneceğini tanımlayacak olan budur!

Tüm bu kalıpları anlamamız gerekiyor, ancak aynı zamanda her zaman uygulamamızın tam olarak neye ihtiyacı olduğunu, ayrıştırma ve uyum için ne kadar ileri gitmemiz gerektiğini düşünmemiz ve anlamamız gerekiyor. Bu karar, projenin işlevsel gereksinimlerinden başlayarak pek çok faktöre bağlı olabilir, ancak uygulamayı oluşturmak için zaman çerçevesi, uygulamanın ömrü, geliştirme ekibinin deneyimi vb. gibi faktörleri de içerebilir.

İşte bu, ben her şeyi böyle anlamlandırıyorum. Bunu kafamda bu şekilde rasyonelleştiriyorum.

Bu fikirleri bir takip gönderisinde biraz daha genişlettim: Konsantrik katmanlardan daha fazlası .

Ancak, tüm bunları kod tabanında nasıl açık hale getirebiliriz? Sonraki gönderilerimden birinin konusu bu: kodda mimari ve etki alanı nasıl yansıtılır.

Son olarak, iş arkadaşım Francesco Mastrogiacomo’ya infografiğimi güzel göstermeme yardım ettiği için teşekkürler.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak.

This site uses Akismet to reduce spam. Learn how your comment data is processed.