Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Component Diagrams: Provided/Required Interfaces, Ports and Drawing .NET Modules
A component diagram splits a system into replaceable modules and shows which interfaces each module provides (lollipop) and which it requires (socket); in .NET that means a public contracts project, an internal implementation and DI registrations in the composition root. In this post we draw the Leave, Payroll and Notification modules with ports and delegation connectors and read the wiring back from the DI registrations.
Strategy Pattern in Depth: Keyed Services, IEnumerable<IStrategy> and Func for Runtime Selection
Writing a strategy is easy; choosing the right one at runtime is the hard part: keyed services select by a single key, IEnumerable<IStrategy> with CanHandle looks at several properties of the object, and Dictionary<string, Func<>> selects with the least ceremony when formulas have no dependencies. How registration order silently changes prices, why keyed registrations don't show up in GetServices, and IComparer and StringComparer as strategies in the BCL.
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.
Factory Method and Static Factories: Delegating Creation to Subclasses or a Single Method
The GoF Factory Method leaves the choice of product to a subclass; a static factory method is a single named method that replaces a constructor, can cache and can return a hidden subtype. The factory-method-in-constructor bug and CA2214, DbConnection.CreateDbCommand, ILoggerFactory and IHttpClientFactory, Func<T>, ActivatorUtilities and keyed services in DI, and ownership of IDisposable products, with real output.
Singleton Pattern: Thread-Safe Lazy<T>, Static Fields and Why the DI Singleton Lifetime Is Better
If you need a single instance, the cleanest route is not the classic Singleton but registering with AddSingleton in the DI container: the single-instance guarantee stays, while global access and hidden dependencies go away. The race in a naive Singleton, the history of double-checked locking, static readonly and beforefieldinit, Lazy<T>'s exception caching, captive dependencies and shared state inside a singleton, with real output.
Dependency Inversion Principle: DIP vs DI vs IoC and the Composition Root
DIP asks for the compile-time dependency to flow from details to abstractions and for the abstraction to be owned by its user; DI is only the technique of supplying dependencies from outside, and IoC is control of the flow moving to a framework. Which project the interface belongs in, using Program.cs as the composition root, and why a service locator defeats DIP.
Interface Segregation Principle: Fat Interfaces and Role Interfaces
ISP means no client should be forced to depend on methods it doesn't use, and interfaces are cut by client (role interfaces), not by class. One method added to a 14-method IOrderRepository breaking three files, splitting into read/write/report roles and forwarding in DI, IReadOnlyList<T> and IList<T> still separate in .NET 10, single-method interface inflation and the cost of piercing ISP with default interface methods.
Open/Closed Principle: Extending Without Modifying and the Speculative Generality Trap
OCP means adding new behavior as a new implementation without opening existing, working code; but any code can only be closed against a particular axis of change. Closing a growing notification switch with INotificationChannel and .NET 8 keyed services, HttpClient's DelegatingHandler chain, lightweight extension points with Func, and the cost of speculative generality.
Dependency Injection Basics in C#: Lifetimes, the Container and Captive Dependencies
Constructor injection with Microsoft.Extensions.DependencyInjection, singleton/scoped/transient, the container's disposal duty, captive dependencies and ValidateScopes, multiple registrations, TryAdd and factories.
EF Core in ASP.NET Core: AddDbContext, Scoped Lifetime, Factory and Pooling
We wire DbContext into ASP.NET Core with AddDbContext, watch the scoped lifetime through ContextId, solve the singleton and background service traps, and see with real output when AddDbContextFactory and AddDbContextPool help.
Dependency Injection in ASP.NET Core: Lifetimes and the Captive Dependency Trap
We explore dependency injection in ASP.NET Core with real output: constructor injection, singleton/scoped/transient lifetimes, captive dependencies, scope validation, IServiceProvider and the [FromServices] detail in .NET 6.