<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Javier Escobar]]></title><description><![CDATA[I write about software development and cloud technologies.]]></description><link>https://blog.javierescobar.net</link><image><url>https://cdn.hashnode.com/uploads/logos/6344b7c03e763e0bd6bd5d33/e8ee5375-db84-41d7-93fe-ca96300b9b43.jpg</url><title>Javier Escobar</title><link>https://blog.javierescobar.net</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 16 Apr 2026 17:32:38 GMT</lastBuildDate><atom:link href="https://blog.javierescobar.net/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Rethinking Mediator: Why I Opt for Simplicity in .NET Projects]]></title><description><![CDATA[The Mediator pattern has become almost a default choice in many .NET projects I’ve seen over the years. It promises better separation of responsibilities, cleaner controllers, lower coupling, and the idea that it is the right enterprise approach for ...]]></description><link>https://blog.javierescobar.net/rethinking-mediator-why-i-opt-for-simplicity-in-net-projects</link><guid isPermaLink="true">https://blog.javierescobar.net/rethinking-mediator-why-i-opt-for-simplicity-in-net-projects</guid><category><![CDATA[dotnet]]></category><category><![CDATA[mediator]]></category><dc:creator><![CDATA[Javier Escobar Espinoza]]></dc:creator><pubDate>Tue, 11 Nov 2025 13:00:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/_SEbdtH4ZLM/upload/34e3d99f61c5d0b18ad8409967cf2468.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The Mediator pattern has become almost a default choice in many .NET projects I’ve seen over the years. It promises better separation of responsibilities, cleaner controllers, lower coupling, and the idea that it is the right enterprise approach for applying CQRS.</p>
<p>I gave it a real chance. However, over time, I realized its practical value was much lower than expected. Even worse, the developer experience became slower. Following the flow of the code, something as simple as pressing F12 turned into a chain of jumps, references, and layers that were hard to trace. In practice, Mediator often hides the logic instead of organizing it.</p>
<p>Let’s look at some of the most common reasons why this pattern is recommended, and why they don’t always represent a real advantage in practice.</p>
<h3 id="heading-1-the-myth-of-low-coupling">1. The myth of low coupling</h3>
<p>One of the most common arguments is that Mediator reduces coupling between layers, since the controller does not depend directly on a specific use case. In reality, this “low coupling” is relative: the dependency doesn’t disappear, it just moves to a generic interface such as IMediator. The problem is that this makes it harder to trace what actually happens in the code.</p>
<p>This is more natural:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ReportGenerator</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IReportRepository _repo;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ReportGenerator</span>(<span class="hljs-params">IReportRepository repo</span>)</span> =&gt; _repo = repo;

    <span class="hljs-function"><span class="hljs-keyword">public</span> Task&lt;Report&gt; <span class="hljs-title">Generate</span>(<span class="hljs-params">DateTime <span class="hljs-keyword">from</span>, DateTime to</span>)</span>
        =&gt; _repo.GenerateAsync(<span class="hljs-keyword">from</span>, to);
}

<span class="hljs-comment">// Usage</span>
_reportGenerator.Generate(<span class="hljs-keyword">from</span>, to);
</code></pre>
<p>Than this:</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">GenerateReportQuery</span>(<span class="hljs-params">DateTime From, DateTime To</span>) : IRequest&lt;Report&gt;</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">GenerateReportHandler</span> : <span class="hljs-title">IRequestHandler</span>&lt;<span class="hljs-title">GenerateReportQuery</span>, <span class="hljs-title">Report</span>&gt;
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IReportRepository _repo;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">GenerateReportHandler</span>(<span class="hljs-params">IReportRepository repo</span>)</span> =&gt; _repo = repo;

    <span class="hljs-function"><span class="hljs-keyword">public</span> Task&lt;Report&gt; <span class="hljs-title">Handle</span>(<span class="hljs-params">GenerateReportQuery query, CancellationToken ct</span>)</span>
        =&gt; _repo.GenerateAsync(query.From, query.To);
}

<span class="hljs-comment">// Usage</span>
_mediator.Send(<span class="hljs-keyword">new</span> GenerateReportQuery(<span class="hljs-keyword">from</span>, to));
</code></pre>
<p>The code looks decoupled on paper, but in practice, every jump through the mediator adds friction to understanding and debugging. A controller that depends on a façade or an application service is also decoupled from concrete implementations, but keeps a clear and predictable link to the actual use cases of the system.</p>
<h3 id="heading-2-easy-to-test">2. Easy to Test?</h3>
<p>It is often said that Mediator makes testing easier because each handler can be tested on its own. This can be true, but it is not unique to Mediator. Any well-structured design with clear separation of responsibilities provides the same benefit.</p>
<p>In fact, the extra boilerplate (requests, handlers, configurations) usually increases the effort needed to set up tests instead of reducing it. Testing becomes easy when responsibilities are small and explicit, not when wrapped inside an extra abstraction.</p>
<h3 id="heading-3-separation-of-responsibilities">3. Separation of Responsibilities</h3>
<p>Mediator is also associated with a better separation of responsibilities: controllers stay thin, and each handler represents one use case. But again, this is not something exclusive to the pattern. The same clarity can be achieved with explicit classes that describe exactly what they do: <code>ReportGenerator</code>, <code>PaymentProcessor</code>, etc.</p>
<p>This looks clean and thin:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Each operation is forced into a Request + Handler</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">ApproveOrderCommand</span>(<span class="hljs-params">Guid OrderId</span>) : IRequest</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">ApproveOrderHandler</span> : <span class="hljs-title">IRequestHandler</span>&lt;<span class="hljs-title">ApproveOrderCommand</span>&gt;
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IOrderRepository _orders;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ApproveOrderHandler</span>(<span class="hljs-params">IOrderRepository orders</span>)</span> =&gt; _orders = orders;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Handle</span>(<span class="hljs-params">ApproveOrderCommand cmd, CancellationToken ct</span>)</span>
    {
        <span class="hljs-keyword">var</span> order = <span class="hljs-keyword">await</span> _orders.GetAsync(cmd.OrderId);
        order.Approve();
        <span class="hljs-keyword">await</span> _orders.SaveAsync(order);
    }
}
</code></pre>
<p>But this too, and without creating trivial classes (commands, handlers) that add zero semantics to the code:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">OrderApproval</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IOrderRepository _orders;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">OrderApproval</span>(<span class="hljs-params">IOrderRepository orders</span>)</span> =&gt; _orders = orders;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Approve</span>(<span class="hljs-params">Guid orderId</span>)</span>
    {
        <span class="hljs-keyword">var</span> order = <span class="hljs-keyword">await</span> _orders.GetAsync(orderId);
        order.Approve();
        <span class="hljs-keyword">await</span> _orders.SaveAsync(order);
    }
}
</code></pre>
<p>With Mediator, structure is often dictated by the library instead of intentional design. The result is many trivial classes, less context, and slower navigation.</p>
<h3 id="heading-4-pipeline-behaviors">4. Pipeline Behaviors</h3>
<p>If there is one real advantage of Mediator, it is the ability to easily define pipelines for handling common concerns such as logging, validation, or transactions. This helps avoid code duplication and keeps these concerns separated from the business logic.</p>
<p>However, it is very likely that middlewares, action filters, and decorators might cover most scenarios. Here is where I ask, do we really need it? In most cases, probably no.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Mediator aims to solve real problems, such as coupling, testing, and consistency, but it does so by adding an extra layer of infrastructure that often complicates things more than it simplifies them.</p>
<p>Developers spend most of their time reading code, so giving them the best experience when navigating through it is essential. Mediator makes it difficult to follow the flow, increasing cognitive load and slowing navigation. And it even gets worse when using notifications.</p>
<p>A clean design with explicit dependencies and well-defined layers can achieve the same results without unnecessary abstractions. In most real-world projects, we don’t need more indirection; we need more intention.</p>
]]></content:encoded></item><item><title><![CDATA[Boosting C# Performance: A Guide to BenchmarkDotNet]]></title><description><![CDATA[In the fast-paced world of software development, the need to assess the performance of algorithms or compare the efficiency of libraries is a recurring challenge. Developers often find themselves racing against tight deadlines to deliver a certain fe...]]></description><link>https://blog.javierescobar.net/boosting-c-performance-a-guide-to-benchmarkdotnet</link><guid isPermaLink="true">https://blog.javierescobar.net/boosting-c-performance-a-guide-to-benchmarkdotnet</guid><category><![CDATA[benchmarkdotnet]]></category><category><![CDATA[Benchmark]]></category><category><![CDATA[.NET]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[performance]]></category><category><![CDATA[Performance Testing]]></category><category><![CDATA[Tips for Developers]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[code efficiency]]></category><dc:creator><![CDATA[Javier Escobar Espinoza]]></dc:creator><pubDate>Sun, 03 Dec 2023 17:33:28 GMT</pubDate><content:encoded><![CDATA[<p>In the fast-paced world of software development, the need to assess the performance of algorithms or compare the efficiency of libraries is a recurring challenge. Developers often find themselves racing against tight deadlines to deliver a certain feature or product on time, so benchmarking is a thing occasionally overlooked. However, investing some extra time might pay off in the future. Therefore, benchmarking can be a valuable addition to our toolkit, especially in scenarios where performance is critical.</p>
<p>In C#, we have <code>BenchmarkDotNet</code> to accomplish this task very precisely. Here is how to use it.</p>
<h2 id="heading-example">Example</h2>
<p>Let's start comparing string concatenation algorithms. As it is well known, in .NET it is always preferred to use the StringBuilder class. Let's see if that is true.</p>
<h3 id="heading-project-setup">Project setup</h3>
<p>First, let's create a Console project and install the required Nuget package.</p>
<pre><code class="lang-bash">dotnet add package BenchmarkDotNet
</code></pre>
<blockquote>
<p>Please, keep in mind that <a target="_blank" href="https://benchmarkdotnet.org/articles/guides/how-to-run.html">BenchmarkDotNet works only with Console Apps</a>.</p>
</blockquote>
<p>Next, we will create our benchmarking class like this:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> System.Text;
<span class="hljs-keyword">using</span> BenchmarkDotNet.Attributes;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">Benchmarking</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">StringConcatenationBenchmark</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">int</span> NumberOfConcatenations = <span class="hljs-number">100</span>;

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">AdditionAssignment</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">var</span> result = <span class="hljs-string">""</span>;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; NumberOfConcatenations; i++)
        {
            result += i;
        }
        <span class="hljs-keyword">return</span> result;
    }

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">StringConcat</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">var</span> result = <span class="hljs-string">""</span>;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; NumberOfConcatenations; i++)
        {
            result = <span class="hljs-keyword">string</span>.Concat(result, i);
        }
        <span class="hljs-keyword">return</span> result;
    }

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">StringBuilder</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">var</span> builder = <span class="hljs-keyword">new</span> StringBuilder();
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; NumberOfConcatenations; i++)
        {
            builder.Append(i);
        }
        <span class="hljs-keyword">return</span> builder.ToString();
    }

    [<span class="hljs-meta">Benchmark</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> <span class="hljs-title">StringJoin</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-keyword">var</span> list = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">string</span>&gt;();
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; NumberOfConcatenations; i++)
        {
            list.Add(i.ToString());
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">string</span>.Join(<span class="hljs-string">""</span>, list);
    }
}
</code></pre>
<p>There, we have 4 algorithms to concatenate strings. Note that there is a <code>[Benchmark]</code> annotation in each of them. According to <a target="_blank" href="https://benchmarkdotnet.org/articles/guides/good-practices.html">their good practices</a>, it is recommended to <strong>use the result of the calculation</strong> to avoid dead code elimination by JIT. That is why I am using a <code>string</code> result type.</p>
<p>Finally, in our <code>Program.cs</code> file, we will add the following lines to run the benchmarking:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> BenchmarkDotNet.Running;
<span class="hljs-keyword">using</span> Benchmarking;

<span class="hljs-keyword">var</span> summary = BenchmarkRunner.Run&lt;StringConcatenationBenchmark&gt;();
</code></pre>
<p>There are <a target="_blank" href="https://benchmarkdotnet.org/articles/guides/how-to-run.html">more ways</a> to run it, but the most important to make this work correctly is to build the application in Release mode and not attach the debugger during the benchmarking.</p>
<h3 id="heading-running-the-benchmarking">Running the benchmarking</h3>
<p>Let's start the project and see what we get in the Console Window.</p>
<pre><code class="lang-markdown">.NET SDK 8.0.100
  [Host]     : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.0 (8.0.23.53103), X64 RyuJIT AVX2


| Method             | Mean       | Error    | StdDev   |
|------------------- |-----------:|---------:|---------:|
| AdditionAssignment | 1,436.3 ns | 28.58 ns | 34.02 ns |
| StringConcat       | 1,678.7 ns | 32.37 ns | 44.31 ns |
| StringBuilder      |   389.3 ns |  4.66 ns |  4.36 ns |
| StringJoin         |   882.1 ns | 17.22 ns | 17.68 ns |
</code></pre>
<p>Alright! Sort of what we expected. The <code>StringBuilder</code> class was the fastest according to the statistics.</p>
<p>Now, it would be helpful to know how they perform in terms of memory allocation. This feature is not enabled by default, but we can do that by adding the <code>[MemoryDiagnoser]</code> annotation to the class.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">MemoryDiagnoser</span>]
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">StringConcatenationBenchmark</span>
{
  ...
</code></pre>
<p>Let's run this again.</p>
<pre><code class="lang-markdown">| Method             | Mean       | Error    | StdDev   | Gen0   | Allocated |
|------------------- |-----------:|---------:|---------:|-------:|----------:|
| AdditionAssignment | 1,429.6 ns | 25.21 ns | 30.96 ns | 1.6613 |  20.37 KB |
| StringConcat       | 1,668.8 ns | 33.09 ns | 50.54 ns | 1.8520 |  22.71 KB |
| StringBuilder      |   383.6 ns |  3.81 ns |  3.56 ns | 0.1016 |   1.25 KB |
| StringJoin         |   889.4 ns | 14.97 ns | 16.64 ns | 0.2069 |   2.54 KB |
</code></pre>
<p>Great! <code>StringBuilder</code> also wins in GC and memory allocation.</p>
<h3 id="heading-final-thoughts">Final thoughts</h3>
<p><a target="_blank" href="https://benchmarkdotnet.org/articles/overview.html">BenchmarkDotNet</a> proves to be an invaluable tool to diagnose performance issues in our C# code from different metrics and points of view like execution time and memory allocation. The examples provided are just the tip of the iceberg; BenchmarkDotNet offers numerous features to delve deeper into performance evaluations. Take the time to explore their documentation and unlock the full potential of this powerful performance-testing tool for your C# projects.</p>
]]></content:encoded></item></channel></rss>