Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Mediator Pattern: Centralizing Object Conversations and "Do I Really Need MediatR?"
Mediator gathers the interaction rules of components that call each other directly into one object; MediatR is less this pattern than an in-process dispatcher that finds a handler by request type. We fix the bug produced by checkout components that know each other with a CheckoutForm mediator, study Blazor's EditContext as a real example, then write the same order placement with MediatR 14.3, a plain service class and our own 30-line dispatcher and compare them.
Iterator and Interpreter: Two Patterns Built into C# — yield return and Expression Trees
Iterator lets you walk a collection's elements in order without exposing its internals, and in C# it is built into the language as IEnumerable, IEnumerator and yield return; Interpreter represents a small language's grammar as an AST and evaluates it. With real output we read a paged catalog API with yield and IAsyncEnumerable, write a small promotion rule language and compile it to an expression tree, look at the interpreter inside Regex and draw the security boundary for dynamic expressions.
Command Pattern: Turning Requests into Objects, Undo/Redo and Queuing
Command packages a request as an object, so an invoker can run it, undo it, queue it and log it without knowing what it does. An undo/redo stack and a MacroCommand in Bookstore's catalog editor, System.Windows.Input.ICommand in the BCL, serializing commands with System.Text.Json and running them in the background through a Channel, how a GoF command differs from a CQRS command, MediatR 13's license and a library-free dispatcher.
Chain of Responsibility: ASP.NET Core Middleware, DelegatingHandler and Validation Chains
Chain of Responsibility passes a request along a chain of handlers until one handles it, breaking the coupling between sender and receiver; ASP.NET Core middleware and DelegatingHandler are short-circuiting forms of the pattern. A discount approval chain in Bookstore, the hidden business rule carried by IEnumerable registration order in DI, a validation chain that stops at the first error versus a pipeline that collects everything, and the trap of a request falling off the end of the chain.
Proxy Pattern: Lazy Loading, Remote Proxies and Intercepting with DispatchProxy
A proxy implements the same interface as the real object and controls access to it: it defers creation (virtual), checks permissions (protection), represents a remote service (remote) or does extra work on access (smart reference). EF Core's Castle.Proxies.AuthorProxy and the N+1 it hides, a virtual proxy with Lazy<T>, typed HttpClients as remote proxies, DispatchProxy's async and TargetInvocationException pitfalls, and how it differs from Decorator.
Flyweight Pattern: Saving Memory with Shared State — string.Intern and ArrayPool
Flyweight stores the repeating, immutable part of many objects (intrinsic state) once through a factory and shares it, keeping the line-specific part (extrinsic state) outside. A BookInfoFactory that cuts a million-line report from 377 MB to 30 MB, the side effects of string.Intern, the Task and array instances .NET already shares, why ArrayPool is an object pool rather than a flyweight, and BenchmarkDotNet measurements.
Facade Pattern: Presenting a Complex Subsystem Through a Single Door
A facade gathers the knowledge of using several subsystems in the right order, with the right compensation steps, into one class; it gives clients an easy entry point without closing off the subsystem. A CheckoutFacade in Bookstore that coordinates stock, payment, invoicing and notification, the real code behind File.ReadAllTextAsync and GetFromJsonAsync, how Facade differs from Service Layer, Adapter and Mediator, and the god facade trap.
Decorator Pattern: Adding Caching, Logging and Retry Without Touching the Code
Decorator adds behavior to an object at runtime by implementing the same interface and delegating to the object inside; the order of decorators is a design decision that determines what they measure and what they protect. Adding cache, logging and retries to Bookstore's catalog, the Stream family and ownership with leaveOpen, manual DI registration and Scrutor's Decorate, DelegatingHandler and how default interface methods silently bypass a decorator.
Composite Pattern: Treating Tree Structures Through One Interface
Composite puts leaves and the composites containing them behind the same interface, so the client can use a single item and a nested tree without telling them apart; the recursion stays in the only place that knows the tree, inside the composite. Pricing nested book bundles in Bookstore, the transparency versus safety choice, IConfiguration and CompositeChangeToken, and the stack overflow of a cyclic tree.
Bridge Pattern: Letting Abstraction and Implementation Grow Independently
Bridge splits two independent axes of change (like message type and channel) into two hierarchies connected by a reference, instead of squeezing them into one inheritance hierarchy, so the class count becomes a sum rather than a product. Moving Bookstore's notifications from a 3 × 3 class explosion to 3 + 3, the HttpClient/HttpMessageHandler and ILogger/ILoggerProvider bridges and how it differs from Adapter.
Adapter Pattern: Fitting a Third-Party SDK to Your Own Interface
Adapter fits a class you can't change to the interface the client expects; in C# that is almost always an object adapter, built with composition. Connecting two imaginary payment SDKs to a single IPaymentGateway, turning a callback into a Task with TaskCompletionSource, mapping error codes to domain exceptions and verifying both adapters with the same contract test.
Prototype Pattern: record and with, Shallow vs Deep Copy and Why ICloneable Is Discouraged
Prototype creates a new object by copying a configured sample instead of building it from scratch; in C#, the with expression on records does this while preserving the runtime type, but the copy is shallow. A prototype registry, controlled deep copies with a copy constructor, ICloneable's vague contract, the measured cost of copying through JSON and the with traps that skip validation.