Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Microservice Communication Styles: Sync Request/Response, Async Events and Temporal Coupling
If the caller must wait for the answer, communication is synchronous however much await the code has, and it binds two services through temporal coupling; if the answer isn't needed now, prefer an event or a local copy. A comparison of REST, gRPC (including deadlines reaching the server) and messages, a checkout simulation while Inventory is being deployed, the command versus event split, and a decision table.
Message Contracts and Schema Evolution: CloudEvents, AsyncAPI, and Versioning
A message contract is a public API between independently deployed producers and consumers; adding a field is safe, while renaming a field or adding an enum value breaks consumers silently or loudly, so changes are made with expand-contract and versioned type names. Real breakage output with System.Text.Json, the CloudEvents 1.0 envelope with the C# SDK, an event catalog validated with AsyncAPI 3.1, and schema registry compatibility modes.
Implementing a Saga in .NET: Persisted State Machines, Timeouts, and Compensation
To make a saga reliable in .NET you store its state in a table found by correlation id and guarded by a concurrency token, leave the decision to a pure switch expression over (step, message) pairs, write commands to an outbox in the same transaction, and give every waiting step a timeout tied to that step. A lost payment reply, a timeout race, a late payment and a failed refund with EF Core and SQLite and real output; the same saga in MassTransit 8 and Wolverine, with license notes.
Transactional Outbox and Inbox: Understanding and Designing Around the Dual Write Problem
Committing to the database and separately publishing to the broker opens two failure windows; the fix is to write the message to an outbox table in the same transaction as the business data, publish it with a separate relay, and drop the relay's duplicates with an inbox on the consumer side. Dual write scenarios, polling vs CDC relays, losing messages with a watermark on PostgreSQL, and multiple relays with SKIP LOCKED, all with real output.
Idempotent Consumers: What Happens When the Same Message Arrives Twice?
The reliable way to keep the same message from being processed twice is to write a stable dedup key (a MessageId or business key) to a per-consumer unique inbox table in the same transaction as the business data, and to let the unique constraint decide between concurrent duplicates. The check-then-process failure window, a crash before commit, two concurrent copies, and a two-phase record for an email that can't join the transaction, with real output on EF Core 10 and SQL Server 2025.
Message Ordering and Partitioning: Where Ordering Guarantees Begin and End
Brokers don't give a global order; Kafka partition keys, Azure Service Bus sessions, and RabbitMQ single active consumer only give order per key, and consumers should still be order-independent through version checks. Partitioning by OrderId, the hot partition a BookId key creates, keys moving when partitions are added, and the librdkafka versus Java partitioner difference, with real output on Kafka 4.2.
Delivery Guarantees: At-Most-Once, At-Least-Once, and the Truth About "Exactly-Once"
The order of the ack and the work decides the delivery guarantee: ack first means at-most-once, work first means at-least-once; exactly-once is only possible for operations the broker itself controls, and for external side effects at-least-once plus an idempotent consumer gives effectively-once. Redelivery, publisher confirms, the three sources of duplicates, and a poison message landing in a dead-letter queue on RabbitMQ 4.4, with real output.
Channels: Point-to-Point Queues, Publish/Subscribe Topics, and Competing Consumers
In a point-to-point channel each message goes to exactly one consumer, in publish/subscribe each subscriber group gets its own copy, and competing consumers scale by sharing a channel. Two adapters publishing the same OrderPlaced to RabbitMQ and Kafka, exchanges/queues/bindings versus topics/partitions/consumer groups, Kafka 4.2 share groups, and choosing between a queue and a log, with real output.
Message Types: Commands, Events, and Queries; Event Notification and Event-Carried State Transfer
A command is imperative and goes to one receiver, an event is a past-tense fact that doesn't know its receivers, and a query expects an answer; that distinction decides a message's name, its contract owner, and its channel. An envelope with message, correlation, and causation ids, Fowler's event notification versus event-carried state transfer, and domain versus integration events, with real output.
Why Messaging? From Synchronous Call Chains to Asynchronous Communication
A synchronous HTTP chain requires every dependency to be up at the same moment; dropping work on a queue breaks that temporal coupling and shields checkout from an email provider outage. Chained availability, an in-process queue with Channel<T>, the broker's role as buffer and load leveler, Web-Queue-Worker, and the price of asynchrony, with real output.