Event sourcing, verilerinizin üzerine yazmayı bırakmanızı ister. Geleneksel bir CRUD uygulamasında, bir kullanıcının teslimat adresini güncellemek; satırı bulmak, değeri değiştirmek ve önceki durumu çöpe atmak anlamına gelir. Event sourcing ise farklı bir yol izler. Her değişikliği değişmez bir gerçek (immutable fact) olarak saklar: bir kullanıcı hesap oluşturdu, adresini güncelledi, e-postasını doğruladı. Sistemin mevcut durumu doğrudan saklanmaz; bu olayların sırayla yeniden oynatılmasıyla (replaying) hesaplanır.

Bu desen gerçek sorunları çözer. Denetim izleri (audit trails) ücretsiz yan ürünler haline gelir. Bir siparişin durumunu geçmişteki herhangi bir anda yeniden oluşturabilirsiniz. Tam olarak ne olduğunu yeniden oynatarak hata ayıklama (debug) yapabilirsiniz. Bunun karşılığındaki bedel ise karmaşıklıktır. Artık basit satırlar yerine gerçek akışlarını (streams of facts), okuma modellerini (read models) ve nihai tutarlılığı (eventual consistency) yönetirsiniz.

PostgreSQL, event store (olay deposu) olarak görev yapabilir. Çoğu ekip halihazırda bunu kullanıyor. ACID işlemleri, esnek veri yükleri (payloads) için JSONB ve kendini kanıtlamış yedekleme araçları sunar. İlk günden Kafka, Cassandra veya özel bir event-store veritabanı kullanmanıza gerek yoktur. Standart bir Postgres örneği, altyapı ayak izinizi genişletmeden event sourcing'in gerektirdiği işlem garantilerini ve denetim izlerini sağlar.

Bir Postgres Event Store'un Yapısı

Şema neredeyse utanç verici derecede basit olabilir. En azından, olayları ekleyen (append) ve asla yerinde güncelleme (update in place) yapmayan bir tabloya ihtiyacınız vardır. Pratik bir tasarım şöyledir:

  • id (bigserial veya UUID olarak), küresel sıralama işlevi görür.
  • stream_id, tek bir kullanıcıya veya siparişe ait tüm değişiklikler gibi ilgili olayları gruplandırmak için kullanılır.
  • event_type düz metin olarak: UserEmailChanged, PaymentReceived, InventoryAdjusted.
  • payload JSONB olarak, o olaya özgü verileri tutar.
  • occurred_at, zaman dilimi hassasiyetine sahiptir.
  • Her stream için version, iyimser eşzamanlılığı (optimistic concurrency) zorunlu kılar.

Değişmezlik kuralını uygulama kodunda veya bir veritabanı kısıtlaması (constraint) ile uygularsınız. (stream_id, version) üzerindeki benzersiz bir indeks, iki yazıcının aynı sıra numarasını eklemesini engeller. Bir komut geldiğinde, o stream için mevcut sürümü okur, bir artırır ve yeni olayı bir transaction içinde eklersiniz. Eğer başka bir işlem sizden önce davranırsa, benzersizlik kısıtlaması hata verir ve siz de komutu tekrar dener veya reddedersiniz.

Somut bir örnek düşünelim. Bir envanter sistemi yönetiyorsunuz. quantity sütununa sahip tek bir inventory satırı yerine, inventory_events tablosuna olaylar eklersiniz. ItemReceived on birim ekler. ItemReserved iki birim çıkarır. ItemShipped üç birim çıkarır. SKU-42 için mevcut stoğu bilmek için ilgili olay yüklerini (payloads) toplarsınız. Üç gün önceki stoğu bilmek için ise sadece o zaman damgasına kadar olanları toplarsınız. Eğer geçen Salı günü sevkiyat mantığınızdaki bir hata bir soruna yol açtıysa, gerçek durumu elde etmek için olayları düzeltilmiş kodla yeniden oynatırsınız. Bunu basit bir UPDATE ifadesiyle yapamazsınız.

Sizi Sorunlardan Uzak Tutacak Prensipler

Postgres üzerine inşa etmek, disiplin ihtiyacını ortadan kaldırmaz. Aşağıdaki prensipler event-sourced sistemler için doğrudan geçerlidir.

Basit tutun. Karmaşıklık güvenilirliği öldürür. Henüz çalışan tek bir akış (flow) bile yayınlamadan genel bir event framework'ü oluşturma dürtüsüne karşı koyun. Değerini kanıtlamak için tek bir tablo, olayları eklemek için bir repository fonksiyonu ve okuma modellerini oluşturmak için bir projection worker yeterlidir. Araçları yalnızca somut bir sorun ortaya çıktığında ekleyin.

Küçük başlayın. Tüm monolitik yapınızı yeniden yazmayın. Denetim izinin (audit trail) getirdiği ek yükü (overhead) karşılayacağı tek bir sınırlandırılmış bağlam (bounded context) seçin. Bir faturalandırma defteri, bir iş akışı motoru veya bir envanter rezervasyon sistemi iyi adaylardır. Bu tek hattı uçtan uca inşa edin. Üretim ortamında çalışmasına izin verin. Sonra genişleyip genişlemeyeceğinize karar verin.

Önce başarıyı tanımlayın. Event sourcing varsayılan bir mimari değildir; belirli ihtiyaçlara yönelik bir çözümdür. Eğer gereksiniminiz sadece en son durumu takip etmekse, CRUD daha hızlı ve daha ucuzdur. Eğer zamansal sorgulara, sıkı denetlenebilirliğe veya okuma modellerini talep üzerine yeniden oluşturma yeteneğine ihtiyacınız varsa, o zaman event sourcing mantıklıdır. Taahhütte bulunmadan önce hangi sorunu çözdüğünüzü bilin.

Optimize etmeden önce ölçün. Mütevazı donanımlar üzerindeki modern PostgreSQL, basit bir append-only tablo ile saniyede binlerce olayı işleyebilir. İzleme (monitoring) araçlarınız daha basit çözümlerin tükendiğini kanıtlayana kadar event store'unuzu shard etmeyin veya karmaşık bölümlendirme (partitioning) şemaları getirmeyin. Sorguladığınız alanları indeksleyin. autovacuum ayarlarını append-only iş yüklerine göre optimize edin. Sonra tekrar ölçün.

Test everything. Unit test your event handlers. Integration test the append path. Most importantly, test failure scenarios. What happens when two nodes append to the same stream simultaneously? What happens when a projection worker crashes mid-batch? Write tests that verify your optimistic concurrency and your at-least-once delivery guarantees.

Monitor in production. The events table will grow. Unlike a normalized schema where updates keep row counts flat, event sourcing is intentionally additive. Track table size, disk I/O, and the lag between your write model and your read model projections. Set alerts on projection lag before your users notice stale data.

Automate manual tasks. Manual schema changes, manual projection rebuilds, and manual event replay are ticking time bombs. Script your migration strategy. If you evolve an event schema, automate the upcasting or transformation so that old events can be replayed through new logic without human intervention at midnight.

Document your choices. Write down why specific streams exist, what each event type means, and when the team should choose CRUD over events. Event sourcing introduces cognitive load. Good documentation prevents a new engineer from guessing wrong and appending malformed events to a critical stream.

Traps That Waste Months

Event sourcing has a way of sounding elegant in a diagram and painful in production. Watch for these traps.

Underestimating complexity. Replaying events to rebuild state is conceptually simple. Managing idempotency, snapshotting for performance, and compensating transactions across aggregates is not. Break your system into small pieces. Solve one stream at a time.

Over-engineering. Do not provision a multi-node Kafka cluster because you imagine your event volume will one day require it. Postgres can carry you surprisingly far. Introduce new infrastructure only when you have a measured bottleneck that you cannot fix within your current setup.

Ignoring technical debt. Old event schemas linger forever. If you change your OrderCreated payload, you still have ten million historical events in the old shape. Track this debt. Plan backward-compatible readers or migration scripts. Do not let the burden of legacy events slow every new feature.

Choosing tools the team cannot run. The best architecture fails if only one person understands it. If your team knows Postgres and SQL, start there. If you introduce a specialized event store, make sure you have the operational expertise to debug it at two in the morning.

A Practical Starting Point

If this approach fits your problem, do not wait for a five-quarter rewrite. Start this week.

Audit your current systems. Find a place where an audit trail would solve real pain. Maybe it is an order state machine that currently maintains a single status column. Perhaps it is a financial ledger where balance corrections require manual database patches. Pick one gap where overwriting state has hurt you.

Then pick one small improvement you can make today. Create one event table. Model one stream. Write one projection that builds a read model from those events. Deploy it behind a feature flag. Watch it handle real traffic.

Event sourcing with PostgreSQL is not magic. It is a practical tool for teams that need to know not just where things are, but how they got there. Build slowly, measure honestly, and let your actual requirements guide the architecture.

Source: https://dev.to/therizwansaleem/event-sourcing-with-postgresql-using-the-database-as-an-event-store-2kd4