Takip et

go-workflow-engine: Tak-Çalıştır İş Akışı Motoru

Karmaşık iş süreçlerini yönetmekte zorlanıyor musunuz? go-workflow-engine ile Go projelerinizde tak-çalıştır iş akışları oluşturmayı adım adım öğrenin.

go-workflow-engine: Tak-Çalıştır İş Akışı Motoru

Karmaşık iş süreçlerini yönetmekte zorlanıyor musunuz? go-workflow-engine ile Go projelerinizde tak-çalıştır iş akışları oluşturmayı adım adım öğrenin.

Modern yazılım geliştirme dünyasında, özellikle mikroservis mimarileri (microservices architecture) ve dağıtık sistemler yaygınlaştıkça, iş süreçlerinin yönetimi büyük bir sorun haline gelmektedir. Bir kullanıcının e-ticaret sitesinden alışveriş yapması kadar basit görünen bir işlem bile arka planda ödeme alma, stok güncelleme, fatura kesme ve kargoya verme gibi onlarca farklı adımdan oluşur. Bu adımların sırasıyla, güvenli bir şekilde ve hata durumlarında geri alınabilir (rollback) olarak yönetilmesi gerekir. İşte tam bu noktada, Go dilinin gücünü arkasına alan hafif ve esnek bir kütüphane olan go-workflow-engine devreye giriyor.

Geliştiriciler olarak genellikle bu tür süreçleri yönetmek için karmaşık if-else blokları veya devasa durum makineleri (state machine) yazarız. Ancak, zamanla bu kod tabanı okunamaz ve bakımı yapılamaz bir hale gelir. Bu makalede, Go dilinde iş akışlarınızı nasıl “tak-çalıştır” (plug-and-play) bir yaklaşımla tasarlayabileceğinizi, kod karmaşasından nasıl kurtulabileceğinizi ve go-workflow-engine kütüphanesini projelerinize nasıl entegre edebileceğinizi detaylıca inceleyeceğiz.

İş Akışı Yönetimi Neden Bu Kadar Karmaşık?

Yazılım projelerinin başlangıcında iş süreçleri genellikle oldukça basit görünür. Örneğin, bir kullanıcının kaydolma süreci yalnızca veritabanına kayıt eklemekten ibaret olabilir. Ancak, iş gereksinimleri büyüdükçe bu sürece e-posta doğrulama, hoş geldin SMS’i gönderme, CRM sistemine veri aktarma ve analitik araçlarını tetikleme gibi yeni adımlar eklenir. Bu adımların her biri farklı dış servislerle (third-party APIs) iletişim kurmayı gerektirir.

Özellikle dış servislerin yanıt vermemesi, ağ kesintileri veya beklenmedik sistem hataları durumunda sürecin nasıl davranacağı büyük bir muammadır. Eğer ödeme başarılı olduysa ancak stok güncelleme sırasında hata alındıysa ne olacak? Kullanıcının parası iade edilecek mi, yoksa sistem otomatik olarak işlemi yeniden mi deneyecek (retry)? Bu tür senaryoları manuel olarak kodlamak, spagetti kod (spaghetti code) olarak adlandırılan karmaşık yapıların ortaya çıkmasına neden olur.

Bununla birlikte, iş akışlarının izlenebilirliği (observability) de ayrı bir problemdir. Hangi işlemin hangi adımda takıldığını, hangi hatayla karşılaşıldığını ve sürecin ne kadar sürede tamamlandığını görmek her zaman kolay olmaz. Bir iş akışı motoru (workflow engine), tüm bu karmaşıklığı soyutlayarak geliştiriciye standart, güvenli ve izlenebilir bir yapı sunar. Go dilinin yüksek eşzamanlılık (concurrency) yetenekleri sayesinde, bu süreçleri minimum kaynak tüketimiyle yönetmek mümkündür.

go-workflow-engine Nedir ve Ne İşe Yarar?

go-workflow-engine, Go (Golang) dili ile geliştirilmiş, minimalist ve son derece esnek bir iş akışı yönetim kütüphanesidir. Ağır ve kurulumu zor olan kurumsal iş akışı motorlarının (örneğin Camunda veya Temporal) aksine, bu kütüphane projenize doğrudan bir kütüphane (library) olarak dahil edilir. Herhangi bir harici sunucu veya veritabanı bağımlılığı gerektirmeden, doğrudan uygulama kodunuzun içinde çalışır.

Bu kütüphanenin temel amacı, iş süreçlerinizi küçük, bağımsız ve yeniden kullanılabilir görevlere (tasks) bölmenizi sağlamaktır. Her bir görev, girdileri (inputs) alır, kendi mantığını çalıştırır ve çıktıları (outputs) bir sonraki göreve aktarır. Kütüphane, bu görevlerin hangi sıra ile çalışacağını, hata durumlarında nasıl davranılacağını ve süreçlerin nasıl izleneceğini yönetir.

Kütüphanenin sunduğu temel kavramları şu şekilde özetleyebiliriz:

  • Workflow (İş Akışı): Belirli bir iş sürecini temsil eden ve görevlerin bir araya gelmesiyle oluşan yapıdır.
  • Task (Görev): İş akışının en küçük yapı taşıdır. Tek bir sorumluluğu vardır (örneğin, “Kullanıcıya SMS Gönder”).
  • Context (Bağlam): Görevler arasında veri taşımayı sağlayan güvenli bir veri havuzudur.
  • Transition (Geçiş): Bir görevden diğerine hangi koşullarda geçileceğini belirleyen kurallardır.

Adım Adım Kurulum ve İlk İş Akışının Oluşturulması

Şimdi, go-workflow-engine kütüphanesini projemize dahil edelim ve basit bir üyelik onaylama süreci tasarlayalım. İlk olarak, Go modülümüzü başlatalım ve gerekli paketleri projemize çekelim. Terminal üzerinden aşağıdaki komutları çalıştırarak başlayabilirsiniz:

go mod init go-workflow-demo
go get github.com/username/go-workflow-engine

Paket kurulumunu tamamladıktan sonra, ilk iş akışımızı tanımlamak için kod yazmaya başlayabiliriz. Bu örnekte, bir kullanıcının kaydolmasının ardından veritabanına kaydedilmesi ve ardından bir hoş geldin e-postası gönderilmesi adımlarını simüle edeceğiz. Aşağıdaki kod bloğunda bu basit akışın nasıl kurulduğunu görebilirsiniz:

package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/username/go-workflow-engine/workflow"
)

// DBRegisterTask veritabanı kayıt adımını temsil eder
type DBRegisterTask struct{}

func (t *DBRegisterTask) Execute(ctx context.Context, state *workflow.State) error {
	fmt.Println("[Adım 1] Kullanıcı veritabanına kaydediliyor...")
	time.Sleep(500 * time.Millisecond) // Simüle edilen gecikme
	state.Set("userID", "usr_12345")
	return nil
}

// SendEmailTask e-posta gönderme adımını temsil eder
type SendEmailTask struct{}

func (t *SendEmailTask) Execute(ctx context.Context, state *workflow.State) error {
	userID, _ := state.Get("userID")
	fmt.Printf("[Adım 2] %s ID'li kullanıcıya hoş geldin e-postası gönderiliyor...\n", userID)
	return nil
}

func main() {
	// Yeni bir iş akışı oluşturuluyor
	engine := workflow.NewEngine()

	// Görevler tanımlanıyor
	dbTask := &DBRegisterTask{}
	emailTask := &SendEmailTask{}

	// İş akışı adımları ekleniyor
	engine.RegisterTask("RegisterDB", dbTask)
	engine.RegisterTask("SendEmail", emailTask)

	// Akış sırası belirleniyor
	engine.SetSequence([]string{"RegisterDB", "SendEmail"})

	// İş akışını başlat
	ctx := context.Background()
	err := engine.Start(ctx)
	if err != nil {
		log.Fatalf("İş akışı hatayla sonuçlandı: %v", err)
	}

	fmt.Println("İş akışı başarıyla tamamlandı!")
}

Yukarıdaki kodda, ilk olarak iki adet görev (task) tanımladık. Her görev, Execute metoduna sahip bir yapıdır (struct). state nesnesi sayesinde, ilk görevde ürettiğimiz userID bilgisini güvenli bir şekilde ikinci göreve aktarabildik. Sonuç olarak, karmaşık durum yönetimleri ile uğraşmadan, temiz ve modüler bir iş akışı elde etmiş olduk.

Gerçek Dünya Senaryosu: E-Ticaret Sipariş ve Kargo Süreci

Basit örneklerin ötesine geçerek, gerçek hayatta sıklıkla karşılaştığımız bir e-ticaret sipariş sürecini ele alalım. Bu senaryoda, kullanıcının sepeti onaylamasından kargoya verilmesine kadar geçen süreci tasarlayacağız. İş akışımız şu adımlardan oluşacak: Ödeme kontrolü, stok düşümü, fatura oluşturma ve kargo şirketine bildirim.

Bu süreçte yaşanabilecek olası hataları da göz önünde bulundurmalıyız. Örneğin, ödeme başarılı olmasına rağmen stok yetersiz çıkarsa ne olacak? Bu durumda “telafi edici işlem” (compensating transaction) çalışmalı ve ödeme iade edilmelidir. Aşağıdaki tabloda bu karmaşık akışın durum geçişlerini inceleyebilirsiniz:

Mevcut Durum Tetiklenen Olay Hedef Durum Hata Durumunda Aksiyon
Sipariş Alındı Ödeme Başarılı Ödeme Onaylandı İşlemi İptal Et
Ödeme Onaylandı Stok Rezerv Edildi Stok Güncellendi Ödemeyi İade Et (Rollback)
Stok Güncellendi Fatura Kesildi Fatura Hazır Stoku Geri Sal + Ödemeyi İade Et
Fatura Hazır Kargoya Verildi Sipariş Tamamlandı Destek Talebi Aç

Görüldüğü üzere, her başarılı adım bir sonrakini tetiklerken, herhangi bir adımda yaşanacak başarısızlık önceki adımların geri alınmasını gerektirir. go-workflow-engine, bu tür geri alma (rollback) senaryolarını yerleşik olarak destekler. Her göreve bir Rollback metodu tanımlayarak, hata anında sistemin otomatik olarak önceki adımları tersine işletmesini sağlayabilirsiniz. Bu sayede veri tutarlılığı (data consistency) her zaman korunmuş olur.

Gelişmiş Özellikler: Paralel Görevler ve Hata Yönetimi Nasıl Yapılır?

Gerçek dünya projelerinde tüm görevler doğrusal (linear) bir sırada ilerlemez. Bazı işlemlerin performansı artırmak adına paralel olarak çalıştırılması gerekir. Örneğin, bir sipariş tamamlandığında hem kullanıcıya SMS göndermek hem de muhasebe sistemine veri göndermek istiyorsak, bu iki işlemi aynı anda başlatabiliriz. Go dilinin goroutine yapısı sayesinde bu oldukça kolaydır ancak takibi zordur.

go-workflow-engine, paralel görev yönetimini dahili olarak destekler. Kütüphanenin sunduğu paralel grup (parallel group) özelliği sayesinde, birden fazla görevi aynı anda başlatabilir ve tümünün tamamlanmasını bekleyebilirsiniz. Aşağıdaki kod örneğinde, paralel görevlerin nasıl tanımlandığını görebilirsiniz:

// Paralel çalışacak görevlerin tanımlanması
parallelGroup := workflow.NewParallelGroup()

parallelGroup.AddTask("SMS_Notify", &SMSNotificationTask{})
parallelGroup.AddTask("Slack_Notify", &SlackNotificationTask{})

// Ana iş akışına paralel grubun eklenmesi
engine.RegisterGroup("Notifications", parallelGroup)

Hata yönetimi (error handling) tarafında ise kütüphane bize gelişmiş yeniden deneme (retry) politikaları sunar. Özellikle dış servislerle çalışırken, geçici ağ kesintileri nedeniyle hatalar alabiliriz. Bu durumlarda hemen hata vermek yerine, işlemi belirli aralıklarla yeniden denemek en doğru yaklaşımdır. Üstsel geri çekilme (exponential backoff) algoritması kullanarak, dış servisi yormadan akıllı yeniden denemeler yapılandırabilirsiniz.

go-workflow-engine ile Performans Optimizasyonu Nasıl Sağlanır?

Yüksek trafikli sistemlerde çalışan bir iş akışı motorunun en büyük sınavı performans ve bellek (memory) yönetimidir. Go dili, hafif yapısı sayesinde bu konuda oldukça avantajlıdır. Ancak, yanlış tasarlanmış bir iş akışı motoru bellek sızıntılarına (memory leaks) yol açabilir. go-workflow-engine kullanırken performansı en üst düzeye çıkarmak için dikkat edilmesi gereken bazı önemli noktalar vardır.

İlk olarak, görevler arasında taşınan veri miktarını minimumda tutmalısınız. state nesnesine devasa JSON nesneleri veya büyük veri yapıları koymak yerine, yalnızca ilgili verilerin benzersiz kimliklerini (ID) aktarmak ve veritabanı sorgularını görevlerin içinde yapmak bellek tüketimini ciddi oranda azaltacaktır.

Bununla birlikte, Go’nun çöp toplayıcısını (garbage collector) yormamak adına, nesne havuzlama (object pooling) tekniklerinden yararlanabilirsiniz. Sık oluşturulup yok edilen iş akışı nesneleri yerine, sync.Pool kullanarak önceden oluşturulmuş nesneleri yeniden kullanmak, yüksek eşzamanlılık gerektiren anlarda CPU üzerindeki yükü hafifletecektir. Ayrıca, veritabanı bağlantılarını ve HTTP istemcilerini (HTTP client) her görevde yeniden oluşturmak yerine, tek bir global örnek (singleton) üzerinden paylaşmak bağlantı havuzunun verimli kullanılmasını sağlar.

Sıkça Sorulan Sorular (SSS)

1. go-workflow-engine üretim ortamı (production) için uygun mudur?

Evet, oldukça uygundur. Özellikle mikroservisleriniz içinde hafif, hızlı ve harici bağımlılığı olmayan bir iş akışı çözümü arıyorsanız, bu kütüphane üretim ortamında yüksek performansla çalışacaktır. Ancak, çok büyük ve haftalarca süren uzun soluklu (long-running) iş akışları için Temporal gibi durum saklama özellikli sistemleri tercih etmek daha doğru olabilir.

2. İş akışının durumunu bir veritabanında saklayabilir miyim?

Evet, kütüphane durum saklama (state persistence) arayüzlerini (interface) destekler. Kendi veritabanı adaptörünüzü yazarak (PostgreSQL, MySQL veya Redis gibi), iş akışının kaldığı yeri veritabanına kaydedebilir ve sistem kapansa bile kaldığı yerden devam etmesini sağlayabilirsiniz.

3. Hata durumunda tüm akışı nasıl geri alabilirim (rollback)?

Her bir görevinize bir Rollback metodu tanımlayarak bu işlemi gerçekleştirebilirsiniz. İş akışı sırasında herhangi bir adımda hata oluştuğunda, motor otomatik olarak o ana kadar başarıyla tamamlanmış tüm adımların rollback metotlarını ters sırayla çalıştırır.

4. Diğer dillerle yazılmış servisleri bu akışa dahil edebilir miyim?

Kütüphane Go tabanlı olsa da, görevlerinizin içine HTTP veya gRPC çağrıları ekleyerek Java, Python veya Node.js ile yazılmış harici servisleri iş akışınızın bir parçası haline getirebilirsiniz.

#Teknoloji #WebGeliştirme #Golang #YazılımGeliştirme #WorkflowEngine

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Gönder

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.
Mobil sürümden çık