Kısım II · İnşaat · Bölüm 44

Mimariler

Parçaları, sınırları ve değişimin bedelini tasarlamak (Software architecture: monolith, microservices, layers)

Neden burada · Neye dayanıyor

Neden burada: Bölüm 43: Yayına Alma ve Bulut uygulamayı nerede ve nasıl çalıştıracağımızı kurdu. Şimdi bildiğimiz parçaların hangi sınırlarla bir bütün olacağını soruyoruz. Aynı yığın, tek bir iyi düzenlenmiş program da olabilir, birbirine ağla bağlı çok sayıda hizmet de; farkı isim değil bedeller belirler.

Neye dayanıyor: Bölüm 27: Modüler Kod içindeki modül, bağlaşım, uyum ve bilgi gizleme; Bölüm 34: Arka Uç ve API içindeki istemci–sunucu sınırı; Bölüm 37: Go ile API Yazmak içindeki işleyici–servis–depo yolu. Bölüm 39: Postgres Pratikte işlem ve veri, Bölüm 40: Önbellek ve Redis önbellek ve iki yere yazma sorusu, Bölüm 41: Kimlik Doğrulama ve Güvenlik güven, Bölüm 42: Test ve Kalite test ve Bölüm 43: Yayına Alma ve Bulut yayın/gözlem kararlarını birlikte kullanacağız.

  1. Kat 10Bizim uygulamamızUygulamanın mantıksal ve yayın sınırları. Ağ çağrısı Kat 8'e, süreç ve yalıtım Kat 7'ye, veri ve hesap Kat 3'e dayanır; çizimdeki kutular bu katların üstünde anlam kazanır. Kat planına dön.
  2. Kat 9Web ve tarayıcıWeb istemcisi sunucunun sözleşmesini kullanır.
  3. Kat 8Ağ ve internetHTTP istekleri, DNS ve TLS; parçalar ayrı makinelerdeyken aradaki yol.
  4. Kat 7İşletim sistemiSüreçler, dosyalar ve izinler; altta Kat 3'ün işlemcisi ve belleği.

Bölüm sonunda monolitle tek makineyi karıştırmayacak, mikroservisin hangi sorunu hangi bedelle çözdüğünü söyleyecek, katmanları yayın birimlerinden ayıracak ve bir kuyruğun iki yere yazma sorununu neden kendiliğinden çözmediğini açıklayabileceksin. Sonunda aynı parçaları yük arttığında nasıl düşündüğümüzü göreceğiz.

44.1 Mimari nedir: değiştirmesi pahalı kararlar

Bölüm 27: Modüler Kod bir dosyanın ve modülün sınırını, Bölüm 43: Yayına Alma ve Bulut çalıştırma ortamının sınırını gösterdi. Yazılım mimarisi (software architecture), parçaların nasıl bir bütün oluşturduğuna ve bu bütünün önemli tasarım kararlarına bakar. Bunu bu kitapta “değiştirmesi pahalı kararlar” diye okuyacağız. Bu, tek kabul edilmiş matematiksel tanım değil; Fowler'ın mimari tartışmasındaki önem ve değişim maliyeti fikrini kullanışlı hale getiren bir bakış açısıdır.1

Hangi verinin asıl kayıt sayılacağı, iş kurallarının nerede yaşayacağı, parçaların aynı süreçte mi ayrı hizmetlerde mi çalışacağı ve bir değişikliğin kaç parçayı birlikte değiştirmeyi gerektireceği mimari sorulardır. Bir düğmenin rengi de tasarımdır ama çoğu durumda bu ölçekte bir karar değildir. Önem bağlama bağlıdır: ürünün kimlik ve veri modelini değiştirmek, yalnız bir değişkenin adını değiştirmekten farklı bir etki zinciri taşır.1

Çizimdeki her kutu makine değildir

Bir kutu bir modülü, bir süreci, bir veri deposunu ya da bir ekip sorumluluğunu gösterebilir. Bu bölümde üç ayrı sınırı adlandıracağız: mantıksal sınır kodun neyi bildiği; süreç sınırı hangi çalışan programın hangi belleğe sahip olduğu; yayın sınırı hangi parçanın bağımsız sürüm olarak dağıtılabildiği. Bunlar sık sık örtüşebilir ama eş anlamlı değildir.24

KararHemen kazandırdığıSonraki değişimde sorulacak
Puanın asıl kaydı Postgres'teTek yetkili veri noktasıBaşka yerdeki kopyalar nasıl güncellenecek?
İş kuralı HTTP'den ayrıAynı kuralı farklı girişlerden kullanabilmekYeni giriş aynı servis sözleşmesine uyuyor mu?
Arka uç tek yayın birimiBirlikte geliştirme ve yayın sadeliğiBağımsız yayın gereksinimi gerçekten oluştu mu?

Bu tablo evrensel reçete değil, uygulamamız için örnek karar haritasıdır. Bir kararın hem faydasını hem bedelini söylemek, yalnız bir mimari adını seçmekten daha çok şey anlatır.13

Henüz bilinmeyen sınırları ağ üzerinden katılaştırmak, yanlış bölünmeyi düzeltmenin maliyetini artırabilir. “Büyürsek lazım olur” tek başına güçlü gerekçe değildir; Fowler'ın Monolith First yazısını bu temkinle okuyacağız.3

44.2 Monolit ve modüler monolit

Monolit (monolith), bu bağlamda sunucu tarafının tek bir mantıksal yayın birimi olarak oluşturulmasıdır. Kullanıcı, soru ve puan aynı Go programında bulunabilir. Bu kelime “bütün kod tek dosyada” veya “bütün sistem tek makinede” demek değildir. Ayrı Expo istemcisi, Postgres ve Redis kullanmak da Go arka ucunu kendiliğinden mikroservis mimarisi yapmaz.2

Modüler monolit (modular monolith), bu tek yayın biriminin içinde anlaşılır sorumluluk sınırları korur. Kimlik, içerik ve öğrenme ilerlemesi ayrı modüller olabilir. Bir modül ötekinin iç veri yapılarına gelişigüzel erişmek yerine tanımlı arayüzünü kullanır. Tek süreçte olmak modüllerin ayrıntılarını birbirine açmayı zorunlu kılmaz; Bölüm 27: Modüler Kod içindeki bilgi gizleme ve gevşek bağlaşım burada sistem ölçeğine taşınır.32

Tek Go yayın birimi
├─ kimlik: hesabı ve oturumu tanır
├─ içerik: ders ve soruları sunar
├─ öğrenme: cevap, puan, seri ve can kurallarını yürütür
└─ veri erişimi: ilgili sınırlar üzerinden Postgres / Redis ile konuşur

Çizim bir öneridir; var olan prototipin klasör yapısı değildir. İlk satır birlikte yayımlanmayı, alt satırlar mantıksal sorumlulukları gösterir. Modüller aynı programda fonksiyon çağrılarıyla konuşabilir. Bu, ağ isteği, uzak hizmet sürümü ve her çağrıda ağ zaman aşımı yönetimi gerektirmeyen bir başlangıç sağlar. Ağla konuşan dış veri hizmetleri yine vardır; sadelik iddiası “hiç dış bağımlılık yok” değildir.2

Neden başlangıç için düşünülür?

Yeni ürünün sınırları henüz değişkendir. “Can” hesabı ilerleme modülünün mü, ayrı bir ödül hizmetinin mi işi olacak, kullanıcı deneyiminden öğrenebiliriz. Aynı kod tabanında bu sınırı değiştirmek, farklı yayın birimleri ve veri sözleşmeleri arasında taşımaktan daha az işletim koordinasyonu gerektirebilir. Tek yayın hattı ve tek sunucu tarafı hata inceleme noktası da küçük ekip için daha az ek yük demektir.3

İki kişilik ekibimiz için bu bağlama dayalı öneridir. Fowler'ın gözlemini evrensel yasa saymıyoruz; yazı, kendi kanıtının anekdot niteliğini de tartışır. Sınırlar ve işletim ihtiyaçları değişince karar yeniden değerlendirilir.3

Bedeli nerede ortaya çıkar?

Tek yayın biriminde küçük bir değişiklik için bütün programın yeni sürümü dağıtılır. Bir modülün kaynak gereksinimi diğerinden farklıysa yalnız o modülü ayrı ölçeklemek kolay değildir. Kötü sınırlar birikirse ortak kod tabanı birbirini etkileyen değişiklikler ağına dönüşebilir. Fakat bunu düzeltmenin ilk yolu mutlaka ağ eklemek değildir: önce modüllerin hangi bilgiyi paylaştığını ve neden birlikte değiştiğini anlamak gerekir.23

Özellikle şu yanlış çıkarımı bırak: “Monolit tek kopya çalışır.” Aynı Go ikili dosyasını birden fazla süreçte ve makinede başlatabilirsin. Ortak durumu ve yük dengeleyiciyi 44.6'da açacağız. Kodun yayın biriminin tek olması, çalışan örnek sayısının tek olması değildir.2

44.3 Mikroservisler: faydası, bedeli ve ne zaman

Mikroservisler (microservices), uygulamayı iş yetenekleri çevresinde kurulmuş, bağımsız yayımlanabilen hizmetlere ayırma yaklaşımıdır. Hizmetler kendi süreçlerinde çalışır ve HTTP gibi ağ sözleşmeleriyle ya da mesajlarla konuşabilir. “Mikro” için bütün projelerde geçerli satır sayısı yoktur; önemli fikir küçük dosya değil, anlamlı sorumluluk ve bağımsız değişim sınırıdır.2

Örneğin içerik yayımlama ayrı bir ekip ve ayrı yayın ritmi gerektiriyorsa içerik hizmeti ayrılması değerlendirilebilir. Çok farklı iş yükü olan bir parça ayrıca ölçeklenebilir. Her servis kendi ihtiyacına uygun araç kullanabilir. Ancak “kullanabilir” ile “kullanmalı” farklıdır: her servise başka dil ve veritabanı eklemek öğrenme ve işletim yükünü de büyütür. Bağımsızlık, çeşitlilik yarışması değildir.2

Olası faydaKarşılığında gelen yük
Bağımsız yayınSözleşme uyumluluğu ve ayrı yayın hatları
Bir iş yeteneğini ayrı ölçeklemeAyrı ölçüm, kapasite ve hata yönetimi
Ekip sınırlarına uygun sahiplikServisler arası koordinasyon ve veri sorumluluğu
Bir arızayı sınırlandırma imkânıArızanın ağ çağrılarıyla yayılmasını önleme ihtiyacı

Bunlar otomatik kazanımlar değildir. Bağımsızlık için tasarlanmadığında, çok servisli sistem de tek parça gibi birlikte değiştirilmek zorunda kalabilir.2

Fonksiyon çağrısından ağ çağrısına geçmek

Aynı süreçteki çağrıya göre ağ çağrısında ek gecikme ve kısmi başarısızlık vardır. İstek gitmiş fakat yanıt dönmemiş olabilir. Tekrar gönderirsek işlemin iki kez yapılmaması gerekir. Bölüm 35: HTTP, JSON ve REST içindeki idempotentlik ve Bölüm 37: Go ile API Yazmak içindeki zaman aşımı burada mimarinin günlük işi olur. Bölüm 43: Yayına Alma ve Bulut içindeki izler de tek isteğin birden çok hizmetteki yolunu anlamak için önem kazanır.26

Aynı Postgres işlemiyle bağladığımız adımları ayrı veritabanlarına dağıtırsak tek yerel işlem hepsini kapsamaz. “Puan servisinde onaylandı ama ilerleme servisinde yazılamadı” gibi ara durumlar için açık bir politika gerekir. Aynı tablolara gelişigüzel erişen servisler de bağımsız sayılmaz: bir şema değişikliği hepsini bağlayabilir.25

Ne zaman değerlendirilir? Ürün sınırları yeterince biliniyorsa, farklı parçaların bağımsız yayın veya kaynak gereksinimi ölçülmüşse ve ekip otomatik yayın, gözlem ve arıza yönetimini üstlenebiliyorsa. “Şu kadar kullanıcıdan sonra mikroservis şart” diye eşik vermiyoruz; aynı kullanıcı sayısı farklı iş yükleri doğurabilir. Sorun yalnız yavaş bir sorguysa Bölüm 39: Postgres Pratikte içindeki dizin ve sorgu planı düzeltmesi, bütün mimariyi parçalamaktan daha doğrudan çözüm olabilir.32

Agent'ın önerisini bir cümleyle sınamak

“Mikroservise geçelim” önerisine önce şu karşılığı ver: “Hangi bağımsız yayın veya ölçekleme sorununu, hangi yeni işletim bedeli karşılığında çözüyoruz?” Cevap yalnız “daha modern olur” ise gerekçe eksiktir. Cevap belirli bir yavaş iş, ayrı sahiplik ve ölçülebilir kazanç gösteriyorsa değerlendirilecek bir karar vardır. Bu soru teknoloji karşıtlığı değil, tasarımın maliyet hesabıdır.31

44.4 Katmanlı mimari: sunum, iş mantığı ve veri

Katmanlı mimari (layered architecture), sorumlulukları farklı düzeylerde ayırır. Sunum katmanı (presentation layer) dış dünyayla konuşur; iş mantığı (business logic) ürünün kurallarını yürütür; veri katmanı (data layer) verinin okunması ve yazılmasıyla ilgilenir. Bu, monolit/mikroservis ayrımından başka bir eksendir: tek Go programı da her ayrı mikroservis de kendi içinde katmanlı olabilir.42

“Sunum” yalnız ekrandaki renkler değildir: Go işleyicisi de dışarıya HTTP/JSON sunar. Fowler'ın Presentation Domain Separation yazısı, mantıksal iş kuralı ayrımını fiziksel istemci–sunucu ayrımıyla karıştırmamayı vurgular.4

HTTP isteği
    ↓
İşleyici (handler): isteği ve dış sözleşmeyi ele al
    ↓
Servis (service): cevap, puan ve ilerleme kuralını yürüt
    ↓
Depo (repository): gerekli veriyi oku/yaz
    ↓
Postgres; gerektiğinde türetilmiş veriler için Redis

Bu, Bölüm 37: Go ile API Yazmak içindeki handler → servis → depo yoludur. İlk ok dış veriyi Go değerlerine çevirir; sonraki oklar iş talebini ve kalıcı kaydı ayırır. Son satır gerçek veri hizmetidir. Klasör adlarından çok hangi sorumluluğun nerede yaşadığı önemlidir.4

Bir cevap isteğini sorumluluklarına ayırmak

SoruBaşlangıçta aranacak yerGerekçe
Gövde geçerli JSON mu?HTTP sınırıDış veri biçimiyle ilgili
Bu cevaba ödül verilmeli mi?İş mantığıÜrün kuralıyla ilgili
Kayıt hangi SQL'le yazılıyor?Veri erişimiSaklama gerçeklemesiyle ilgili
İşlemde iki yazı birlikte onaylanıyor mu?Servis ve veri erişimi sözleşmesiİş biriminin kalıcılık sınırıyla ilgili

Tablo bir başlangıç kılavuzudur, kusursuz bölünmüş mutlak yasa değildir. Örneğin veritabanı kısıtı da bir iş kuralını koruyabilir; servis kuralıyla veritabanının güvencesi birbirini destekler. Puan formülünü HTTP, mobil ekran ve SQL'e ayrı ayrı gömmekse aynı kararı birkaç yerde değiştirmeyi gerektirir; Bölüm 27: Modüler Kod içindeki bilgi gizleme zayıflar.4

Katmanın arayüzü, kullanılan veritabanının bütün ayrıntılarını üst kata taşımamalıdır. “Bu cevabı kaydet ve sonucu döndür” gibi bir sözleşme, üst parçayı belirli sorgu dizilerinden uzak tutabilir. Bölüm 42: Test ve Kalite içindeki sahte depo da böyle mümkün olur. Ama yalnız test kolaylığı için her satıra boş bir arayüz ve geçiş katmanı eklemek de hedef değildir; ayırdığın parçanın gerçekten hangi bilgiyi sakladığını söyleyebilmelisin.41

İşlem sınırına dikkat: puan güncellemesi ve cevap kaydı aynı iş birimiyse iki ayrı depo metodu her seferinde kendi bağımsız işlemini onaylayarak bu güvenceyi bozabilir. Servis, işin hep-ya-da-hiç sınırını tarif etmeli; veri erişimi de bunu gerçekten sağlamalıdır. Katmanlara ayırmak, Bölüm 39: Postgres Pratikte içindeki ACID güvencesini kendiliğinden oluşturmaz. Sınırların sözleşmesi ve testleri birlikte gerekir.54

44.5 Olay güdümlü mimari ve kuyruklar

Olay güdümlü mimari (event-driven architecture), bir parçada olan değişimin başka parçaların işlemesini tetikleyen olaylarla bildirilmesini kullanır. Olay (event), “ne oldu?” bilgisidir: “cevap kabul edildi” ya da “bölüm tamamlandı” gibi. Komut ise “ne yapılmasını istiyorum?” der. “Hatırlatma e-postası gönder” bir iş emridir; “bölüm tamamlandı” gerçekleşmiş olaya dair bildirimdir.7

Bölüm 40: Önbellek ve Redis içinde kuyruk ve işçiyi gördün. İsteğin içinde e-posta göndermeyi beklemek yerine işi kuyruğa bırakıp işçi sürecinin yapmasını sağlayabiliriz. Bu, parçaların zaman bakımından ayrılmasına yardım eder. Aynı Go kod tabanı içinde API ve işçi olarak farklı süreçler çalışabilir; her kuyruk eklenmesi bütün sistemi mikroservise dönüştürmez. Olayları bellek içinde iletmek de mümkündür; “olay” ile “ağ mesajı” özdeş değildir.72

Önceki bölümün iki yere yazma sorusu

Bölüm 40: Önbellek ve Redis açık bir problem bıraktı: Postgres'te puan onaylandıktan sonra Redis güncellemesi başarısız olursa iki kayıt ayrışır. Sadece kuyruğa mesaj eklemek de aynı pencereyi kendiliğinden kapatmaz: Postgres onaylanır, sonra kuyruğa yazarken süreç çökebilir. Sıralamayı Postgres'ten yeniden kurmak küçük sistemde kabul edilebilir bir onarım stratejisi olabilir; daha güvenilir bildirim ihtiyacında iki yazıyı nasıl bağladığımızı ayrıca tasarlarız.5

İşlemsel giden kutusu (transactional outbox) kalıbının sezgisi şudur: asıl veri değişikliği ile “bildirilecek iş/olay” kaydı aynı Postgres işlemi içinde yazılır. Ayrı bir iletici onaylanmış giden kutusu kayıtlarını okuyup dış sisteme yollar. Böylece asıl değişiklik olup bildirim niyetinin hiç kaydedilmediği boşluk azaltılır; ağdaki teslimat hâlâ ayrıca yönetilir.5

Tek Postgres işlemi:
    cevap kaydı + puan güncellemesi + giden kutusuna olay kaydı
    → birlikte COMMIT

Daha sonra iletici:
    onaylanmış olayı oku → kuyruğa ilet → teslim durumunu kaydet

İlk blokta bütün kayıtlar aynı yerel işlemdedir: işlem geri alınırsa olay kaydı da kalmaz. İkinci blokta artık ağ sınırı vardır. İletici mesajı gönderdikten sonra “gönderildi” yazamadan çökerse aynı mesajı yeniden gönderebilir. Dolayısıyla outbox, “her yan etki evrende tam bir kez gerçekleşir” garantisi değildir. Bu bölüm kalıbı yalnız sezgi düzeyinde gösteriyor; tam iletici, kilitleme ve teslim protokolü gerçeklenmiyor.5

Tekrar teslim neden tasarımın parçasıdır?

En az bir kez teslim (at-least-once delivery) düzeninde bir mesaj birden fazla kez gelebilir. Amazon SQS standart kuyruk belgesi de tüketicinin bu olasılığı ele almasını ister. Bölüm 35: HTTP, JSON ve REST içindeki idempotentlik burada somut bir iş kuralına dönüşür: aynı olay tekrar işlendiğinde puan yeniden artmamalıdır. Olay kimliği ve işlenmiş kayıtların uygun şekilde tutulması bunun araçlarından olabilir.65

Ayrıca “puanı bir artır” ile “bu sürümdeki toplam puan şu” farklı güncellemelerdir. Körlemesine artışın iki kez çalışması puanı şişirebilir; mutlak değeri yazmak ise eski mesaj daha sonra gelirse güncel değeri geriye götürebilir. Tekrar ve sıra politikası birlikte düşünülür. Küçük uygulamada doğrudan Postgres'ten yeniden hesaplanan bir görünüm yeterliyken karmaşık olay düzeni kurmak şart değildir. Kalıcı gerçek, yeniden üretilebilir görünüm ve kabul edilen gecikme açıkça seçilir.56

Burada olay göndermek, bütün durumu olay geçmişinden yeniden kuran başka bir tasarım seçmek anlamına gelmez. Fowler'ın yazısı farklı olay kalıplarını ayırır. Bizim bu bölümdeki dar ihtiyacımız, kritik veriyi kaybetmeden bazı yan işleri ertelemek ve başarısızlık penceresini tanımaktır. Bir mekanizmaya isim vermek, onun teslim ve hata politikasını kurmuş olmak değildir.7

44.6 Ölçeklenme: dikey, yatay ve yük dengeleyici

Ölçeklenme (scaling), artan yük altında hizmeti sürdürebilmek için kapasite düzenini değiştirmektir. Dikey ölçeklenme (vertical scaling), bir makineye daha çok işlemci/bellek gibi kaynak ayırır. Yatay ölçeklenme (horizontal scaling) ise birden çok çalışan örneğe yayılmayı kullanır. “Kodu hızlandırmak” ile “kaynakları artırmak” aynı hamle değildir; önce darboğazın nerede olduğunu ölçmeliyiz.811

Yük dengeleyici (load balancer), gelen trafiği birden çok hedefe dağıtır. Örneğin aynı Go API imajından çalışan birkaç kopyaya istek yollar. Sağlık denetimleriyle hizmet veremeyen hedefleri dışarıda bırakabilen düzenler vardır; AWS Elastic Load Balancing belgesi bunu somut olarak açıklar. Bu, istemcinin her kopyanın adresini bilmesini gerektirmeyen bir giriş noktası sağlar.9

                         ┌─ Go API kopyası A ─┐
İstemci → yük dengeleyici ┤                    ├→ Postgres
                         └─ Go API kopyası B ─┘       + ortak oturum deposu

API kopyaları: aynı monolitin aynı sürümünden çalışan örnekler olabilir.

Şekildeki ayrım yayın birimi değil çalışan örnek sayısıdır. İlk ok ortak giriş noktasını gösterir. İki yol isteklerin farklı süreçlere gidebildiğini gösterir. Sağ taraf ise her kopyanın yine ortak kaynaklara bağımlı olduğunu hatırlatır. Monoliti yatay büyütebiliyoruz; mikroservis adını almak için değil kapasite ve kullanılabilirlik ihtiyacı için.29

Durumu nereye koyduğun büyümeyi etkiler

Kullanıcının oturumunu yalnız A'nın belleğinde tutarsan sonraki istek B'ye gidince B onu tanımaz. Bölüm 40: Önbellek ve Redis içindeki ortak oturum deposu bu yüzden önemlidir; Bölüm 43: Yayına Alma ve Bulut içindeki durumsuz süreç ilkesi de aynı soruna bakar. Durumsuzluk, sistemde hiçbir veri bulunmaması değil, bir isteğin doğruluğunun belirli süreç belleğine bağlanmamasıdır. Kullanıcıya ait kalıcı gerçek ortak uygun veri hizmetinde durur.10

Ortak veri hizmeti de sınırsız değildir. Her API kopyası kendi bağlantı havuzunu açarsa Bölüm 39: Postgres Pratikte içindeki toplam bağlantı sınırına yaklaşabiliriz. API sayısını artırmak veritabanına daha fazla eşzamanlı iş bindirebilir. Bu yüzden tüm yol birlikte ölçülür: işlemci mi dolu, SQL mi bekliyor, bağlantı havuzu mu dolu, dış hizmet mi yavaş? Yatay büyüme, yavaş sorguyu otomatik düzeltmez.92

Yalnız sezgi için bir hesap yapalım. Bağımsız kopyaların her biri varsayımsal olarak saniyede 80 istek işleyebilsin. Dört kopyanın teorik toplamı 4 × 80 = 320 istek/saniyedir. Her istek tek bir ortak veritabanı işlemine muhtaçsa ve bu örnekte veritabanının varsayımsal sınırı 200 işlem/saniye ise bütün yolun üst sınırı min(320, 200) = 200 olur. Python ile hesaplanan bu değerler ürün ölçümü veya bulut hizmeti vaadi değildir; ortak darboğazın neden çarpma hesabını bozduğunu gösteren varsayımlardır.

Dikey büyüme daha sade olabilir fakat tek makinenin kaynak sınırına dayanır; yatay büyüme kapasite ve arıza yalıtımı fırsatı verir fakat ortak durum, yönlendirme ve gözlem yükü getirir. Kullanılabilirlik de yalnız kopya sayısı değildir: bütün kopyalar aynı kırılgan bağımlılığa bağlıysa ortak arıza devam eder. Mimari kararı hata yollarıyla da okuruz.119

Uygulamamızda: başlangıç ve sonraki adım

Önerilen başlangıç, anlaşılır katman ve modülleri olan Go arka ucu, kalıcı kayıt için Postgres ve ölçülmüş ihtiyaçta Redis'tir. Yük artınca önce Bölüm 43: Yayına Alma ve Bulut içindeki ölçümlere ve Bölüm 39: Postgres Pratikte içindeki sorgu planına bakarız. Sorun API kapasitesiyse kopya sayısı; sorun belirli bir uzun işse ayrı işçi; sorun gerçekten bağımsız yayınsa servis sınırı tartışılır. Bunlar sıralı zorunlu basamaklar değil, farklı sorunlara verilen farklı cevaplardır.39

Bölüm 45: Uçtan Uca: Uygulamamız artık bu kararları tek cevabın yolculuğunda birleştirecek: telefon, HTTPS, Go işleyicisi, iş kuralı, Postgres ve Redis aynı haritaya yerleşecek. Bu bölümün katkısı yeni kutular eklemek değil, her kutunun hangi sorumluluğu ve hangi bedeli taşıdığını ayırmaktı.

Bölüm 1 ile bağlantı

Bölüm 1: Büyük Harita soyutlamayı alt ayrıntıyı arayüz arkasında saklamak olarak kurmuştu. Mimari bunun devamıdır: bir modül, servis veya veri sınırı üst parçaya anlaşılır bir söz verir. İyi sınır ayrıntıyı saklar; yanlış sınır ise her değişiklikte öteki parçaları da çağırır. Bina benzetmesindeki taşıyıcı duvarın karşılığı, burada değişim boyunca korumak istediğimiz sözleşmelerdir.

Bu bölümde neler kuruldu

Mimari, önemli ve değiştirmesi pahalı kararları ele alır. Monolit modüler olabilir ve çok kopya çalışabilir. Mikroservisler bağımsızlık karşılığında ağ ve işletim yükü getirir. Katmanlar sorumlulukları ayırır. Outbox bildirim niyetini asıl veriyle bağlar, tekrar teslimi kaldırmaz. Dikey büyüme kaynağı, yatay büyüme kopyaları artırır; ortak darboğazlar sürer.

Terimler sözlükte, Bölüm 44 başlığı altında.

Birincil okuma

Birincil okuma: Martin Fowler, Monolith First, özellikle microservice premium ve sınırların erken bilinmemesi tartışması. Derinleşme: James Lewis ve Martin Fowler, Microservices, Componentization via Services ve Decentralized Data Management; AWS, Transactional outbox pattern, Intent ve Issues and considerations.

Takıldığın yeri yapay zekâ öğretmenine sor. Ondan hukuk uygulamamızı önce tek Go yayın birimi, sonra iki ayrı hizmet olarak çizmesini iste. Çizimde yeni oluşan her ağ sınırı için 'yanıt dönmeden süreç çökerse ne olur?' sorusuna sen cevap ver. Bir kutu eklemekle bir sorumluluk eklemek arasındaki farkı özellikle tartışın.

Bölüm sonu soruları

Kitaba bakmadan cevapla. Önce gerekçeni düşün, sonra şıkkını seç.

1. Monolit için hangi ifade doğrudur?
Monolit yayın birimiyle ilgilidir; iç modül, dosya ve çalışan kopya sayısını tek yapmaz. Bkz. 44.2.
2. Modüler monolitte neyi korumak isteriz?
Modülün arayüzü iç ayrıntıyı saklar; aynı süreçte olmak bu sınırı gereksiz kılmaz. Bkz. 44.2.
3. Mikroservis önerisini hangi gerekçe güçlendirir?
Anlamlı bağımsız yayın ve işletilebilir sınırlar somut gerekçedir; modern görünmek yeterli değildir. Bkz. 44.3.
4. HTTP biçiminden bağımsız puan kuralı nerede yaşamalıdır?
Puan ürün kuralıdır; dış JSON biçimi veya görünümden ayrılınca farklı girişlerden kullanılabilir. Bkz. 44.4.
5. Outbox kaydı asıl veriye nasıl bağlanır?
Asıl veri ve bildirim niyeti aynı yerel işlemde yazılır; işlem başarısızsa olay da kalmaz. Bkz. 44.5.
6. Aynı olay tekrar teslim edilirse ne gerekir?
Teslim birden fazla olabilir; tüketici aynı olayın tekrarında yanlış ek etki doğurmamalıdır. Bkz. 44.5.
7. Aynı API'den birkaç kopya çalıştırmak nedir?
Yatay büyüme çalışan örnek sayısını artırır; aynı monolitin kopyalarıyla da yapılabilir. Bkz. 44.6.
8. Yük dengeleyici hangi işi doğrudan yapar?
Yük dengeleyici trafiği dağıtır; veri kaynağı darboğazını ve uygulama hatalarını kendiliğinden çözmez. Bkz. 44.6.
9. Postgres onaylandıktan sonra Redis'e puan artışı gönderilemedi. İki çözüm düzeyi tarif et; outbox hangi sorunu bırakır?
Örnek cevabı göster

Türetilmiş leaderboard Postgres'ten yeniden kurulabilir ve kabul edilen gecikmeyle onarılabilir. Bildirim daha güvenilir izlenecekse asıl değişiklik ve olay niyeti aynı işlemde outbox'a yazılıp sonra iletilebilir. İletim sonrası durum kaydı yazılamadan çökme tekrar göndermeye yol açabilir; tüketici tekrar ve sıra politikasını ayrıca kurar. Bkz. 44.5.

10. İki kişilik ekip için modüler monolit önerisini savun; sonra onu yeniden düşünmeyi gerektirecek bir kanıt ver.
Örnek cevabı göster

Ürün sınırları değişirken tek yayın birimi ve süreç içi çağrılar işletim yükünü azaltabilir; modüller sorumlulukları saklar. Belirli parçanın bağımsız ekip ve yayın ritmi, farklı ölçülmüş kapasite ihtiyacı ya da belirgin işletim sınırı oluşursa ayrılma değerlendirilir. Sırf kullanıcı sayısı veya teknoloji modası yeterli gerekçe değildir. Bkz. 44.3.

Kaynaklar

  1. Martin Fowler, Software Architecture Guide: What is architecture? ve değişim maliyeti.
  2. James Lewis ve Martin Fowler, Microservices: tek yayın birimi, bağımsız hizmetler, veri sahipliği ve arızaya göre tasarım.
  3. Martin Fowler, Monolith First: başlangıç sadeliği, sınırların öğrenilmesi ve kanıtın sınırları.
  4. Martin Fowler, Presentation Domain Separation: mantıksal sunum–iş kuralı ayrımı ve test edilebilirlik.
  5. AWS Prescriptive Guidance, Transactional outbox pattern: iki yere yazma, aynı işlemde olay kaydı, tekrar ve sıralama.
  6. AWS, Amazon SQS at-least-once delivery: tekrar teslim ve tüketicide idempotentlik.
  7. Martin Fowler, What do you mean by Event-Driven?: Event Notification ve farklı olay kalıplarının ayrılması.
  8. AWS Database Blog, Scaling Your Amazon RDS Instance Vertically and Horizontally: dikey ve yatay büyüme tanımları.
  9. AWS, What is Elastic Load Balancing?: trafik dağıtımı, hedef sağlığı ve ortak giriş.
  10. The Twelve-Factor App, VI. Processes: kalıcı durumun süreçten ayrılması ve ortak oturum.
  11. AWS Well-Architected, Reliability Pillar, Design principles: yatay dağılım, ortak arıza noktası ve kapasiteyi ölçme.