Mert Özen Further With Every Line

Blog

Mert Özen's blog posts on software development, backend, frontend, and DevOps.

Series
15 posts found
Splitting Bookstore into Microservices: Target Architecture, Service List and the Rules of This Series
Backend PART 01

Splitting Bookstore into Microservices: Target Architecture, Service List and the Rules of This Series

We split Bookstore into five services: Catalog, Ordering, Inventory, Payments and Notifications; each owns its database, identities between services are ISBNs and Guids, and shared code is limited to contracts and ServiceDefaults. The post shows the target architecture, which edges are sync and which async, the reasons behind the Aspire, YARP, RabbitMQ, WolverineFx and Keycloak decisions, and the architecture test that guards the rule in CI, with real output.

14 Nov 2026 14 min 3
The Solution Skeleton: Aspire AppHost, ServiceDefaults and a One-Command Local Environment
Backend PART 02

The Solution Skeleton: Aspire AppHost, ServiceDefaults and a One-Command Local Environment

An Aspire AppHost describes Bookstore's five services, a SQL Server database per service, Redis, RabbitMQ and Keycloak in one C# file and runs them with a single command; ServiceDefaults adds telemetry, health checks, service discovery and resilience to every service with one line. We run migrations as a separate resource by starting the service binary with --migrate and wait for it with WaitForCompletion; real start times show why WaitFor isn't enough.

14 Nov 2026 14 min 2
The Catalog Service: Its Own Database, a REST API and Read-Heavy Optimizations
Backend PART 03

The Catalog Service: Its Own Database, a REST API and Read-Heavy Optimizations

We turn Catalog into a service that owns its database and is tuned for read traffic: search and paging with AsNoTracking projections, a shared output cache in Redis with tag eviction, URL segment versioning, ProblemDetails, an OpenAPI contract generated at build time and committed, and a breaking change check with oasdiff. Price and title changes become versioned BookPriceChanged and BookRenamed events; the same version, as a concurrency token, stops 4 of 8 parallel requests with 409.

14 Nov 2026 13 min 2
The Ordering Service: A Rich Domain Model, Its Own Database and the Order API
Backend PART 04

The Ordering Service: A Rich Domain Model, Its Own Database and the Order API

Ordering keeps the Order aggregate in its own database, reads book titles and prices from a local copy filled once instead of asking Catalog, and rejects an order with 409 when the price the customer saw in the cart doesn't match the copy. Orders are created with 201 even while Catalog is down; how SQL Server sorts Guid v7, a non-clustered primary key and a projection for the order list are shown with real output.

14 Nov 2026 14 min 6
The Inventory Service and Sync Communication with gRPC: Stock Reservation, Deadlines and Resilience
Backend PART 05

The Inventory Service and Sync Communication with gRPC: Stock Reservation, Deadlines and Resilience

Ordering uses Inventory for stock reservation through a gRPC call with a 2-second deadline; the reservation is idempotent by order id, uses optimistic concurrency with rowversion and frees itself after a 15-minute TTL. The post shows, with real output, conflicts among 20 parallel orders, a deadline cancelling the work on the server, the cleartext HTTP/2 trap, breaking change checks with buf, and the cost to checkout while Inventory is down.

14 Nov 2026 17 min 6
Async Communication with Integration Events: RabbitMQ, a Messaging Library and the Transactional Outbox
Backend PART 06

Async Communication with Integration Events: RabbitMQ, a Messaging Library and the Transactional Outbox

Catalog and Ordering write integration events to their own databases through WolverineFx's EF Core outbox and send them to RabbitMQ after the commit; even with RabbitMQ down a price change returns 204, and the event reaches Ordering once the broker is back. Ordering's durable inbox drops repeated messages, a per-field version drops late old events, and poison messages land in the service's own dead letter queue with exception details.

14 Nov 2026 13 min 5
The Checkout Saga: Orchestrating Order, Stock and Payment with Compensation
Backend PART 07

The Checkout Saga: Orchestrating Order, Stock and Payment with Compensation

A Wolverine saga in Ordering owns checkout: it asks for stock and payment with messages, releases the stock and cancels the order on a declined payment or a 15-minute timeout, and turns a late payment for a cancelled order into a refund. Orders are now accepted with 202; even with Inventory down the API answers in 23 ms and the order is confirmed once Inventory is back. The saga's steps are checked with unit tests that need no database.

14 Nov 2026 12 min 6
The Payments Service: A Fake Payment Provider, Idempotent Charges and Webhook Callbacks
Backend PART 08

The Payments Service: A Fake Payment Provider, Idempotent Charges and Webhook Callbacks

Payments sends charges to the payment provider with an Idempotency-Key derived from the order, does not repeat POSTs in the HTTP layer and treats a timeout as an unknown result, not a failure; a signed, replay-protected webhook or a reconciliation job settles the outcome. The provider's model stays in an anti-corruption layer, and a late payment for a cancelled order turns into a refund end to end.

14 Nov 2026 10 min 7
API Gateway and BFF: A Single Entry Point and Aggregation for Angular with YARP
Backend PART 09

API Gateway and BFF: A Single Entry Point and Aggregation for Angular with YARP

Bookstore.Bff becomes Angular's single entry point with YARP: proxy routes resolved through service discovery, a rewritten Location header that no longer leaks the internal address, and a rate limit on the order route. The book page comes from an aggregation endpoint that calls Catalog and Inventory in parallel; with Inventory down the page arrives without stock in 533 ms, with Catalog down it returns 503 in 2 seconds instead of a 500 after 30.

14 Nov 2026 9 min 6
Authentication and Service-to-Service Trust: Keycloak, BFF Sessions, Token Propagation and Client Credentials
Backend PART 10

Authentication and Service-to-Service Trust: Keycloak, BFF Sessions, Token Propagation and Client Credentials

The Keycloak realm is imported from a JSON file without secrets; the BFF signs users in with OIDC and keeps their tokens in an HttpOnly cookie session, forwards the user's access token to Ordering on the order routes, and uses its own client credentials token for the stock query. Every service validates tokens itself by audience and role; a token issued for Inventory gets 401 from Ordering, and identity enters events as a CustomerId, not as a token.

14 Nov 2026 11 min 5
The Angular Frontend: Handling Eventual Consistency in the UI and Showing Live Order Status
Backend PART 11

The Angular Frontend: Handling Eventual Consistency in the UI and Showing Live Order Status

When an order is accepted with a 202, the UI should say "waiting for confirmation", not "confirmed"; Notifications pushes status changes over SignalR, but the page reads the order from Ordering again on every push. The Angular 22 app is served from one origin behind the BFF; a push arriving before the POST response, falling back to polling while Notifications is down, once-only e-mails with Redis, and stock, payment and price failures were tried in a real browser.

14 Nov 2026 14 min 6
Distributed Tracing and Observability: Following One Order from Angular to RabbitMQ
Backend PART 12

Distributed Tracing and Observability: Following One Order from Angular to RabbitMQ

To see one order end to end in a single trace, ServiceDefaults needs Wolverine's ActivitySource and the gRPC client instrumentation; the trace context travels in the message's parent-id header. A trace that ended after two services grew to 81 spans across five; most of checkout's 1.15 seconds is the payment provider, the trace breaks at reconciliation, and order and payment metrics are measured with IMeterFactory.

14 Nov 2026 11 min 8
Blog Map