İçeriğe atla

Strangler Fig Deseni: Bir Monoliti Adım Adım Taşımak, Diyagramla

Strangler fig deseniyle bir monolit nasıl değiştirilir: yönlendiren bir cephe, dört aşama, önce neyin ayrılacağı, çift yazmasız veri taşıma ve eşdeğerliği kanıtlama.

· Kurucu ve geliştirici

Yayımlandı:

Müşteriler cephedeki API gateway'i çağırır. Gateway /catalog/* isteklerini (yüzde 100) katalog servisine, GET /orders/* isteklerinin yüzde 25'ini sipariş servisine, kalanını eski monolite gönderir; monolitin veritabanı kesikli CDC yoluyla sipariş veritabanını besler.

Monolitiniz on yaşında, her sürüm uzun bir regresyon döngüsü gerektiriyor ve biri baştan yazmayı öneriyor: yeni sistemi yanında kurmak, özellik eşdeğerliğine ulaşmak, bir hafta sonu geçiş yapmak. Bu arada eski sistem değişmeye devam ediyor ve geçiş tarihi kayıyor. Martin Fowler, kendisinin ve meslektaşlarının bu planın “çoğu zaman alevler içinde çöktüğünü” gördüklerini yazıyor.

Strangler fig deseni ise sistemi parça parça değiştirir. Monolitin önündeki bir cephe (facade) her isteği eski koda ya da yeni bir servise yönlendirir ve yetenekler, monolite hiçbir şey yönlendirilmeyene kadar birer birer taşınır. Yönlendirme işin kolay kısmıdır. Zor olanlar veri sahipliği, eşdeğerliği kanıtlamak ve işi bitirmektir.

Bu yazı, sürmekte olan bir geçişin diyagramıyla birlikte hepsini ele alıyor.

Adı nereden geliyor

Fowler 2001'de Queensland yağmur ormanında boğucu incirler gördü: bir konak ağacın üzerinde filizlenen, toprağa kök salan ve ağaçtan uzun yaşayarak geride ağacın biçiminde bir incir bırakabilen sarmaşıklar. Haziran 2004'te onları eski bir sistemi kademeli olarak değiştirmenin metaforu olarak kullandı. Nisan 2019'da bitkiye gönderme yapmak için yazının adını “Strangler Application” yerine “Strangler Fig Application” yaptı ve Ağustos 2024'te yazıyı yeniden yazdı. Güncel sürüm, ekiplerin direndiği şeyin altını çiziyor: geçiş mimarisi, yani yalnızca eskiyle yeninin bir arada var olabilmesi için yazdığınız kod.

Yönlendirmeyi cephe yapar

Cephe, monolitin önünde duran bir reverse proxy ya da API gateway'dir: NGINX, Envoy, bir bulut API gateway'i ya da yol kuralları olan bir yük dengeleyici. Başlangıçta her şeyi monolite gönderir. Ondan sonra her geçiş adımı bir yönlendirme değişikliğidir. Şunlara göre yönlendirin:

  • Yol: /catalog/* katalog servisine gider.
  • Metot: önce GET /orders/*, sonra POST /orders; böylece önce okumalar taşınır.
  • Pay: oturum ya da kullanıcı kimliğinin hash'i her kullanıcıyı tek bir tarafta tutar.
  • Header ya da tenant: önce şirket içi kullanıcılar ya da bir pilot müşteri geçer.
# upstream blocks catalog, orders and monolith defined elsewhere
split_clients "${cookie_session}" $orders_read {
    25%      orders;       # canary share for reads
    *        monolith;
}
map $request_method $orders_target {
    GET      $orders_read;
    default  monolith;     # order writes stay in the monolith
}
server {
    location /catalog/ { proxy_pass http://catalog; }
    location /orders/  { proxy_pass http://$orders_target; }
    location /         { proxy_pass http://monolith; }
}

Geri almak tek bir düzenleme demektir: %25 satırının hedefini monolith yapın (nginx %0'lık bir girdiyi reddeder). Her isteğin taşıdığı bir anahtarı hash'leyin: cookie'si olmayan isteklerin hepsi aynı kovaya düşer.

Cepheyi ince tutun: yönlendirme, TLS, kimlik doğrulama ve loglama olsun, iş kuralı olmasın. Her isteğin yolu üzerinde durduğu için birkaç instance çalıştırın ve eklediği gecikmeyi ölçün. Bir gateway'e neyin ait olduğunu API gateway mimari diyagramı ele alıyor.

Bir geçişin dört aşaması

Aşama Cephenin yönlendirdiği Veri Tamamlanma ölçütü
0. Önce cephe Her şey monolite Değişmez Cephe canlıda, eklediği gecikme ölçülmüş
1. İlk yetenek Tek bir rota, örneğin /catalog/*, yeni bir servise Servis önce monolit tablolarını ya da bir CDC kopyasını, sonra kendi tablolarını okur Rota tam bir iş döngüsü boyunca %100'de
2. Trafiğin çoğu yeni servislerde Çoğu rota; monolitte bilinen bir liste kalır Her servis kendi tablolarının sahibidir Geriye kalan tek sayfaya sığar
3. Monolit emekli Monolite hiçbir şey Eski veritabanı önce arşivlenir, sonra silinir Monolit kapatılmış

Microsoft'un Strangler Fig deseni sayfası son bir adım ekliyor: cepheyi kaldırmak ya da eski istemciler için bir adaptör olarak tutmak.

Önce neyi ayıracağınızı seçmek

İyi ilk adayların üç özelliği vardır:

  • Net bir ek yeri (seam), Michael Feathers'ın deyişiyle davranışı o yerde düzenleme yapmadan değiştirebileceğiniz bir nokta: bir URL öneki, bir kuyruk, bir modül sınırı.
  • Yüksek değişim sıklığı, böylece taşıma kısa sürede karşılığını verir. Sürüm kontrolü bunu gösterir.
  • Düşük bağımlılık: başka tablolara az join, monolite geri az çağrı.
git log --since="12 months ago" --format= --name-only -- src/ \
  | sort | uniq -c | sort -rn | head -20

Bir online mağaza için sıralama:

Aday Ek yeri Değişim sıklığı Bağımlılık Karar
Katalogda gezinme /catalog/* Yüksek Düşük İlk
Sipariş geçmişi GET /orders/* Orta Orta İkinci, önce okumalar
Sipariş tamamlama Fiyatlandırmayı, stoku ve ödemeleri çağırır Yüksek Yüksek Bağımlılıklarından sonra
Gece raporları Şemanın tamamına yayılan SQL Düşük Çok yüksek En son ya da bir veri ambarı beslemesi

Teknik katmana göre değil, iş yeteneğine göre ayırın. Ortak bir “veri erişim servisi” hiçbir şeyi taşımaz ve her şeyi birbirine bağlar.

Veriyi taşımak

Yönlendirme veriyi değil istekleri taşır ve geçişler en çok veride takılır. Her adımda her tablonun sahibinin kim olduğuna karar verin.

Strateji Nasıl çalışır Ne için kullanılır Risk
Ortak veritabanı Yeni servis monolitin tablolarını kullanır İlk haftalar Monolitteki şema değişiklikleri onu bozar
Change data capture (CDC) Bir connector, monolitin log'undaki satır değişikliklerini akış olarak aktarır Önce okumaları taşımak Gecikme, eski şemalar, tek yönlü akış
Servis başına veritabanı Servis kendi tablolarının sahibidir; diğerleri onun API'sini çağırır Son durum Servisler arası join'ler API çağrılarına dönüşür
Koddan çift yazma Uygulama iki veritabanına da yazar Hiçbir şey İki yazma arasındaki bir çökme onları tutarsız bırakır

Microsoft'un rehberi olağan sırayı veriyor: yeni servis monolitin tablolarıyla başlar, sonra bir kez yüklenip CDC ile güncel tutulan kendi veritabanına geçer ve doğrulamadan sonra asıl kayıt sistemi (system of record) olur. Eski tablolar kaldırılana kadar geri alma bir rota değişikliğidir; sonrasında ise bir geri yükleme.

Çift yazmadan kaçının: iki ayrı yazma arasındaki bir çökme veritabanlarını uyuşmaz hâlde bırakır, eşzamanlı güncellemeler de farklı sıralarla işlenebilir. Her değişikliğin kaynağını tek bir transaction yapın; bunu CDC ya da bir transactional outbox ile sağlayın. Change data capture diyagramı Debezium'u ve replication slot'ları ele alıyor.

Anti-corruption layer

Monolitin modeli dışarı sızar: tek harfli durum kodları, aynı tabloda fatura ve kargo alanları, stored procedure'lerde duran kurallar. Bu satırları tüketen bir servis, bırakmaya çalıştığınız modeli miras alır. Eric Evans'ın Domain-Driven Design (2003) kitabından gelen bir desen olan anti-corruption layer, sınırda çeviri yapar:

  • Eskiden yeniye: eski satırları taşıyan CDC olayları yeni modelde siparişlere dönüşür.
  • Yeniden eskiye: sipariş servisi müşteri verisini monolitten bir adaptör üzerinden okur.
  • Monolitten yeniye: katalog taşındıktan sonra monolit kodu, eski tabloları yerine katalog API'sini çağırır.

İş kurallarını bu katmanın dışında tutun. Hexagonal mimarili bir serviste katman adaptörlerde yer alır; böylece çekirdek eski tipleri hiç görmez.

Bir rota taşınmadan önce eşdeğerliği kanıtlamak

Her rota aynı adımlardan geçer:

  1. Gölge trafik. Cephe, NGINX'in mirror modülü ve Envoy'un istek yansıtma özelliğinin yaptığı gibi istekleri yeni servise kopyalar ve onun yanıtlarını atar. Gölge trafiğe yalnızca okumaları alın: yansıtılan bir POST /orders ikinci bir sipariş oluşturur.
  2. Karşılaştırma. Zaman damgalarını, kimlikleri ve sıralamayı normalleştirdikten sonra iki yanıtın farkını alın. Twitter'ın 2015'te açık kaynak yaptığı Diffy, eski kodun iki kopyasını çalıştırır ve aralarında farklı çıkanları gürültü sayıp yok sayar. Monolitin içinde ise GitHub'ın Scientist kütüphanesi (Ruby; başka dillere port'ları da var) eski ve yeni yolları yan yana çalıştırır ve eski sonucu döndürür.
  3. Canary. Gerçek trafiğin önce %1'ini, %5'ini, %25'ini, %50'sini, sonra %100'ünü taşıyın; aynı rota için hata oranını ve p99 gecikmesini monolitinkilerle karşılaştırın ve her adımı bir günlük trafik zirvesi boyunca tutun. Mekaniği deployment stratejileri diyagramı ele alıyor.
  4. Bekletme. %100'e ulaştığınızda, ay sonu kapanışı gibi olaylar geçene kadar eski yolu deploy edilebilir, verisini de güncel tutun.
  5. Silme. Eski kodu, tablolarını ve rotayı silin.

Bir rotayı geri çevirmek, yalnızca monolitte güncel veri olduğu sürece işe yarar. Yazmaların sahibi yeni servis olduğunda bunları ters CDC ya da olaylarla geri besleyin; aksi hâlde bir geri alma onları kaybeder.

Strangler fig geçişleri nasıl başarısız olur

  • Hiç bitmemek. Kolay %80 taşınır; raporlar, batch işler ve yönetim ekranları kalır ve iki sistemi birden işletirsiniz. Her sprint'te geriye kalanı sayın ve bir kapatma tarihi belirleyin.
  • Dağıtık monolit. Tablo paylaşan, birlikte deploy edilen ya da birbirini uzun senkron zincirlerle çağıran servisler. Ölçüt, tablo başına tek bir yazandır.
  • Darboğaz hâline gelen cephe: gateway'de iş mantığı ya da tüm trafiği taşıyan tek bir instance.
  • Gizli tüketiciler. Raporlar ve diğer ekipler, cephenin göremeyeceği şekilde monolitin veritabanını doğrudan okur. Bir tabloyu taşımadan önce bağlantı log'larını kontrol edin.
  • Hedef olarak özellik eşdeğerliği. Fowler, eski davranışların çoğunun “aslında istenmediğini” belirtiyor. Patterns of Legacy Displacement bu ve diğer tuzakları ele alıyor.

Strangler fig geçiş diyagramı

Müşteriler cephedeki API gateway'i çağırır. Gateway /catalog/* isteklerini (yüzde 100) katalog servisine, GET /orders/* isteklerinin yüzde 25'ini sipariş servisine, kalanını eski monolite gönderir; monolitin veritabanı kesikli CDC yoluyla sipariş veritabanını besler.

17 bileşen · 10 bağlantı

Archboard'da aç
Diyagram içeriği
  • Customers
  • Facade (API gateway)
  • Catalog service
  • Orders service
  • Legacy monolith
  • Catalog DB
  • Orders DB
  • Monolith DB
  • Debezium + Kafka
  • Anti-corruption layer
  • HTTPS
  • /catalog/* · 100%
  • GET /orders/* · 25%
  • everything else + 75% of GET /orders
  • reads, writes
  • reads
  • WAL changes
  • order row events
  • upserts in the new model
Sürmekte olan bir monolit geçişi. Cephe, tüm katalog trafiğini kendi verisinin sahibi olan yeni bir servise, sipariş okumalarının dörtte birini change data capture ile beslenen yeni bir sipariş servisine, kalanını da monolite gönderir. Her adım geri alınabilen bir rota değişikliğidir.

Nasıl okunur:

  • Soldan sağa: müşteriler cepheyi çağırır, cephe de trafiği rotaya göre böler; veritabanları sağda durur.
  • Yeşil, taşındı: /catalog/* trafiğinin tamamı, kendi verisinin sahibi olan katalog servisine gider.
  • Kehribar, canary: GET /orders/* isteklerinin %25'i sipariş servisine gider. Yazmalar ve diğer okumalar hâlâ monolite gider.
  • Gri, eski: monolit geri kalan trafiğe hizmet eder ve sipariş verisinin sahibi hâlâ odur.
  • Mor, kesikli: Debezium, sipariş değişikliklerini monolitin WAL'ından Kafka üzerinden akıtır; anti-corruption layer da bunları yeni modele eşler.
  • Rota tablosu her rota için hedefi, payı ve aşamayı listeler; böylece geri alma tek bir düzenlemedir.

Strangler fig geçişlerini net çizmek

  • Her aşama için bir diyagram ya da tarihli bir başlık kullanın; yönü göstermek için eskilerini saklayın.
  • Rotaları yol ve payla etiketleyin, örneğin “GET /orders/* · 25%”.
  • Veri sahipliğini gösterin: asıl kayıt sistemini ve her senkronizasyonun yönünü.
  • Ekibe göre değil, duruma göre renklendirin (taşındı, canary, eski).
  • İç detayları dışarıda bırakın. Bu bir geçiş haritasıdır, bileşen diyagramı değil.
  • Gizli tüketicileri çizin, örneğin monolitin veritabanını okuyan bir gece raporu.

Son durumun nasıl okunabilir tutulacağını mikroservis mimari diyagramı gösteriyor.

Archboard'da oluşturun

  1. Google ya da GitHub ile giriş yapın ve bir diyagram oluşturun.
  2. Müşteriler için C4 Modeli paketinden bir Kişi, cephe için de Bulut paketinden bir API ağ geçidi ekleyin.
  3. C4 Modeli paketinden üç Konteyner düğümü ekleyin: Catalog service, Orders service ve Legacy monolith. Onları yeşil, kehribar ve gri renge boyayın.
  4. Her container'ın sağına Veri paketinden bir Postgres düğümü ekleyin.
  5. Monolitin veritabanı ile sipariş veritabanı arasına, üst üste gelecek şekilde Veri paketinden bir Kafka düğümü (Debezium + Kafka) ve C4 Modeli paketinden bir Bileşen düğümü (Anti-corruption layer) ekleyin; bunları yukarı doğru kesikli kenarlarla bağlayın.
  6. Cepheden çıkan rotaları yol ve payla etiketleyerek çizin ve her container'ın üstüne bir Temel Metin durum etiketi ekleyin.
  7. Rota tablosunu içeren, Temel paketinden bir Markdown bloğu ekleyin.

Rota haritanız bir Mermaid akış şemasındaysa Mermaid dönüştürücü onu düzenlenebilir bir ilk taslağa çevirir.

Örnek 11 diyagram düğümü kullanıyor ve Free planın sınırları içinde kalıyor.

SSS

Strangler fig deseni nedir? Eski bir sistemi kademeli olarak değiştirmenin bir yolu: bir cephe her isteği eski koda ya da yeni bir servise yönlendirir, yetenekler birer birer taşınır ve ona hiçbir şey yönlendirilmediğinde eski sistem kapatılır.

Neden strangler fig deseni deniyor? Martin Fowler bu adı 2004'te, Queensland'de gördüğü ve bir konak ağacın etrafında büyüyüp onun yerini alabilen boğucu incirlerden esinlenerek verdi. Bitkiyi vurgulamak için 2019'da ada “Fig” sözcüğünü ekledi.

Strangler fig geçişinde veritabanı nasıl ele alınır? Adım adım. Yeni servis önce monolitin tablolarını okur, sonra change data capture ile beslenen kendi veritabanına geçer ve doğrulamadan sonra yazmaları devralır. Uygulama kodundan çift yazmadan kaçının.

Strangler fig ile branch by abstraction arasındaki fark nedir? Strangler fig istekleri sistemin sınırında yönlendirir. Branch by abstraction ise kodun içinde çalışır: bir arayüz, her çağıran geçiş yapana kadar eski ve yeni gerçekleştirimlerin bir arada var olmasını sağlar. Pek çok geçiş ikisini birden kullanır.

Strangler fig deseni ne zaman kullanılmamalı? Eski sisteme giden istekler araya girilerek yakalanamıyorsa, sistem tek seferde değiştirilebilecek kadar küçükse ya da hızla kapatılması gerekiyorsa.

Ücretsiz başlayın — tek kullanıcı, kredi kartı gerekmez.

İlgili yazılar

← Blog'a dön

Bir dakikada canvas açın

Google veya GitHub ile giriş yapın. Tek koltuk ücretsiz; ekip katılınca planı yükseltin.