<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Emanuele Longo on Medium]]></title>
        <description><![CDATA[Stories by Emanuele Longo on Medium]]></description>
        <link>https://medium.com/@emanuele.longo992?source=rss-6cbbf07417e5------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*aHBcAlhxaVpPyDXujHE9gg.jpeg</url>
            <title>Stories by Emanuele Longo on Medium</title>
            <link>https://medium.com/@emanuele.longo992?source=rss-6cbbf07417e5------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 11:49:25 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@emanuele.longo992/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Model Port in .NET C#: From vendor SDKs to a port for the language model (2026)]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@emanuele.longo992/model-port-in-net-c-from-vendor-sdks-to-a-port-for-the-language-model-2026-d1e9d96f4185?source=rss-6cbbf07417e5------2"><img src="https://cdn-images-1.medium.com/max/2600/1*FTV8eohoTWtu44E_sdJPEg.jpeg" width="2800"></a></p><p class="medium-feed-snippet">Imagine your e-commerce wants its first AI feature: a product description written from the supplier&#x2019;s notes. Nobody wants to call the HTTP&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/@emanuele.longo992/model-port-in-net-c-from-vendor-sdks-to-a-port-for-the-language-model-2026-d1e9d96f4185?source=rss-6cbbf07417e5------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@emanuele.longo992/model-port-in-net-c-from-vendor-sdks-to-a-port-for-the-language-model-2026-d1e9d96f4185?source=rss-6cbbf07417e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/d1e9d96f4185</guid>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[c-sharp-programming]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[dotnet]]></category>
            <dc:creator><![CDATA[Emanuele Longo]]></dc:creator>
            <pubDate>Sat, 14 Nov 2026 14:01:02 GMT</pubDate>
            <atom:updated>2026-11-14T14:01:02.387Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Memento Pattern in .NET C#: From private snapshots to immutable records and versioned JSON (2026)]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://levelup.gitconnected.com/memento-pattern-in-net-c-from-private-snapshots-to-immutable-records-and-versioned-json-2026-9fcdced70151?source=rss-6cbbf07417e5------2"><img src="https://cdn-images-1.medium.com/max/2600/1*5IV0tvO8v0nZSVFGYRDeSw.jpeg" width="2800"></a></p><p class="medium-feed-snippet">Imagine developing the back-office of an e-commerce, and in particular the editor of the product sheets: title, description, price, and&#x2026;</p><p class="medium-feed-link"><a href="https://levelup.gitconnected.com/memento-pattern-in-net-c-from-private-snapshots-to-immutable-records-and-versioned-json-2026-9fcdced70151?source=rss-6cbbf07417e5------2">Continue reading on Level Up Coding »</a></p></div>]]></description>
            <link>https://levelup.gitconnected.com/memento-pattern-in-net-c-from-private-snapshots-to-immutable-records-and-versioned-json-2026-9fcdced70151?source=rss-6cbbf07417e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/9fcdced70151</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[design-patterns]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[c-sharp-programming]]></category>
            <dc:creator><![CDATA[Emanuele Longo]]></dc:creator>
            <pubDate>Fri, 13 Nov 2026 17:39:59 GMT</pubDate>
            <atom:updated>2026-11-13T17:39:59.390Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[TimeProvider in .NET 8+: From DateTime.UtcNow to deterministic tests with FakeTimeProvider]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://levelup.gitconnected.com/timeprovider-in-net-8-from-datetime-utcnow-to-deterministic-tests-with-faketimeprovider-d5fb7662e706?source=rss-6cbbf07417e5------2"><img src="https://cdn-images-1.medium.com/max/2400/1*W0PDrII5BilpJ6959L-QzQ.jpeg" width="2400"></a></p><p class="medium-feed-snippet">Time is the one dependency every business application has and almost nobody injects. If you&#x2019;ve ever written C# code for a shop, you&#x2019;ve&#x2026;</p><p class="medium-feed-link"><a href="https://levelup.gitconnected.com/timeprovider-in-net-8-from-datetime-utcnow-to-deterministic-tests-with-faketimeprovider-d5fb7662e706?source=rss-6cbbf07417e5------2">Continue reading on Level Up Coding »</a></p></div>]]></description>
            <link>https://levelup.gitconnected.com/timeprovider-in-net-8-from-datetime-utcnow-to-deterministic-tests-with-faketimeprovider-d5fb7662e706?source=rss-6cbbf07417e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/d5fb7662e706</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[best-practices]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[c-sharp-programming]]></category>
            <category><![CDATA[dotnet]]></category>
            <dc:creator><![CDATA[Emanuele Longo]]></dc:creator>
            <pubDate>Fri, 13 Nov 2026 17:39:43 GMT</pubDate>
            <atom:updated>2026-11-13T17:39:43.112Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Observer Pattern in .NET C#: From Subject and Update to events and Reactive Extensions (2026)]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://levelup.gitconnected.com/observer-pattern-in-net-c-from-subject-and-update-to-events-and-reactive-extensions-2026-8c7c5f71a5b8?source=rss-6cbbf07417e5------2"><img src="https://cdn-images-1.medium.com/max/2600/1*XaLiVe0TxIZhpiCsa7kk0A.jpeg" width="2800"></a></p><p class="medium-feed-snippet">Imagine developing an e-commerce site whose best-selling running shoes have just sold out. Customers click &#x201C;Notify me when it&#x2019;s back in&#x2026;</p><p class="medium-feed-link"><a href="https://levelup.gitconnected.com/observer-pattern-in-net-c-from-subject-and-update-to-events-and-reactive-extensions-2026-8c7c5f71a5b8?source=rss-6cbbf07417e5------2">Continue reading on Level Up Coding »</a></p></div>]]></description>
            <link>https://levelup.gitconnected.com/observer-pattern-in-net-c-from-subject-and-update-to-events-and-reactive-extensions-2026-8c7c5f71a5b8?source=rss-6cbbf07417e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/8c7c5f71a5b8</guid>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[design-patterns]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[c-sharp-programming]]></category>
            <dc:creator><![CDATA[Emanuele Longo]]></dc:creator>
            <pubDate>Fri, 13 Nov 2026 17:39:11 GMT</pubDate>
            <atom:updated>2026-11-13T17:39:11.547Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[AI in Software Architecture in .NET C#: Where language models belong in your application (2026)]]></title>
            <link>https://medium.com/@emanuele.longo992/ai-in-software-architecture-in-net-c-where-language-models-belong-in-your-application-2026-a9315400a6e4?source=rss-6cbbf07417e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/a9315400a6e4</guid>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[c-sharp-programming]]></category>
            <category><![CDATA[dotnet]]></category>
            <dc:creator><![CDATA[Emanuele Longo]]></dc:creator>
            <pubDate>Thu, 12 Nov 2026 14:01:02 GMT</pubDate>
            <atom:updated>2026-11-12T14:01:02.061Z</atom:updated>
            <content:encoded><![CDATA[<p>Imagine the e-commerce of the previous series: the order flow works, from the cart to the “Your order has been confirmed!” email. Now the business wants three new features: a product description written from the supplier’s notes, a short summary of the customer reviews, and an assistant that answers support questions such as “Where is my order?”. The first one takes an afternoon:</p><pre>// Naive approach - What NOT to do: the endpoint talks to the model vendor directly<br>app.MapPost(&quot;/products/{id:int}/description&quot;, async (int id, IProductCatalog catalog, IHttpClientFactory httpClientFactory) =&gt;<br>{<br>    var product = await catalog.FindAsync(id);<br>    if (product is null)<br>        return Results.NotFound();<br> <br>    var http = httpClientFactory.CreateClient();<br>    http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(&quot;Bearer&quot;, &quot;YOUR-API-KEY&quot;);   // a secret in the source code<br> <br>    var response = await http.PostAsJsonAsync(&quot;https://api.example.com/v1/chat/completions&quot;, new<br>    {<br>        model = &quot;the-biggest-model&quot;,                                                                      // chosen once, for every request<br>        messages = new[] { new { role = &quot;user&quot;, content = $&quot;Write a nice description for: {product.Name}. Notes from the supplier: {product.SupplierNotes}&quot; } }<br>    });<br> <br>    var json = await response.Content.ReadAsStringAsync();                                               // no status check, no timeout chosen, no retry<br>    var text = JsonDocument.Parse(json).RootElement<br>        .GetProperty(&quot;choices&quot;)[0].GetProperty(&quot;message&quot;).GetProperty(&quot;content&quot;).GetString();<br> <br>    return Results.Ok(new { description = text });                                                       // published as it is, nobody reads it first<br>});</pre><p>It works, today. The result?</p><ul><li>The <strong>vendor leaks into the endpoint</strong>: its URL, its JSON, its model names, its key.</li><li>The prompt is a string inside the endpoint, and the supplier’s notes are pasted into it without a thought. Whoever writes those notes can write instructions to the model.</li><li>When the provider is slow or fails, nobody has decided what the customer sees.</li><li>To test it, you either call the real service, and pay for it, or fake the vendor’s HTTP and JSON by hand.</li></ul><p>And you have two more features to go, each one with its own copy of this code.</p><p>This is exactly the kind of problem <strong>AI architecture</strong> was born to solve: <strong>treat the model as one more dependency, with the same discipline we give to a database or a payment provider, and a few extra rules for the ways it surprises us.</strong></p><figure><img alt="Cover of the AI in Software Architecture series: a blueprint with the title Where the Model Belongs, a request passing through three boxes towards a dashed circle, and the labels Model Port, Gateway, Routing, Semantic Cache, RAG, Guardrails, Observability, Prompts and Multi-tenant" src="https://cdn-images-1.medium.com/max/1024/1*ZUw_MZfXxCgyXYdPXlDWcw.jpeg" /><figcaption>AI in Software Architecture .NET C#</figcaption></figure><figure><img alt="Two designs for the same product description feature. Before: one endpoint knows the vendor URL and API key, the model name, the prompt text, the JSON of the answer, and has no timeout, fallback or log. After: the endpoint calls a use case, the use case talks to a port, and the port leads into a pipeline of telemetry, guardrails, cache and routing with fallback, which ends at the model provider. A vendor change stays in Program.cs" src="https://cdn-images-1.medium.com/max/1024/1*qmK6_iJGdiVmpcOptJbACQ.png" /><figcaption>The endpoint that knows the vendor, and the one that does not</figcaption></figure><p>This article is the map of a new series. It follows “Modern Software Architecture”, where we rebuilt the same order endpoint in many architectures without a single language model in sight. (If you want to find out more about that series, <a href="https://medium.com/p/0379b3051edc">click here</a>.) Now we add the new dependency, and ask where it belongs.</p><h3>A Different Kind of Dependency: Six ways a model surprises you</h3><p>A database, a payment gateway and a language model are all external services. But the model breaks some habits we take for granted:</p><ul><li><strong>Latency:</strong> usually slower and less predictable than the other calls of the same request.</li><li><strong>Cost per use:</strong> with most hosted models it grows with the size of the prompt and of the answer, so “just call it again” is a decision.</li><li><strong>Non-determinism:</strong> the same input can give different outputs, so a test asserting an exact sentence will fail one day for no reason.</li><li><strong>Silent failure:</strong> besides exceptions and timeouts, a model can answer fluently, confidently and wrongly. No status code tells you.</li><li><strong>Data leaving the building:</strong> the prompt may carry customer data outside your boundary.</li><li><strong>Change without you:</strong> the provider can update or retire a model, and the behavior you tested moves.</li></ul><p>None of these is a prompt problem. They are <strong>architecture problems</strong>: where the call lives, who may make it, what happens around it.</p><h3>The Origins: From a hidden debt to a middleware pipeline</h3><p>Software with a learned model inside is older than today’s chat assistants, and so is the lesson.</p><ul><li><strong>2015, the debt:</strong> D. Sculley and nine colleagues at Google presented “Hidden Technical Debt in Machine Learning Systems” at NIPS. Their message: a machine learning system is quick to build and expensive to maintain, and many of the risks are in the system design, with names like boundary erosion, entanglement, hidden feedback loops, undeclared consumers, data dependencies and configuration issues. Almost every item of that list will come back in this series.</li><li><strong>End of 2022, the wave:</strong> chat assistants built on large language models reached the general public, and teams rushed to call the same models from their own applications. The first integrations looked like the code above.</li><li><strong>March 2023, the SDKs:</strong> Microsoft published Semantic Kernel on GitHub, an open source SDK that brought language models into C# applications.</li><li><strong>October 8, 2024, the abstraction:</strong> the .NET team previewed Microsoft.Extensions.AI: shared interfaces for AI services, plus middleware for logging, caching and telemetry.</li><li><strong>May 21, 2025, the standard:</strong> Microsoft.Extensions.AI, its abstractions package and Microsoft.Extensions.VectorData.Abstractions reached general availability.</li></ul><p>It was clear that something familiar was happening. ASP.NET Core had given HTTP a pipeline of middleware, each piece wrapping the next. Now AI calls got the same treatment: one interface, IChatClient, and a chain of components around it. (If you want to find out more about the Decorator pattern, <a href="https://medium.com/p/111ace6e8658">click here</a>.) The provider-specific part sits behind an Adapter, which is what the client libraries do. (If you want to find out more about the Adapter, <a href="https://medium.com/p/f67d0f4b2ac0">click here</a>.)</p><h3>The Shape: Where the model sits</h3><p>The rule that will guide the whole series is short: <strong>the use case asks for a result, and the pipeline decides how to get it.</strong> The use case doesn’t know which provider answers, which model, whether the answer came from a cache, or how many times the call was retried.</p><figure><img alt="The path of one AI call, left to right: the use case, then telemetry, guardrails, semantic cache and routing with fallback, then the model, cloud or local. The four middle steps sit in an area called AI Gateway. The Model Port is in front of the use case. RAG and prompts feed the use case, multi-tenant AI applies to the gateway. Each box shows the date of its article. The order is illustrative" src="https://cdn-images-1.medium.com/max/1024/1*neZyRSbM2vsVT-0cpbXu3A.png" /><figcaption>The path of one AI call, and where each article fits</figcaption></figure><p>In a .NET solution this shows up as a few projects and a few arrows. The tree is illustrative, and the names are ours:</p><pre>Shop.slnx<br>src/<br>  Shop.Core/             use cases: PlaceOrder, SummarizeReviews, ...         -&gt; Microsoft.Extensions.AI.Abstractions<br>  Shop.Ai/               the pipeline: telemetry, guardrails, cache, routing  -&gt; Microsoft.Extensions.AI<br>  Shop.Infrastructure/   EF Core, payment client, email                       -&gt; Shop.Core<br>  Shop.Web/              minimal API, Program.cs                              -&gt; all of the above<br>tests/<br>  Shop.Core.Tests/       the model is a mock                                  -&gt; Shop.Core</pre><p>What does the port look like? Should the core know IChatClient, or a smaller interface that says &quot;describe this product&quot;? That&#39;s the subject of the first article. For now, the pipeline is where the cross-cutting concerns live, and in .NET it takes a few lines in the composition root. (If you want to find out more about Dependency Injection, <a href="https://medium.com/p/af2ca1222331">click here</a>.)</p><pre>// Composition root: the model is one more dependency, wired in one place<br>builder.Services.AddDistributedMemoryCache();<br> <br>builder.Services.AddChatClient(_ =&gt;<br>        new OpenAIClient(<br>                new ApiKeyCredential(builder.Configuration[&quot;Ai:ApiKey&quot;]!),                              // from user secrets or a vault, never from the source<br>                new OpenAIClientOptions { Endpoint = new Uri(builder.Configuration[&quot;Ai:Endpoint&quot;]!) })<br>            .GetChatClient(builder.Configuration[&quot;Ai:Model&quot;]!)<br>            .AsIChatClient())<br>    .UseOpenTelemetry()<br>    .UseDistributedCache()<br>    .UseLogging();</pre><p>Three details matter here. First, the endpoint, the model and the key come from configuration, not from the code: moving to another model or another compatible endpoint is a deployment decision, and replacing the client library is a change in this one place. Second, each Use... call wraps the client in a new layer, and <strong>the first one registered is the outermost</strong>: here the telemetry sees every request, even those answered by the cache, while the logging only sees the calls that reach the provider. (We checked the order with a test.) Third, writing your own layer is just a class that derives from DelegatingChatClient: this is how guardrails and tenant rules will be built (routing and failover get base classes of their own).</p><p>One caution, from the package itself: UseOpenTelemetry follows the OpenTelemetry conventions for generative AI, which its documentation still calls experimental. What you measure may change names, so we&#39;ll handle it with care.</p><h3>The Use Case Stays Boring: Testing without a model</h3><p>Now look at what the use case becomes when the model is a dependency like any other. For now it depends on IChatClient itself, from the small Microsoft.Extensions.AI.Abstractions package:</p><pre>public sealed class SummarizeReviewsHandler(IChatClient chat)<br>{<br>    public async Task&lt;string&gt; HandleAsync(IReadOnlyList&lt;Review&gt; reviews, CancellationToken cancellationToken)<br>    {<br>        if (reviews.Count == 0)<br>            return string.Empty;                                    // nothing to summarize: no call, no cost, no risk<br> <br>        List&lt;ChatMessage&gt; messages =<br>        [<br>            new(ChatRole.System, &quot;Summarize the customer reviews in two sentences. Use only what the reviews say.&quot;),<br>            new(ChatRole.User, string.Join(&quot;\n&quot;, reviews.Select(r =&gt; $&quot;{r.Rating}/5: {r.Text}&quot;)))<br>        ];<br> <br>        var response = await chat.GetResponseAsync(<br>            messages,<br>            new ChatOptions { Temperature = 0.2f, MaxOutputTokens = 200 },<br>            cancellationToken);<br> <br>        return response.Text;<br>    }<br>}</pre><p>There is plenty of logic worth testing here, and none of it needs a real model. The model is an interface, so a mock stands in for it:</p><pre>[Test]<br>public async Task HandleAsync_NoReviews_DoesNotCallTheModel()<br>{<br>    // Arrange<br>    var chat = new Mock&lt;IChatClient&gt;();<br>    var handler = new SummarizeReviewsHandler(chat.Object);<br> <br>    // Act<br>    var summary = await handler.HandleAsync([], CancellationToken.None);<br> <br>    // Assert<br>    Assert.That(summary, Is.Empty);<br>    chat.Verify(c =&gt; c.GetResponseAsync(<br>        It.IsAny&lt;IEnumerable&lt;ChatMessage&gt;&gt;(), It.IsAny&lt;ChatOptions?&gt;(), It.IsAny&lt;CancellationToken&gt;()), Times.Never);<br>}</pre><p>The test runs in milliseconds, costs nothing and never flakes. It checks what we <strong>own</strong>: when the model is called, what we send, what we do with the answer. What the model says is another matter: a mock proves that your code is correct, not that the answers are good.</p><h3>Part B: The patterns that belong to AI</h3><p>The first nine topics each take one problem that appears as soon as a model enters the application, and give it an architectural answer. Three of them, routing, RAG and guardrails, need two articles each. They are neutral: Microsoft.Extensions.AI, OpenTelemetry, ASP.NET Core and open libraries, no product attached.</p><figure><img alt="Map of the series. Part B, the AI patterns, November 14 to December 6: Model Port, AI Gateway, Routing and Fallback, Semantic Cache, RAG, Guardrails, Observing AI calls, Prompts as configuration, Multi-tenant AI. Part A, the earlier architectures with AI inside: Layered, MVC and Razor, MVVM, Hexagonal and Clean, Vertical Slice, CQRS, Event-Driven, Microkernel, Modular Monolith, Microservices, Serverless, and a reference architecture" src="https://cdn-images-1.medium.com/max/1024/1*5Sp8zxafBT34Dx09dCHDJQ.png" /><figcaption>The series map: patterns first, architectures after</figcaption></figure><ul><li><strong>Model Port (Nov 14):</strong> which interface does the application own? IChatClient is already shared by the ecosystem, but a use case wants &quot;describe this product&quot;, not &quot;messages in, text out&quot;.</li><li><strong>AI Gateway (Nov 16):</strong> one place where every call passes, with the keys, limits, logs and policies: inside the application or as a service in front of many.</li><li><strong>Model Routing and Fallback (Nov 18, 20):</strong> not every request needs the same model, and every provider fails sometimes. Microsoft.Extensions.AI has experimental pieces for routing and failover; we&#39;ll look at them and at a hand-written version.</li><li><strong>Semantic Cache (Nov 22):</strong> the exact cache is one line of the pipeline above. A semantic one uses embeddings (IEmbeddingGenerator), and the hard part is deciding when two questions are really the same.</li><li><strong>RAG as an architecture (Nov 24, 26):</strong> retrieval-augmented generation as components with owners: an ingestion pipeline, a store, and a retrieval step on the request path.</li><li><strong>Guardrails (Nov 28, 30):</strong> checks before and after the model. The output is untrusted input, and the application needs a plan for when a check fails.</li><li><strong>Observing AI calls (Dec 2):</strong> traces and metrics with OpenTelemetry, and what you must never write in a log.</li><li><strong>Prompts as versioned configuration (Dec 4):</strong> a prompt changes the behavior of the application as much as code does, so it deserves review, versions and rollback.</li><li><strong>Multi-tenant AI (Dec 6):</strong> isolating data, context, limits and cache, so that the answer prepared for one tenant never reaches another.</li></ul><p>What do you think? Which of these nine problems hurts most in your application today, or is the one you’re hoping to avoid? Leave a comment with your opinion.</p><h3>Part A: The same architectures, with AI inside</h3><p>The second part goes back to the architectures of the first series, one at a time, and asks the question that each one makes interesting:</p><ul><li><strong>Layered:</strong> which layer may call the model?</li><li><strong>MVC and Razor:</strong> where does a streamed answer live?</li><li><strong>MVVM:</strong> what changes when a view model calls the model, on a device?</li><li><strong>Hexagonal and Clean:</strong> is the chat an input port and the model an output adapter?</li><li><strong>Vertical Slice:</strong> one AI feature per slice, one shared pipeline.</li><li><strong>CQRS:</strong> what may a model read, and what may it change?</li><li><strong>Event-Driven:</strong> reacting to events with a call that is slow, costly and not repeatable.</li><li><strong>Microkernel:</strong> AI features as plugins.</li><li><strong>Modular Monolith:</strong> one AI module for everybody, or one per module?</li><li><strong>Microservices:</strong> where the gateway and the boundaries go.</li><li><strong>Serverless:</strong> functions, long answers and timeouts.</li><li><strong>A reference architecture</strong> that puts the pieces of both parts together.</li></ul><p>Each one starts where the classic version of that architecture leaves off. Part A starts on November 23 and runs on the days in between.</p><h3>So What?: When AI belongs in the architecture (and when not)</h3><p>A model deserves its own place in the architecture because its calls are <strong>costly, slow, uncertain or sensitive</strong>. That doesn’t mean every problem needs one. A rule you can write down should stay a rule:</p><pre>// You do NOT need a model for this: the rule is known, so write the rule<br>public static decimal ApplyCoupon(decimal total, string? couponCode) =&gt;<br>    couponCode == &quot;BENVENUTO10&quot; ? total * 0.9m : total;</pre><p>Asking a model whether a cart qualifies for a coupon would be slower, costlier, and sometimes wrong. The model is for work with no exact rule: writing, summarizing, classifying, answering in natural language. Three more criteria:</p><ul><li><strong>Consequence of a mistake:</strong> a wrong summary of a review is an annoyance; a wrong refund is a problem. The more it costs to be wrong, the more checks, and the more a human in the loop.</li><li><strong>Number of features:</strong> with one AI feature, a single well-written client may be enough. With three, the duplication starts to hurt, and the pipeline pays for itself.</li><li><strong>Data and rules:</strong> if the prompt carries personal data, or customers expect isolation, the gateway and the tenant rules stop being optional.</li></ul><h3>Modern Best Practices (2026): Five ideas that return in every article</h3><p>Whatever the pattern or the architecture, the same ideas keep coming back:</p><ol><li><strong>Own the interface, rent the model.</strong> Your code depends on a port you control; the provider is a detail.</li><li><strong>One place for the rules of every call.</strong> Limits, logs, policies and retries live in the pipeline, not in forty endpoints.</li><li><strong>Assume the answer can be wrong.</strong> Check it, bound it, and design what happens when it is.</li><li><strong>Test what you own.</strong> Mock the model, and evaluate the quality of the answers as a separate activity.</li><li><strong>Measure before you tune.</strong> Without traces and metrics, “it feels slow” is an opinion.</li></ol><h3>What’s Next</h3><p>AI in software architecture is not a new religion with new commandments. It is the old craft of <strong>drawing boundaries around something unpredictable</strong>, applied to a component that happens to speak our language. The patterns of part B are the first boundaries, and the architectures of part A show where they fit.</p><p>The next article starts with the interface itself: the Model Port. Because before you decide how to call a model, you need to decide what your application is allowed to know about it.</p><p>The key to success is not adding AI everywhere, but <strong>knowing exactly where it belongs and what must surround it</strong>.</p><p>Thanks for reading.</p><p>If you found this article helpful, follow me for the next ones or just clap.</p><p>Back to article <a href="https://medium.com/p/0379b3051edc">Software Architecture in .NET C#</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a9315400a6e4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Frozen Collections and SearchValues in .NET]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://levelup.gitconnected.com/frozen-collections-and-searchvalues-in-net-75a60c11211b?source=rss-6cbbf07417e5------2"><img src="https://cdn-images-1.medium.com/max/2400/1*S1ajn3nJTbD3pHybI_9tOw.jpeg" width="2400"></a></p><p class="medium-feed-snippet">Lookup tables are one of the quiet pillars of every business application.</p><p class="medium-feed-link"><a href="https://levelup.gitconnected.com/frozen-collections-and-searchvalues-in-net-75a60c11211b?source=rss-6cbbf07417e5------2">Continue reading on Level Up Coding »</a></p></div>]]></description>
            <link>https://levelup.gitconnected.com/frozen-collections-and-searchvalues-in-net-75a60c11211b?source=rss-6cbbf07417e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/75a60c11211b</guid>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[c-sharp-programming]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Emanuele Longo]]></dc:creator>
            <pubDate>Wed, 11 Nov 2026 17:23:05 GMT</pubDate>
            <atom:updated>2026-11-11T17:23:05.590Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[State Pattern in .NET C#: From scattered switches to exhaustive state machines (2026)]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://levelup.gitconnected.com/state-pattern-in-net-c-from-scattered-switches-to-exhaustive-state-machines-2026-f016c2dbb968?source=rss-6cbbf07417e5------2"><img src="https://cdn-images-1.medium.com/max/2600/1*FzXpsEzu9ivuJqdOYXBKMw.jpeg" width="2800"></a></p><p class="medium-feed-snippet">Imagine developing the order management of an e-commerce. An order is born Pending, becomes Paid, then Shipped and Delivered. It can be&#x2026;</p><p class="medium-feed-link"><a href="https://levelup.gitconnected.com/state-pattern-in-net-c-from-scattered-switches-to-exhaustive-state-machines-2026-f016c2dbb968?source=rss-6cbbf07417e5------2">Continue reading on Level Up Coding »</a></p></div>]]></description>
            <link>https://levelup.gitconnected.com/state-pattern-in-net-c-from-scattered-switches-to-exhaustive-state-machines-2026-f016c2dbb968?source=rss-6cbbf07417e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/f016c2dbb968</guid>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[c-sharp-programming]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[design-patterns]]></category>
            <dc:creator><![CDATA[Emanuele Longo]]></dc:creator>
            <pubDate>Wed, 11 Nov 2026 17:21:54 GMT</pubDate>
            <atom:updated>2026-11-11T17:21:54.567Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Mediator Pattern in .NET C#: From dialog boxes to in-process request handlers (2026)]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://levelup.gitconnected.com/mediator-pattern-in-net-c-from-dialog-boxes-to-in-process-request-handlers-2026-8ed5011292b7?source=rss-6cbbf07417e5------2"><img src="https://cdn-images-1.medium.com/max/2600/1*ZZTlHlsoA_3bhqaCHlgEcA.jpeg" width="2800"></a></p><p class="medium-feed-snippet">Imagine developing the checkout page of an e-commerce site. Four components share the screen: the cart with its quantities, a box for&#x2026;</p><p class="medium-feed-link"><a href="https://levelup.gitconnected.com/mediator-pattern-in-net-c-from-dialog-boxes-to-in-process-request-handlers-2026-8ed5011292b7?source=rss-6cbbf07417e5------2">Continue reading on Level Up Coding »</a></p></div>]]></description>
            <link>https://levelup.gitconnected.com/mediator-pattern-in-net-c-from-dialog-boxes-to-in-process-request-handlers-2026-8ed5011292b7?source=rss-6cbbf07417e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/8ed5011292b7</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[c-sharp-programming]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[design-patterns]]></category>
            <category><![CDATA[dotnet]]></category>
            <dc:creator><![CDATA[Emanuele Longo]]></dc:creator>
            <pubDate>Wed, 11 Nov 2026 17:19:23 GMT</pubDate>
            <atom:updated>2026-11-11T17:19:23.910Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Choosing an Architecture in .NET C#: From fashion to a decision you can defend (2026)]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@emanuele.longo992/choosing-an-architecture-in-net-c-from-fashion-to-a-decision-you-can-defend-2026-0f30de1679a0?source=rss-6cbbf07417e5------2"><img src="https://cdn-images-1.medium.com/max/1400/1*ji3Xmi-QumD_JEHgX_oJaA.jpeg" width="1400"></a></p><p class="medium-feed-snippet">Imagine you are starting the new version of your online shop on Monday morning. The team of four meets to decide the architecture, and the&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/@emanuele.longo992/choosing-an-architecture-in-net-c-from-fashion-to-a-decision-you-can-defend-2026-0f30de1679a0?source=rss-6cbbf07417e5------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@emanuele.longo992/choosing-an-architecture-in-net-c-from-fashion-to-a-decision-you-can-defend-2026-0f30de1679a0?source=rss-6cbbf07417e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/0f30de1679a0</guid>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[c-sharp-programming]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[software-design]]></category>
            <dc:creator><![CDATA[Emanuele Longo]]></dc:creator>
            <pubDate>Wed, 11 Nov 2026 17:01:02 GMT</pubDate>
            <atom:updated>2026-11-11T17:01:02.324Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Architecture Tests and ADRs in .NET]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@emanuele.longo992/architecture-tests-and-adrs-in-net-b12d5c6a83fd?source=rss-6cbbf07417e5------2"><img src="https://cdn-images-1.medium.com/max/1400/1*J8wN_KNnioFgREo4ZUFMvg.jpeg" width="1400"></a></p><p class="medium-feed-snippet">Imagine your shop is six months old and the team has doubled. The order feature works, the rules live in Shop.Core, and the project&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/@emanuele.longo992/architecture-tests-and-adrs-in-net-b12d5c6a83fd?source=rss-6cbbf07417e5------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@emanuele.longo992/architecture-tests-and-adrs-in-net-b12d5c6a83fd?source=rss-6cbbf07417e5------2</link>
            <guid isPermaLink="false">https://medium.com/p/b12d5c6a83fd</guid>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[software-design]]></category>
            <category><![CDATA[c-sharp-programming]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Emanuele Longo]]></dc:creator>
            <pubDate>Tue, 10 Nov 2026 17:01:02 GMT</pubDate>
            <atom:updated>2026-11-10T17:01:02.095Z</atom:updated>
        </item>
    </channel>
</rss>