<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pl_PL"><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://systizen.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://systizen.com/" rel="alternate" type="text/html" hreflang="pl_PL" /><updated>2026-06-15T14:20:44+02:00</updated><id>https://systizen.com/feed.xml</id><title type="html">SystiZen</title><subtitle>Tworzymy technologie z ludzką twarzą dla małych i średnich firm. Szkolenia, konsulting i rozwój kompetencji w obszarze Java, Python i bezpieczeństwa.</subtitle><entry xml:lang="pl"><title type="html">Clean Architecture w .NET - praktyczne podejście</title><link href="https://systizen.com/clean-architecture-dotnet/" rel="alternate" type="text/html" title="Clean Architecture w .NET - praktyczne podejście" /><published>2024-12-10T00:00:00+01:00</published><updated>2024-12-10T00:00:00+01:00</updated><id>https://systizen.com/clean-architecture-dotnet</id><content type="html" xml:base="https://systizen.com/clean-architecture-dotnet/"><![CDATA[<h2 id="wprowadzenie">Wprowadzenie</h2>

<p>Clean Architecture to podejście architektoniczne, które zyskuje coraz większą popularność w świecie .NET. W tym artykule pokażę, jak praktycznie wdrożyć te zasady w projekcie bankowym.</p>

<h2 id="kluczowe-zasady">Kluczowe zasady</h2>

<p>Clean Architecture opiera się na kilku fundamentalnych zasadach:</p>

<ol>
  <li><strong>Niezależność od frameworków</strong> - logika biznesowa nie powinna zależeć od konkretnego frameworka</li>
  <li><strong>Testowalność</strong> - reguły biznesowe można testować bez UI, bazy danych czy serwera</li>
  <li><strong>Niezależność od UI</strong> - można łatwo zmienić interfejs użytkownika</li>
  <li><strong>Niezależność od bazy danych</strong> - logika biznesowa nie jest związana z konkretną bazą</li>
</ol>

<h2 id="implementacja-z-mediatr">Implementacja z MediatR</h2>

<p>MediatR świetnie wspiera implementację Clean Architecture poprzez wzorzec mediator:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">CreateAccountCommand</span> <span class="p">:</span> <span class="n">IRequest</span><span class="p">&lt;</span><span class="n">AccountDto</span><span class="p">&gt;</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="kt">string</span> <span class="n">AccountNumber</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
    <span class="k">public</span> <span class="kt">decimal</span> <span class="n">InitialBalance</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>

<span class="k">public</span> <span class="k">class</span> <span class="nc">CreateAccountHandler</span> <span class="p">:</span> <span class="n">IRequestHandler</span><span class="p">&lt;</span><span class="n">CreateAccountCommand</span><span class="p">,</span> <span class="n">AccountDto</span><span class="p">&gt;</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">IAccountRepository</span> <span class="n">_repository</span><span class="p">;</span>
    
    <span class="k">public</span> <span class="nf">CreateAccountHandler</span><span class="p">(</span><span class="n">IAccountRepository</span> <span class="n">repository</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_repository</span> <span class="p">=</span> <span class="n">repository</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">AccountDto</span><span class="p">&gt;</span> <span class="nf">Handle</span><span class="p">(</span><span class="n">CreateAccountCommand</span> <span class="n">request</span><span class="p">,</span> 
        <span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="c1">// Logika biznesowa</span>
        <span class="kt">var</span> <span class="n">account</span> <span class="p">=</span> <span class="n">Account</span><span class="p">.</span><span class="nf">Create</span><span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="n">AccountNumber</span><span class="p">,</span> <span class="n">request</span><span class="p">.</span><span class="n">InitialBalance</span><span class="p">);</span>
        <span class="k">await</span> <span class="n">_repository</span><span class="p">.</span><span class="nf">AddAsync</span><span class="p">(</span><span class="n">account</span><span class="p">);</span>
        
        <span class="k">return</span> <span class="n">AccountDto</span><span class="p">.</span><span class="nf">FromEntity</span><span class="p">(</span><span class="n">account</span><span class="p">);</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="pipeline-behaviors">Pipeline Behaviors</h2>

<p>Jedną z największych zalet MediatR są pipeline behaviors, które pozwalają na eleganckie dodawanie cross-cutting concerns:</p>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">ValidationBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;</span> 
    <span class="p">:</span> <span class="n">IPipelineBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">IValidator</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">&gt;&gt;</span> <span class="n">_validators</span><span class="p">;</span>
    
    <span class="k">public</span> <span class="nf">ValidationBehavior</span><span class="p">(</span><span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">IValidator</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">&gt;&gt;</span> <span class="n">validators</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_validators</span> <span class="p">=</span> <span class="n">validators</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span> <span class="nf">Handle</span><span class="p">(</span><span class="n">TRequest</span> <span class="n">request</span><span class="p">,</span> 
        <span class="n">RequestHandlerDelegate</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span> <span class="n">next</span><span class="p">,</span> 
        <span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ValidationContext</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">&gt;(</span><span class="n">request</span><span class="p">);</span>
        <span class="kt">var</span> <span class="n">failures</span> <span class="p">=</span> <span class="n">_validators</span>
            <span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">v</span> <span class="p">=&gt;</span> <span class="n">v</span><span class="p">.</span><span class="nf">Validate</span><span class="p">(</span><span class="n">context</span><span class="p">))</span>
            <span class="p">.</span><span class="nf">SelectMany</span><span class="p">(</span><span class="n">result</span> <span class="p">=&gt;</span> <span class="n">result</span><span class="p">.</span><span class="n">Errors</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">f</span> <span class="p">=&gt;</span> <span class="n">f</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">ToList</span><span class="p">();</span>
            
        <span class="k">if</span> <span class="p">(</span><span class="n">failures</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
        <span class="p">{</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">ValidationException</span><span class="p">(</span><span class="n">failures</span><span class="p">);</span>
        <span class="p">}</span>
        
        <span class="k">return</span> <span class="k">await</span> <span class="nf">next</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="korzyści-w-środowisku-bankowym">Korzyści w środowisku bankowym</h2>

<p>W regulowanym środowisku bankowym Clean Architecture przynosi szczególne korzyści:</p>

<ul>
  <li><strong>Auditability</strong> - jasny podział odpowiedzialności ułatwia audyty</li>
  <li><strong>Testability</strong> - łatwiejsze spełnienie wymogów testowych</li>
  <li><strong>Maintainability</strong> - niższe koszty utrzymania w długim terminie</li>
  <li><strong>Security</strong> - łatwiejsze wdrażanie security controls</li>
</ul>

<h2 id="podsumowanie">Podsumowanie</h2>

<p>Clean Architecture z MediatR to potężna kombinacja dla projektów .NET, szczególnie w środowiskach regulowanych. Wymaga pewnej początkowej inwestycji w strukturę projektu, ale zwraca się szybko poprzez łatwiejsze utrzymanie i rozbudowę systemu.</p>

<p>W kolejnych artykułach pokażę bardziej zaawansowane wzorce i techniki implementacji.</p>]]></content><author><name>Bartłomiej Antosik</name></author><category term=".NET" /><category term="Clean Architecture" /><category term="MediatR" /><summary type="html"><![CDATA[Praktyczne wprowadzenie do implementacji Clean Architecture w projektach .NET z wykorzystaniem MediatR i dependency injection.]]></summary></entry><entry xml:lang="pl"><title type="html">MediatR Pipeline Behaviors – eleganckie rozwiązanie zagadnień przekrojowych</title><link href="https://systizen.com/mediatr-pipeline-behaviors/" rel="alternate" type="text/html" title="MediatR Pipeline Behaviors – eleganckie rozwiązanie zagadnień przekrojowych" /><published>2024-12-08T00:00:00+01:00</published><updated>2024-12-08T00:00:00+01:00</updated><id>https://systizen.com/mediatr-pipeline-behaviors</id><content type="html" xml:base="https://systizen.com/mediatr-pipeline-behaviors/"><![CDATA[<h2 id="czym-są-pipeline-behaviors">Czym są Pipeline Behaviors?</h2>

<p>Pipeline Behaviors w MediatR to mechanizm pozwalający na przechwytywanie i modyfikowanie przetwarzania żądań (requestów) przed wykonaniem właściwego handlera oraz po jego zakończeniu. Koncepcyjnie przypominają middleware w ASP.NET Core, z tą różnicą, że działają na poziomie logiki biznesowej, a nie warstwy HTTP.</p>

<p>Każdy behavior implementuje interfejs <code class="language-plaintext highlighter-rouge">IPipelineBehavior&lt;TRequest, TResponse&gt;</code> i otrzymuje:</p>
<ul>
  <li>Żądanie (<code class="language-plaintext highlighter-rouge">TRequest</code>)</li>
  <li>Delegat <code class="language-plaintext highlighter-rouge">next</code> do wywołania kolejnego elementu w łańcuchu</li>
  <li>Token anulowania</li>
</ul>

<p>Dzięki temu behaviors tworzą łańcuch odpowiedzialności (Chain of Responsibility pattern), przez który przechodzi każde żądanie.</p>

<h2 id="dlaczego-są-niezbędne">Dlaczego są niezbędne?</h2>

<p>W aplikacjach biznesowych nieuchronnie pojawiają się zagadnienia przekrojowe (cross-cutting concerns) – aspekty funkcjonalności, które przewijają się przez wiele komponentów systemu:</p>

<ul>
  <li><strong>Walidacja</strong> – sprawdzanie poprawności danych wejściowych</li>
  <li><strong>Logowanie</strong> – rejestrowanie operacji w celach audytowych</li>
  <li><strong>Zarządzanie transakcjami</strong> – zapewnienie spójności danych</li>
  <li><strong>Buforowanie</strong> – optymalizacja wydajności dla często używanych zapytań</li>
  <li><strong>Obsługa błędów</strong> – ustandaryzowane przetwarzanie wyjątków</li>
  <li><strong>Monitorowanie wydajności</strong> – pomiar czasów wykonania operacji</li>
  <li><strong>Autoryzacja</strong> – kontrola dostępu do zasobów</li>
</ul>

<p>Tradycyjne podejścia do rozwiązania tych problemów prowadzą do:</p>
<ul>
  <li><strong>Duplikacji kodu</strong> – każdy handler powtarza tę samą logikę walidacji, logowania, itp.</li>
  <li><strong>Trudności w utrzymaniu</strong> – zmiana wymaga modyfikacji dziesiątek klas</li>
  <li><strong>Niejasnych zależności</strong> – kod biznesowy miesza się z infrastrukturą</li>
  <li><strong>Testowania</strong> – konieczność mockowania infrastruktury w każdym teście</li>
</ul>

<p>Pipeline behaviors rozwiązują te problemy poprzez wydzielenie zagadnień przekrojowych do osobnych, reużywalnych komponentów.</p>

<h2 id="szczegółowy-przykład-behavior-walidacyjny">Szczegółowy przykład: Behavior walidacyjny</h2>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">ValidationBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;</span> 
    <span class="p">:</span> <span class="n">IPipelineBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;</span>
    <span class="k">where</span> <span class="n">TRequest</span> <span class="p">:</span> <span class="n">IRequest</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">IValidator</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">&gt;&gt;</span> <span class="n">_validators</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">ValidationBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;&gt;</span> <span class="n">_logger</span><span class="p">;</span>
    
    <span class="k">public</span> <span class="nf">ValidationBehavior</span><span class="p">(</span>
        <span class="n">IEnumerable</span><span class="p">&lt;</span><span class="n">IValidator</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">&gt;&gt;</span> <span class="n">validators</span><span class="p">,</span>
        <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">ValidationBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;&gt;</span> <span class="n">logger</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_validators</span> <span class="p">=</span> <span class="n">validators</span><span class="p">;</span>
        <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span> <span class="nf">Handle</span><span class="p">(</span>
        <span class="n">TRequest</span> <span class="n">request</span><span class="p">,</span> 
        <span class="n">RequestHandlerDelegate</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span> <span class="n">next</span><span class="p">,</span> 
        <span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">requestName</span> <span class="p">=</span> <span class="k">typeof</span><span class="p">(</span><span class="n">TRequest</span><span class="p">).</span><span class="n">Name</span><span class="p">;</span>
        
        <span class="c1">// Jeśli brak walidatorów, pomijamy walidację</span>
        <span class="k">if</span> <span class="p">(!</span><span class="n">_validators</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogDebug</span><span class="p">(</span>
                <span class="s">"Brak walidatorów dla {RequestName}, pomijam walidację"</span><span class="p">,</span> 
                <span class="n">requestName</span><span class="p">);</span>
            <span class="k">return</span> <span class="k">await</span> <span class="nf">next</span><span class="p">();</span>
        <span class="p">}</span>
        
        <span class="n">_logger</span><span class="p">.</span><span class="nf">LogDebug</span><span class="p">(</span>
            <span class="s">"Walidacja {RequestName} przy użyciu {ValidatorCount} walidatorów"</span><span class="p">,</span>
            <span class="n">requestName</span><span class="p">,</span>
            <span class="n">_validators</span><span class="p">.</span><span class="nf">Count</span><span class="p">());</span>
        
        <span class="c1">// Tworzymy kontekst walidacji</span>
        <span class="kt">var</span> <span class="n">context</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ValidationContext</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">&gt;(</span><span class="n">request</span><span class="p">);</span>
        
        <span class="c1">// Wykonujemy wszystkie walidatory równolegle</span>
        <span class="kt">var</span> <span class="n">validationResults</span> <span class="p">=</span> <span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="nf">WhenAll</span><span class="p">(</span>
            <span class="n">_validators</span><span class="p">.</span><span class="nf">Select</span><span class="p">(</span><span class="n">v</span> <span class="p">=&gt;</span> <span class="n">v</span><span class="p">.</span><span class="nf">ValidateAsync</span><span class="p">(</span><span class="n">context</span><span class="p">,</span> <span class="n">cancellationToken</span><span class="p">))</span>
        <span class="p">);</span>
        
        <span class="c1">// Zbieramy wszystkie błędy walidacji</span>
        <span class="kt">var</span> <span class="n">failures</span> <span class="p">=</span> <span class="n">validationResults</span>
            <span class="p">.</span><span class="nf">SelectMany</span><span class="p">(</span><span class="n">result</span> <span class="p">=&gt;</span> <span class="n">result</span><span class="p">.</span><span class="n">Errors</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">failure</span> <span class="p">=&gt;</span> <span class="n">failure</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
            <span class="p">.</span><span class="nf">ToList</span><span class="p">();</span>
        
        <span class="k">if</span> <span class="p">(</span><span class="n">failures</span><span class="p">.</span><span class="nf">Any</span><span class="p">())</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span>
                <span class="s">"Walidacja {RequestName} zakończona niepowodzeniem. Liczba błędów: {ErrorCount}"</span><span class="p">,</span>
                <span class="n">requestName</span><span class="p">,</span>
                <span class="n">failures</span><span class="p">.</span><span class="n">Count</span><span class="p">);</span>
                
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">ValidationException</span><span class="p">(</span><span class="n">failures</span><span class="p">);</span>
        <span class="p">}</span>
        
        <span class="n">_logger</span><span class="p">.</span><span class="nf">LogDebug</span><span class="p">(</span><span class="s">"Walidacja {RequestName} zakończona sukcesem"</span><span class="p">,</span> <span class="n">requestName</span><span class="p">);</span>
        
        <span class="c1">// Przechodzimy dalej w łańcuchu</span>
        <span class="k">return</span> <span class="k">await</span> <span class="nf">next</span><span class="p">();</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Kluczowe aspekty implementacji:</strong></p>

<ol>
  <li><strong>Równoległe wykonanie walidatorów</strong> – <code class="language-plaintext highlighter-rouge">Task.WhenAll</code> pozwala na równoczesne uruchomienie wszystkich walidatorów, co poprawia wydajność</li>
  <li><strong>Logowanie diagnostyczne</strong> – rejestrujemy rozpoczęcie i zakończenie walidacji dla celów debugowania</li>
  <li><strong>Zbieranie wszystkich błędów</strong> – użytkownik otrzymuje kompletną listę problemów, nie tylko pierwszy napotkany błąd</li>
</ol>

<h2 id="rejestracja-w-kontenerze-di">Rejestracja w kontenerze DI</h2>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">services</span><span class="p">.</span><span class="nf">AddMediatR</span><span class="p">(</span><span class="n">cfg</span> <span class="p">=&gt;</span>
<span class="p">{</span>
    <span class="n">cfg</span><span class="p">.</span><span class="nf">RegisterServicesFromAssembly</span><span class="p">(</span><span class="n">Assembly</span><span class="p">.</span><span class="nf">GetExecutingAssembly</span><span class="p">());</span>
    
    <span class="c1">// Behaviors wykonują się w kolejności rejestracji</span>
    <span class="n">cfg</span><span class="p">.</span><span class="nf">AddBehavior</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">IPipelineBehavior</span><span class="p">&lt;,&gt;),</span> <span class="k">typeof</span><span class="p">(</span><span class="n">LoggingBehavior</span><span class="p">&lt;,&gt;));</span>
    <span class="n">cfg</span><span class="p">.</span><span class="nf">AddBehavior</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">IPipelineBehavior</span><span class="p">&lt;,&gt;),</span> <span class="k">typeof</span><span class="p">(</span><span class="n">ValidationBehavior</span><span class="p">&lt;,&gt;));</span>
    <span class="n">cfg</span><span class="p">.</span><span class="nf">AddBehavior</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">IPipelineBehavior</span><span class="p">&lt;,&gt;),</span> <span class="k">typeof</span><span class="p">(</span><span class="n">AuthorizationBehavior</span><span class="p">&lt;,&gt;));</span>
    <span class="n">cfg</span><span class="p">.</span><span class="nf">AddBehavior</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">IPipelineBehavior</span><span class="p">&lt;,&gt;),</span> <span class="k">typeof</span><span class="p">(</span><span class="n">TransactionBehavior</span><span class="p">&lt;,&gt;));</span>
    <span class="n">cfg</span><span class="p">.</span><span class="nf">AddBehavior</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">IPipelineBehavior</span><span class="p">&lt;,&gt;),</span> <span class="k">typeof</span><span class="p">(</span><span class="n">PerformanceBehavior</span><span class="p">&lt;,&gt;));</span>
<span class="p">});</span>

<span class="c1">// Rejestracja walidatorów FluentValidation</span>
<span class="n">services</span><span class="p">.</span><span class="nf">AddValidatorsFromAssembly</span><span class="p">(</span><span class="n">Assembly</span><span class="p">.</span><span class="nf">GetExecutingAssembly</span><span class="p">());</span>
</code></pre></div></div>

<p><strong>Uwagi dotyczące kolejności:</strong></p>
<ul>
  <li>Logowanie powinno być pierwsze, aby rejestrować wszystkie operacje</li>
  <li>Walidacja przed autoryzacją – nie ma sensu sprawdzać uprawnień do niepoprawnych danych</li>
  <li>Transakcja najbliżej handlera – otwieramy ją najpóźniej, zamykamy najwcześniej</li>
  <li>Performance monitoring może być na początku lub końcu, w zależności od tego, co chcemy mierzyć</li>
</ul>

<h2 id="kolejność-wykonania--szczegółowy-przebieg">Kolejność wykonania – szczegółowy przebieg</h2>

<p>Pipeline behaviors tworzą strukturę przypominającą “matriószkę” – każdy behavior otacza kolejny:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Żądanie (Request)
  │
  ├─→ LoggingBehavior START
  │     │
  │     ├─→ ValidationBehavior START
  │     │     │
  │     │     ├─→ AuthorizationBehavior START
  │     │     │     │
  │     │     │     ├─→ TransactionBehavior START
  │     │     │     │     │
  │     │     │     │     ├─→ HANDLER
  │     │     │     │     │
  │     │     │     │     └─← TransactionBehavior END (commit)
  │     │     │     │
  │     │     │     └─← AuthorizationBehavior END
  │     │     │
  │     │     └─← ValidationBehavior END
  │     │
  │     └─← LoggingBehavior END
  │
  └─← Odpowiedź (Response)
</code></pre></div></div>

<p><strong>Istotne konsekwencje:</strong></p>

<ul>
  <li>Błąd w wewnętrznym behaviorze “wypływa” przez wszystkie zewnętrzne, które mogą go przechwycić</li>
  <li>Timing zewnętrznego behaviora obejmuje czas wykonania wszystkich wewnętrznych</li>
  <li>Transakcja powinna być jak najbliżej handlera, aby minimalizować czas jej trwania</li>
</ul>

<h2 id="behavior-zarządzający-transakcjami">Behavior zarządzający transakcjami</h2>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">TransactionBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;</span> 
    <span class="p">:</span> <span class="n">IPipelineBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;</span>
    <span class="k">where</span> <span class="n">TRequest</span> <span class="p">:</span> <span class="n">IRequest</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">IDbContext</span> <span class="n">_context</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">TransactionBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;&gt;</span> <span class="n">_logger</span><span class="p">;</span>
    
    <span class="k">public</span> <span class="nf">TransactionBehavior</span><span class="p">(</span>
        <span class="n">IDbContext</span> <span class="n">context</span><span class="p">,</span>
        <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">TransactionBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;&gt;</span> <span class="n">logger</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_context</span> <span class="p">=</span> <span class="n">context</span><span class="p">;</span>
        <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span> <span class="nf">Handle</span><span class="p">(</span>
        <span class="n">TRequest</span> <span class="n">request</span><span class="p">,</span> 
        <span class="n">RequestHandlerDelegate</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span> <span class="n">next</span><span class="p">,</span> 
        <span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">requestName</span> <span class="p">=</span> <span class="k">typeof</span><span class="p">(</span><span class="n">TRequest</span><span class="p">).</span><span class="n">Name</span><span class="p">;</span>
        
        <span class="c1">// Tylko komendy wymagają transakcji</span>
        <span class="c1">// Zapytania (queries) są read-only i nie modyfikują stanu</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">request</span> <span class="k">is</span> <span class="n">IQuery</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;)</span>
        <span class="p">{</span>
            <span class="k">return</span> <span class="k">await</span> <span class="nf">next</span><span class="p">();</span>
        <span class="p">}</span>
        
        <span class="n">_logger</span><span class="p">.</span><span class="nf">LogDebug</span><span class="p">(</span><span class="s">"Rozpoczynam transakcję dla {RequestName}"</span><span class="p">,</span> <span class="n">requestName</span><span class="p">);</span>
        
        <span class="c1">// Rozpoczynamy transakcję z odpowiednim poziomem izolacji</span>
        <span class="k">await</span> <span class="k">using</span> <span class="nn">var</span> <span class="n">transaction</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_context</span><span class="p">.</span><span class="nf">BeginTransactionAsync</span><span class="p">(</span>
            <span class="n">IsolationLevel</span><span class="p">.</span><span class="n">ReadCommitted</span><span class="p">,</span>
            <span class="n">cancellationToken</span><span class="p">);</span>
        
        <span class="k">try</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="k">await</span> <span class="nf">next</span><span class="p">();</span>
            
            <span class="k">await</span> <span class="n">transaction</span><span class="p">.</span><span class="nf">CommitAsync</span><span class="p">(</span><span class="n">cancellationToken</span><span class="p">);</span>
            
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogDebug</span><span class="p">(</span>
                <span class="s">"Transakcja dla {RequestName} zakończona sukcesem"</span><span class="p">,</span>
                <span class="n">requestName</span><span class="p">);</span>
                
            <span class="k">return</span> <span class="n">response</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span> <span class="n">ex</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">_logger</span><span class="p">.</span><span class="nf">LogError</span><span class="p">(</span>
                <span class="n">ex</span><span class="p">,</span>
                <span class="s">"Transakcja dla {RequestName} wycofana z powodu błędu"</span><span class="p">,</span>
                <span class="n">requestName</span><span class="p">);</span>
                
            <span class="k">await</span> <span class="n">transaction</span><span class="p">.</span><span class="nf">RollbackAsync</span><span class="p">(</span><span class="n">cancellationToken</span><span class="p">);</span>
            <span class="k">throw</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>Kluczowe decyzje projektowe:</strong></p>

<ol>
  <li><strong>Różnicowanie komend i zapytań</strong> – tylko komendy modyfikujące stan wymagają transakcji</li>
  <li><strong>Poziom izolacji</strong> – <code class="language-plaintext highlighter-rouge">ReadCommitted</code> to rozsądny domyślny wybór w większości scenariuszy</li>
  <li><strong>Jawne wycofywanie</strong> – choć nie jest technicznie konieczne (transakcja zostanie wycofana przy Dispose), jawny rollback poprawia czytelność logów</li>
</ol>

<h2 id="warunkowe-wykonanie--zaawansowane-scenariusze">Warunkowe wykonanie – zaawansowane scenariusze</h2>

<h3 id="rozróżnienie-komend-i-zapytań">Rozróżnienie komend i zapytań</h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">interface</span> <span class="nc">ICommand</span> <span class="p">:</span> <span class="n">IRequest</span><span class="p">&lt;</span><span class="n">Result</span><span class="p">&gt;</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">public</span> <span class="k">interface</span> <span class="nc">ICommand</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span> <span class="p">:</span> <span class="n">IRequest</span><span class="p">&lt;</span><span class="n">Result</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;&gt;</span> <span class="p">{</span> <span class="p">}</span>
<span class="k">public</span> <span class="k">interface</span> <span class="nc">IQuery</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span> <span class="p">:</span> <span class="n">IRequest</span><span class="p">&lt;</span><span class="n">Result</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;&gt;</span> <span class="p">{</span> <span class="p">}</span>

<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span> <span class="nf">Handle</span><span class="p">(...)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">request</span> <span class="k">is</span> <span class="n">ICommand</span> <span class="p">||</span> <span class="n">request</span> <span class="k">is</span> <span class="n">ICommand</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;)</span>
    <span class="p">{</span>
        <span class="c1">// Logika specyficzna dla komend</span>
        <span class="c1">// np. walidacja biznesowa, audyt, transakcje</span>
    <span class="p">}</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="n">request</span> <span class="k">is</span> <span class="n">IQuery</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;)</span>
    <span class="p">{</span>
        <span class="c1">// Logika specyficzna dla zapytań</span>
        <span class="c1">// np. cache, read-only connection</span>
    <span class="p">}</span>
    
    <span class="k">return</span> <span class="k">await</span> <span class="nf">next</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="atrybuty-kontrolujące-behaviors">Atrybuty kontrolujące behaviors</h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="nf">AttributeUsage</span><span class="p">(</span><span class="n">AttributeTargets</span><span class="p">.</span><span class="n">Class</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">SkipTransactionAttribute</span> <span class="p">:</span> <span class="n">Attribute</span> <span class="p">{</span> <span class="p">}</span>

<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span> <span class="nf">Handle</span><span class="p">(...)</span>
<span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="nf">GetType</span><span class="p">().</span><span class="n">GetCustomAttribute</span><span class="p">&lt;</span><span class="n">SkipTransactionAttribute</span><span class="p">&gt;()</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="k">return</span> <span class="k">await</span> <span class="nf">next</span><span class="p">();</span>
    <span class="p">}</span>
    
    <span class="c1">// Normalna logika transakcji</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="behavior-monitorujący-wydajność">Behavior monitorujący wydajność</h3>

<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">PerformanceBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;</span> 
    <span class="p">:</span> <span class="n">IPipelineBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;</span>
    <span class="k">where</span> <span class="n">TRequest</span> <span class="p">:</span> <span class="n">IRequest</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span>
<span class="p">{</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">PerformanceBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;&gt;</span> <span class="n">_logger</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">readonly</span> <span class="n">IMetrics</span> <span class="n">_metrics</span><span class="p">;</span>
    <span class="k">private</span> <span class="k">const</span> <span class="kt">int</span> <span class="n">PerformanceThresholdMs</span> <span class="p">=</span> <span class="m">500</span><span class="p">;</span>
    
    <span class="k">public</span> <span class="nf">PerformanceBehavior</span><span class="p">(</span>
        <span class="n">ILogger</span><span class="p">&lt;</span><span class="n">PerformanceBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;&gt;</span> <span class="n">logger</span><span class="p">,</span>
        <span class="n">IMetrics</span> <span class="n">metrics</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="n">_logger</span> <span class="p">=</span> <span class="n">logger</span><span class="p">;</span>
        <span class="n">_metrics</span> <span class="p">=</span> <span class="n">metrics</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="k">public</span> <span class="k">async</span> <span class="n">Task</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span> <span class="nf">Handle</span><span class="p">(</span>
        <span class="n">TRequest</span> <span class="n">request</span><span class="p">,</span>
        <span class="n">RequestHandlerDelegate</span><span class="p">&lt;</span><span class="n">TResponse</span><span class="p">&gt;</span> <span class="n">next</span><span class="p">,</span>
        <span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="kt">var</span> <span class="n">requestName</span> <span class="p">=</span> <span class="k">typeof</span><span class="p">(</span><span class="n">TRequest</span><span class="p">).</span><span class="n">Name</span><span class="p">;</span>
        <span class="kt">var</span> <span class="n">stopwatch</span> <span class="p">=</span> <span class="n">Stopwatch</span><span class="p">.</span><span class="nf">StartNew</span><span class="p">();</span>
        
        <span class="k">try</span>
        <span class="p">{</span>
            <span class="kt">var</span> <span class="n">response</span> <span class="p">=</span> <span class="k">await</span> <span class="nf">next</span><span class="p">();</span>
            <span class="n">stopwatch</span><span class="p">.</span><span class="nf">Stop</span><span class="p">();</span>
            
            <span class="kt">var</span> <span class="n">elapsedMs</span> <span class="p">=</span> <span class="n">stopwatch</span><span class="p">.</span><span class="n">ElapsedMilliseconds</span><span class="p">;</span>
            
            <span class="c1">// Metryki dla systemu monitoringu</span>
            <span class="n">_metrics</span><span class="p">.</span><span class="nf">RecordRequestDuration</span><span class="p">(</span><span class="n">requestName</span><span class="p">,</span> <span class="n">elapsedMs</span><span class="p">);</span>
            
            <span class="k">if</span> <span class="p">(</span><span class="n">elapsedMs</span> <span class="p">&gt;</span> <span class="n">PerformanceThresholdMs</span><span class="p">)</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogWarning</span><span class="p">(</span>
                    <span class="s">"Wydajność: {RequestName} wykonał się w {ElapsedMs}ms (próg: {ThresholdMs}ms). Żądanie: {@Request}"</span><span class="p">,</span>
                    <span class="n">requestName</span><span class="p">,</span>
                    <span class="n">elapsedMs</span><span class="p">,</span>
                    <span class="n">PerformanceThresholdMs</span><span class="p">,</span>
                    <span class="n">request</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">else</span>
            <span class="p">{</span>
                <span class="n">_logger</span><span class="p">.</span><span class="nf">LogDebug</span><span class="p">(</span>
                    <span class="s">"Wydajność: {RequestName} wykonał się w {ElapsedMs}ms"</span><span class="p">,</span>
                    <span class="n">requestName</span><span class="p">,</span>
                    <span class="n">elapsedMs</span><span class="p">);</span>
            <span class="p">}</span>
            
            <span class="k">return</span> <span class="n">response</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">catch</span> <span class="p">(</span><span class="n">Exception</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="n">stopwatch</span><span class="p">.</span><span class="nf">Stop</span><span class="p">();</span>
            <span class="n">_metrics</span><span class="p">.</span><span class="nf">RecordRequestDuration</span><span class="p">(</span><span class="n">requestName</span><span class="p">,</span> <span class="n">stopwatch</span><span class="p">.</span><span class="n">ElapsedMilliseconds</span><span class="p">);</span>
            <span class="k">throw</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="korzyści-w-projektach-bankowych">Korzyści w projektach bankowych</h2>

<p>W systemach bankowych pipeline behaviors przynoszą wymierne korzyści:</p>

<h3 id="1-audytowalność-i-zgodność-z-regulacjami">1. Audytowalność i zgodność z regulacjami</h3>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">AuditBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;</span> 
    <span class="p">:</span> <span class="n">IPipelineBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;</span>
<span class="p">{</span>
    <span class="c1">// Centralne logowanie wszystkich operacji zgodnie z wymogami PSD2/RODO</span>
    <span class="c1">// - Kto wykonał operację (użytkownik, system)</span>
    <span class="c1">// - Kiedy (timestamp z timezone)</span>
    <span class="c1">// - Co zostało zmienione (przed/po)</span>
    <span class="c1">// - Kontekst biznesowy (numer konta, kwota)</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="2-bezpieczeństwo-wielowarstwowe">2. Bezpieczeństwo wielowarstwowe</h3>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">AuthorizationBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;</span>
    <span class="p">:</span> <span class="n">IPipelineBehavior</span><span class="p">&lt;</span><span class="n">TRequest</span><span class="p">,</span> <span class="n">TResponse</span><span class="p">&gt;</span>
<span class="p">{</span>
    <span class="c1">// - Weryfikacja tożsamości (authentication)</span>
    <span class="c1">// - Kontrola uprawnień (authorization)</span>
    <span class="c1">// - Sprawdzenie limitów operacyjnych</span>
    <span class="c1">// - Wykrywanie podejrzanych wzorców</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="3-spójność-operacyjna">3. Spójność operacyjna</h3>
<ul>
  <li>Każda operacja przechodzi przez te same kontrole</li>
  <li>Nie ma możliwości “pominięcia” walidacji czy audytu</li>
  <li>Jednolite formaty błędów i logów</li>
  <li>Przewidywalne zachowanie systemu</li>
</ul>

<h3 id="4-testowalność">4. Testowalność</h3>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="n">Fact</span><span class="p">]</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">ValidationBehavior_Should_Reject_Invalid_Transfer</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// Testowanie behaviora w izolacji, bez konieczności</span>
    <span class="c1">// uruchamiania całego handlera czy bazy danych</span>
    
    <span class="kt">var</span> <span class="n">validators</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="k">new</span> <span class="nf">TransferCommandValidator</span><span class="p">()</span> <span class="p">};</span>
    <span class="kt">var</span> <span class="n">behavior</span> <span class="p">=</span> <span class="k">new</span> <span class="n">ValidationBehavior</span><span class="p">&lt;</span><span class="n">TransferCommand</span><span class="p">,</span> <span class="n">Result</span><span class="p">&gt;(</span><span class="n">validators</span><span class="p">);</span>
    
    <span class="kt">var</span> <span class="n">invalidCommand</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TransferCommand</span> 
    <span class="p">{</span> 
        <span class="n">Amount</span> <span class="p">=</span> <span class="p">-</span><span class="m">100</span> <span class="c1">// Niepoprawna kwota</span>
    <span class="p">};</span>
    
    <span class="k">await</span> <span class="n">Assert</span><span class="p">.</span><span class="n">ThrowsAsync</span><span class="p">&lt;</span><span class="n">ValidationException</span><span class="p">&gt;(</span>
        <span class="p">()</span> <span class="p">=&gt;</span> <span class="n">behavior</span><span class="p">.</span><span class="nf">Handle</span><span class="p">(</span><span class="n">invalidCommand</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="n">Task</span><span class="p">.</span><span class="nf">FromResult</span><span class="p">(</span><span class="n">Result</span><span class="p">.</span><span class="nf">Success</span><span class="p">()),</span> <span class="k">default</span><span class="p">)</span>
    <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="5-łatwość-utrzymania">5. Łatwość utrzymania</h3>
<ul>
  <li>Zmiana logiki walidacji w jednym miejscu wpływa na wszystkie operacje</li>
  <li>Dodanie nowego wymogu regulacyjnego to dodanie jednego behaviora</li>
  <li>Refactoring nie wymaga modyfikacji dziesiątek handlerów</li>
  <li>Jasny punkt integracji dla zewnętrznych systemów (np. fraud detection)</li>
</ul>

<h3 id="6-separacja-zagadnień">6. Separacja zagadnień</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌─────────────────────────────────────┐
│   Warstwa prezentacji (API)         │  ← Deserializacja, routing
├─────────────────────────────────────┤
│   Pipeline Behaviors                │  ← Cross-cutting concerns
├─────────────────────────────────────┤
│   Handlery (logika biznesowa)       │  ← Czysta logika domeny
├─────────────────────────────────────┤
│   Warstwa dostępu do danych         │  ← Persystencja
└─────────────────────────────────────┘
</code></pre></div></div>

<p>Każda warstwa ma jasno określoną odpowiedzialność. Handlery zawierają tylko logikę biznesową, bez “szumu” infrastrukturalnego.</p>

<h2 id="pułapki-i-dobre-praktyki">Pułapki i dobre praktyki</h2>

<h3 id="kolejność-ma-znaczenie">Kolejność ma znaczenie</h3>
<p>❌ <strong>Źle:</strong> Transakcja przed walidacją</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cfg</span><span class="p">.</span><span class="n">AddBehavior</span><span class="p">&lt;</span><span class="n">TransactionBehavior</span><span class="p">&lt;,&gt;&gt;();</span> <span class="c1">// Otwiera transakcję</span>
<span class="n">cfg</span><span class="p">.</span><span class="n">AddBehavior</span><span class="p">&lt;</span><span class="n">ValidationBehavior</span><span class="p">&lt;,&gt;&gt;();</span>  <span class="c1">// Może odrzucić żądanie</span>
<span class="c1">// Transakcja była otwarta niepotrzebnie!</span>
</code></pre></div></div>

<p>✅ <strong>Dobrze:</strong> Walidacja przed transakcją</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cfg</span><span class="p">.</span><span class="n">AddBehavior</span><span class="p">&lt;</span><span class="n">ValidationBehavior</span><span class="p">&lt;,&gt;&gt;();</span>  <span class="c1">// Najpierw sprawdź</span>
<span class="n">cfg</span><span class="p">.</span><span class="n">AddBehavior</span><span class="p">&lt;</span><span class="n">TransactionBehavior</span><span class="p">&lt;,&gt;&gt;();</span> <span class="c1">// Potem otwieraj zasoby</span>
</code></pre></div></div>

<h3 id="nie-dubluj-logiki-z-handlerów">Nie dubluj logiki z handlerów</h3>
<p>❌ <strong>Źle:</strong> Behavior zawiera logikę biznesową</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// To NIE jest zagadnienie przekrojowe, to logika biznesowa!</span>
<span class="k">if</span> <span class="p">(</span><span class="n">request</span> <span class="k">is</span> <span class="n">TransferCommand</span> <span class="n">transfer</span> <span class="p">&amp;&amp;</span> <span class="n">transfer</span><span class="p">.</span><span class="n">Amount</span> <span class="p">&gt;</span> <span class="m">10000</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">await</span> <span class="n">_fraudDetection</span><span class="p">.</span><span class="nf">CheckHighValueTransfer</span><span class="p">(</span><span class="n">transfer</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>✅ <strong>Dobrze:</strong> Behavior wywołuje odpowiednią usługę</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Behavior tylko orkiestruje, logikę zostawia dla serwisów</span>
<span class="k">if</span> <span class="p">(</span><span class="n">request</span> <span class="k">is</span> <span class="n">IRequiresFraudCheck</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">await</span> <span class="n">_fraudDetection</span><span class="p">.</span><span class="nf">CheckRequest</span><span class="p">(</span><span class="n">request</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="uważaj-na-wydajność">Uważaj na wydajność</h3>
<p>Behaviors wykonują się dla <strong>każdego</strong> żądania. Unikaj:</p>
<ul>
  <li>Ciężkich operacji I/O w każdym behaviorze</li>
  <li>Nadmiernego logowania w środowiskach produkcyjnych</li>
  <li>Zbędnych alokacji pamięci</li>
</ul>

<h3 id="testuj-behaviors-osobno">Testuj behaviors osobno</h3>
<p>Każdy behavior to niezależny komponent z jasną odpowiedzialnością – powinien mieć własne testy jednostkowe.</p>

<h2 id="podsumowanie">Podsumowanie</h2>

<p>Pipeline Behaviors to fundamentalny element architektury opartej na MediatR. Umożliwiają:</p>

<ul>
  <li><strong>Separację zagadnień</strong> – logika biznesowa oddzielona od infrastruktury</li>
  <li><strong>Zasadę DRY</strong> – brak duplikacji kodu cross-cutting concerns</li>
  <li><strong>Testowalność</strong> – każdy aspekt można testować osobno</li>
  <li><strong>Łatwość utrzymania</strong> – zmiany w jednym miejscu</li>
  <li><strong>Spójność</strong> – te same zasady dla wszystkich operacji</li>
</ul>

<p>W aplikacjach enterprise, szczególnie w sektorze finansowym, gdzie wymagania dotyczące audytowalności, bezpieczeństwa i spójności są kluczowe, pipeline behaviors stanowią nie tyle opcję, co konieczność architektury.</p>

<p>W następnym artykule przedstawię zaawansowane wzorce łączące pipeline behaviors z result pattern oraz strategię obsługi błędów w systemach rozproszonych.</p>

<hr />

<h2 id="dodatkowe-zasoby">Dodatkowe zasoby</h2>

<ul>
  <li><a href="https://github.com/jbogard/MediatR">Dokumentacja MediatR</a></li>
  <li><a href="https://docs.fluentvalidation.net/">FluentValidation</a></li>
  <li><a href="https://github.com/jasontaylordev/CleanArchitecture">Clean Architecture w .NET</a></li>
</ul>]]></content><author><name>Bartłomiej Antosik</name></author><category term=".NET" /><category term="MediatR" /><category term="Architecture" /><summary type="html"><![CDATA[Jak wykorzystać mechanizm pipeline behaviors w MediatR do eleganckiego rozwiązania zagadnień przekrojowych w aplikacjach .NET.]]></summary></entry></feed>