Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
When Are CQRS and Event Sourcing Unnecessary? Anti-Patterns and a Decision Guide
CQRS and event sourcing are unnecessary for plain CRUD, MVPs, static reference data, real-time consistent views, and inexperienced teams; the decision is made separately for each bounded context. Bookstore's context map, a decision flow for CQRS levels 1/2/3 and event sourcing, and four anti-patterns: CQRS ceremony on a single database, event sourcing everywhere, state-change events, and using internal events as an integration contract.
The Hard Parts of Event Sourcing: GDPR, Unique Constraints, and Dynamic Consistency Boundaries
Keep personal data out of events and encrypt it with crypto-shredding when it must stay; protect cross-stream uniqueness with a natural-key stream id or an Inline projection with a unique index, and rules spanning two aggregates with a Dynamic Consistency Boundary. AES-GCM crypto-shredding with Marten 9, a race between two concurrent registrations, a campaign quota with DcbConcurrencyException, and not calling external services on replay.
Event Versioning and Snapshots: Changing Event Schemas Without Breaking History
Written events are not changed; new code reads old shapes through tolerant deserialization, fixed JSON names, and upcasters, while snapshots are turned on based on measurement and together with a rebuild. A property rename that silently turns into zero, RespectRequiredConstructorParameters, a Marten upcaster, a Turkish-culture alias breaking on a namespace move, the cost of in-place migration, a snapshot serialization trap, and closing the books.
Projections: Building Read Models with Inline, Async, and Live Projections
A projection is a function that turns events into a read model shaped by a specific question; Inline runs in the same transaction as the events, Async runs in the background with the daemon, and Live is never stored. A stock badge, a monthly stock movement report, and reservations waiting to ship from the Bookstore stock ledger; checkpoints, changing a projection and rebuilding it, dead letters, and a failed rebuild emptying a read model.
Working with an Event Store: Append, Optimistic Concurrency, and Streams with Marten and PostgreSQL
An event store's core contract is appending to a stream only while it is at the expected version; Marten provides this on PostgreSQL with a unique index on (stream_id, version) and FetchForWriting. Setting up Marten 9, StartStream/Append, wiring a decider to FetchForWriting, a race between two writers, stream id strategies, KurrentDB versus your own table, and event names that break under a Turkish culture.
Event-Sourced Aggregates with the Decider Pattern: decide, evolve, and Given-When-Then Tests
The decider pattern splits an event-sourced model's decision logic into two pure functions: Decide produces new events from a command and a state, Evolve produces a new state from a state and an event. A decider for the stock ledger with C# 14 switch expressions, moving time into the command with TimeProvider, database-free Given-When-Then tests and a comparison with the OOP aggregate style, with real test output.
What Is Event Sourcing? Storing What Happened Instead of State
Event Sourcing stores what happened in an append-only event stream instead of the latest state, and derives state by folding those events. Using Bookstore's stock ledger: rehydration, audit, temporal queries, optimistic concurrency with an expected version, why an event store is not a message broker, why it goes together with CQRS, and its real costs.