<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: thecodewrapper</title>
    <description>The latest articles on DEV Community by thecodewrapper (@thecodewrapper).</description>
    <link>https://dev.to/thecodewrapper</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F808330%2Ff1b08631-56d4-438f-8ed6-1652ab406b38.jpg</url>
      <title>DEV Community: thecodewrapper</title>
      <link>https://dev.to/thecodewrapper</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thecodewrapper"/>
    <language>en</language>
    <item>
      <title>The Evolution of the Developer in the Era of AI Agents</title>
      <dc:creator>thecodewrapper</dc:creator>
      <pubDate>Fri, 20 Feb 2026 15:58:27 +0000</pubDate>
      <link>https://dev.to/thecodewrapper/the-evolution-of-the-developer-in-the-era-of-ai-agents-5akp</link>
      <guid>https://dev.to/thecodewrapper/the-evolution-of-the-developer-in-the-era-of-ai-agents-5akp</guid>
      <description>&lt;p&gt;Over the past days, a recurring theme has surfaced in conversations with colleagues — often not in formal meetings, but around lunch tables, coffee machines, and hallway chats. The topic is unavoidable: AI coding agents. Alongside the excitement sits a quiet, restrained anxiety. What happens to software developers when machines can write code?&lt;/p&gt;

&lt;p&gt;My perspective is neither alarmist nor dismissive. Yes, change is coming — profound change. But no, the developer role is not disappearing. What we are witnessing is not extinction. It is evolution.&lt;/p&gt;

&lt;p&gt;And like most technological shifts in our field, this one will likely unfold in stages.&lt;/p&gt;




&lt;h2&gt;
  
  
  From Tool to Teammate
&lt;/h2&gt;

&lt;p&gt;Historically, programming tools have always evolved to increase abstraction. We moved from assembly language to high-level languages, from manual memory management to managed runtimes, from bare-metal development to frameworks and platforms, and from manual deployments to fully automated cloud pipelines. Each step reduced the need to write repetitive or low-level code while increasing the importance of design, intent, and problem understanding.&lt;/p&gt;

&lt;p&gt;AI coding agents represent the next layer of abstraction — one that operates not on syntax, but on intent expressed in natural language. They do not merely autocomplete lines of code; they can generate entire modules, tests, documentation, and even deployment configurations. In effect, they begin to resemble junior collaborators rather than tools.&lt;/p&gt;

&lt;p&gt;But collaborators still need direction, context, and oversight.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 1: The Mild Shift — From Writing Code to Reviewing It
&lt;/h2&gt;

&lt;p&gt;The first noticeable transition is already underway, and many teams are already experiencing it. Developers are writing less raw code themselves and spending more time evaluating code produced by AI systems. The daily workflow begins to resemble that of a reviewer or editor rather than a manual implementer.&lt;/p&gt;

&lt;p&gt;In this phase, developers increasingly articulate requirements through structured prompts, examine generated code for correctness and security, refactor outputs to meet production standards, validate edge cases, and ensure consistency with existing systems. The role shifts toward verifying that the solution behaves as intended and integrates safely into a larger codebase.&lt;/p&gt;

&lt;p&gt;What does not change is the need for critical thinking. AI can produce code that is syntactically correct but inefficient, fragile under edge conditions, insecure, or misaligned with business rules and architectural constraints. Review, therefore, becomes the primary skill. Writing code transforms into a process of expressing intent clearly and then verifying the machine’s interpretation.&lt;/p&gt;

&lt;p&gt;Interestingly, this shift may affect junior developers first. Historically, early-career engineers added value through volume of implementation. When that volume can be generated automatically, experience becomes the differentiator. Engineers who can detect subtle flaws, foresee downstream consequences, and maintain system coherence become significantly more valuable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 2: The Significant Shift — Developers as Architects
&lt;/h2&gt;

&lt;p&gt;As AI agents grow more capable, the transformation becomes more profound. Developers cease to be primarily implementers and evolve into system designers and orchestrators. The central question moves away from “How do I code this?” toward “What system should exist, and what properties must it satisfy?”&lt;/p&gt;

&lt;p&gt;Responsibilities expand into areas traditionally associated with senior engineers or software architects. Developers must define domain boundaries, select architectural patterns, design data flows, and plan for scalability and resilience from the outset. They must also consider performance characteristics and resource efficiency, since AI-generated solutions are not inherently optimized. Decisions about latency, throughput, and cloud costs become part of everyday engineering judgment.&lt;/p&gt;

&lt;p&gt;Operational excellence becomes equally central. Modern software is not simply a collection of source files but a living system deployed across infrastructure, monitored continuously, and expected to remain reliable under unpredictable conditions. Engineers must therefore design for observability, deployment strategies, fault tolerance, security posture, and regulatory compliance. In essence, the role shifts from building components to engineering systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Developers Will Not Disappear
&lt;/h2&gt;

&lt;p&gt;Despite rapid advances, several fundamental factors make full replacement of developers unlikely.&lt;/p&gt;

&lt;p&gt;Software does not exist in isolation; it exists within a socio-technical environment shaped by business goals, organizational constraints, legal obligations, human workflows, and political realities. AI systems have no stake in these contexts. Humans interpret them, negotiate them, and translate them into technical solutions.&lt;/p&gt;

&lt;p&gt;Moreover, ambiguity is the default state of real-world projects. Requirements are often incomplete or contradictory. Stakeholders change priorities, discover new needs, or disagree with one another. AI performs best when the problem is clearly defined, but much of software engineering involves clarifying the problem itself.&lt;/p&gt;

&lt;p&gt;Accountability is another critical dimension. When a system fails, organizations require someone to explain what happened, determine responsibility, and guide remediation. Machines cannot be held accountable in any meaningful organizational sense.&lt;/p&gt;

&lt;p&gt;Finally, innovation depends on judgment. AI excels at extrapolating from existing patterns, but breakthrough ideas frequently arise from cross-domain thinking, intuition, and risk-taking — qualities rooted in human experience rather than statistical prediction.&lt;/p&gt;




&lt;h2&gt;
  
  
  A New Skill Set Emerges
&lt;/h2&gt;

&lt;p&gt;If routine coding becomes partially commoditized, new differentiators emerge. Future high-value developers will need to communicate precisely, because expressing intent clearly becomes the foundation for effective collaboration with AI systems. Prompting is not merely typing instructions; it is structured problem formulation grounded in domain knowledge.&lt;/p&gt;

&lt;p&gt;Equally important is the ability to evaluate outputs critically. Engineers must recognize when a solution appears correct but contains hidden flaws, performance pitfalls, or security vulnerabilities. This requires deep understanding rather than surface familiarity.&lt;/p&gt;

&lt;p&gt;Architectural thinking becomes central as well. Developers must reason about systems at multiple levels of abstraction, connecting business objectives to technical implementation and infrastructure realities. Knowledge across disciplines — product design, operations, data management, and security — increasingly defines expertise.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Risk Is Not AI — It Is Stagnation
&lt;/h2&gt;

&lt;p&gt;The real danger facing developers is not replacement by machines, but obsolescence through resistance to change. Our industry consistently rewards those who adopt new abstractions early. Engineers who remained tied to assembly were overtaken by those who embraced high-level languages. Those who resisted frameworks or cloud computing found themselves outpaced by those who leveraged them.&lt;/p&gt;

&lt;p&gt;AI agents represent another step on this same trajectory. The competitive advantage will belong to those who learn to direct these tools effectively rather than compete with them at tasks they are designed to automate.&lt;/p&gt;




&lt;h2&gt;
  
  
  A More Human Role, Not a Less Human One
&lt;/h2&gt;

&lt;p&gt;Paradoxically, as machines assume mechanical tasks, the remaining work becomes more human. Judgment, creativity, empathy for users, ethical reasoning, and leadership grow in importance. Software development may shift from a craft dominated by syntax toward one centered on decision-making and problem solving.&lt;/p&gt;

&lt;p&gt;In this sense, AI may elevate the profession rather than diminish it. Developers will spend less time translating ideas into code and more time shaping what should be built and why.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;We are not approaching the end of the developer profession. We are approaching the end of the developer as a code-producing machine.&lt;/p&gt;

&lt;p&gt;The future developer more closely resembles a systems thinker, architect, reviewer, integrator, and product-aware engineer. AI coding agents will transform how software is created, but not the need for human direction, responsibility, and insight.&lt;/p&gt;

&lt;p&gt;The question is not whether developers will remain relevant.&lt;/p&gt;

&lt;p&gt;The question is what kind of developers we choose to become.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>The 3 Most Practical Features of C# 14 for Everyday Developers</title>
      <dc:creator>thecodewrapper</dc:creator>
      <pubDate>Sun, 21 Sep 2025 10:21:58 +0000</pubDate>
      <link>https://dev.to/thecodewrapper/the-3-most-practical-features-of-c-14-for-everyday-developers-3hm7</link>
      <guid>https://dev.to/thecodewrapper/the-3-most-practical-features-of-c-14-for-everyday-developers-3hm7</guid>
      <description>&lt;p&gt;C# 14 is just around the corner, and it ships alongside .NET 10, the next big milestone for the .NET ecosystem.&lt;br&gt;
The official release of .NET 10 is scheduled for &lt;strong&gt;November 2025&lt;/strong&gt;, and it will be a &lt;strong&gt;Long-Term Support (LTS)&lt;/strong&gt; release — meaning you’ll get free patches and updates for three years.&lt;/p&gt;

&lt;p&gt;Even though the final release isn’t here yet, the &lt;strong&gt;Release Candidate (RC)&lt;/strong&gt; for .NET 10 is already available. That means you can start experimenting with C# 14 &lt;strong&gt;today&lt;/strong&gt;, try out its new language features, and get a head start on upgrading your apps.&lt;/p&gt;

&lt;p&gt;In this post, we’ll explore &lt;strong&gt;the 3 most practical features of C# 14 for everyday developers&lt;/strong&gt;, with clear explanations, before-and-after examples, and practical tips for when to use each feature.&lt;/p&gt;


&lt;h2&gt;
  
  
  Null-Conditional Assignment
&lt;/h2&gt;

&lt;p&gt;Null-checking before an assignment is one of the most repetitive tasks in C#. Previously, you had to guard every assignment with an &lt;code&gt;if&lt;/code&gt; statement, which bloated code and added indentation. This feature removes that noise while still being safe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before C#14&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetCurrentOrder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;C# 14 now allows you to use &lt;code&gt;?.&lt;/code&gt; and &lt;code&gt;?[]&lt;/code&gt; on the &lt;strong&gt;left-hand side&lt;/strong&gt; of an assignment (or compound assignment like &lt;code&gt;+=&lt;/code&gt;). If the receiver is &lt;code&gt;null&lt;/code&gt;, the assignment is skipped with no exceptions thrown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// With C#14&lt;/span&gt;
&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetCurrentOrder&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The right side of the &lt;code&gt;=&lt;/code&gt; operator is evaluated only when the left side isn’t null. If &lt;code&gt;customer&lt;/code&gt; is null, the code doesn’t call &lt;code&gt;GetCurrentOrder&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caveats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The right-hand side won’t be evaluated if the receiver is null — don’t put important side effects there if they must always run.&lt;/li&gt;
&lt;li&gt;Doesn’t work with increment/decrement operators (&lt;code&gt;++&lt;/code&gt; or &lt;code&gt;--&lt;/code&gt;). Only assignment and compound assignment are supported.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The &lt;code&gt;field&lt;/code&gt; Keyword
&lt;/h2&gt;

&lt;p&gt;Up until now, if you wanted to enforce a rule in a property setter (like preventing &lt;code&gt;null&lt;/code&gt; assignments), you had to create a separate backing field and write both accessors yourself, losing the simplicity of auto-properties.&lt;/p&gt;

&lt;p&gt;In the example below, we’re forced to introduce a &lt;code&gt;_message&lt;/code&gt; field just so we can add a &lt;code&gt;null&lt;/code&gt; check in the setter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before C#14&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;_message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;C# 14 introduces the &lt;code&gt;field&lt;/code&gt; keyword, which allows you to reference the compiler-generated backing field of an auto-property directly. This means you can add logic to a property accessor without having to manually declare and manage a private field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// With C#14&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;field&lt;/code&gt; token is replaced by the compiler with a synthesized backing field. You still get a fully functioning auto-property, but with custom accessor logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caveats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If your type already has a member named &lt;code&gt;field&lt;/code&gt;, this could be confusing. You can disambiguate by using &lt;code&gt;@field&lt;/code&gt; or &lt;code&gt;this.field&lt;/code&gt;, or (better) rename the existing member.&lt;/li&gt;
&lt;li&gt;Remember that &lt;code&gt;field&lt;/code&gt; only exists inside the property accessor body — it’s not available anywhere else.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Extension Members and the Extension Syntax
&lt;/h2&gt;

&lt;p&gt;Extension methods were a big deal when they were added in C# 3.0 — they let you add functionality to existing types (like LINQ does) without modifying them.&lt;br&gt;
But methods were all you could add — no properties, no operators, no static helpers. C# 14 removes those limitations.&lt;/p&gt;

&lt;p&gt;C# 14 introduces a &lt;strong&gt;new syntax for extension members&lt;/strong&gt;, letting you do more than just add methods.&lt;/p&gt;

&lt;p&gt;With the new &lt;code&gt;extension&lt;/code&gt; blocks, you can now declare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Extension properties&lt;/strong&gt; — so you can call &lt;code&gt;obj.MyProperty&lt;/code&gt; on types you don’t own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static extension members&lt;/strong&gt; — members that appear as if they’re part of the type itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User-defined operators&lt;/strong&gt; — adding custom operators like &lt;code&gt;+&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, or even comparison operators to existing types.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes extensions feel more natural, letting you express domain logic as if you had direct control over the type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before C#14&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ListExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsEmpty&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;list1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;list2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;combined&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;list1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// With C#14&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ListExtensions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Instance-style extension members&lt;/span&gt;
    &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsEmpty&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Static (type-level) extension members&lt;/span&gt;
    &lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;operator&lt;/span&gt; &lt;span class="p"&gt;+(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Empty&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;list1&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;list2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsEmpty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;combined&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;list1&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;list2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;emptyList&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Extensions now look and behave like native members of the type.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Adding &lt;strong&gt;semantic helpers&lt;/strong&gt; to framework types (like &lt;code&gt;.IsEmpty&lt;/code&gt; or &lt;code&gt;.FullName&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Providing &lt;strong&gt;type-level utilities&lt;/strong&gt; (like &lt;code&gt;List&amp;lt;T&amp;gt;.Empty&lt;/code&gt; or &lt;code&gt;DateOnly.Today&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Defining &lt;strong&gt;domain-specific operators&lt;/strong&gt; (e.g. &lt;code&gt;+&lt;/code&gt; for combining domain objects).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Caveats
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keep it intuitive — avoid adding operators or properties that surprise readers.&lt;/li&gt;
&lt;li&gt;Remember that extension members are still &lt;em&gt;syntactic sugar&lt;/em&gt; — they don’t actually modify the type.&lt;/li&gt;
&lt;li&gt;Consider discoverability: too many extension blocks scattered across namespaces can confuse users.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;C# 14 is a refinement release that focuses on making everyday code cleaner and more expressive.&lt;/p&gt;

&lt;p&gt;With null-conditional assignment, you eliminate repetitive null checks. With the &lt;code&gt;field&lt;/code&gt; keyword, you get custom logic on auto-properties without sacrificing simplicity. And with extension members, you can make your code feel more natural by adding properties, operators, and utilities to types you don’t own.&lt;/p&gt;

&lt;p&gt;These features may seem small at first glance, but they add up to a more elegant developer experience — fewer lines of boilerplate, less ceremony, and more readable code.&lt;/p&gt;

&lt;p&gt;For more features and full details on what’s new in C# 14, check out &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-14#the-field-keyword" rel="noopener noreferrer"&gt;“What’s new in C# 14” on Microsoft Learn&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>dotnet</category>
      <category>csharp</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>How to: Properly Simulate a Race Condition in C#</title>
      <dc:creator>thecodewrapper</dc:creator>
      <pubDate>Sat, 15 Feb 2025 08:29:46 +0000</pubDate>
      <link>https://dev.to/thecodewrapper/how-to-properly-simulate-a-race-condition-in-c-37o8</link>
      <guid>https://dev.to/thecodewrapper/how-to-properly-simulate-a-race-condition-in-c-37o8</guid>
      <description>&lt;p&gt;Race conditions are one of the trickiest and most elusive bugs in multithreaded programming. They occur when multiple threads access and modify shared data simultaneously, leading to unpredictable results.&lt;/p&gt;

&lt;p&gt;In this article, we will explore how to intentionally create a race condition, analyze why it happens, and demonstrate the best ways to properly simulate a race condition in C#.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding a Race Condition with a Simple Counter
&lt;/h2&gt;

&lt;p&gt;Before diving into real-world examples, let’s start with a simple demonstration using a counter. This example shows a basic race condition where multiple threads increment a shared variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Threading;

class Program
{
    static int counter = 0;

    static void IncrementCounter()
    {
        for (int i = 0; i &amp;lt; 1000; i++)
        {
            counter++; // NOT ATOMIC → Race condition occurs
        }
    }

    static void Main()
    {
        Thread thread1 = new Thread(IncrementCounter);
        Thread thread2 = new Thread(IncrementCounter);

        thread1.Start();
        thread2.Start();

        thread1.Join();
        thread2.Join();

        Console.WriteLine($"Final counter value: {counter} (Expected: 2000)");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why is this a race condition?
&lt;/h3&gt;

&lt;p&gt;The statement counter++ is &lt;strong&gt;not atomic&lt;/strong&gt;; it consists of three operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read counter value.&lt;/li&gt;
&lt;li&gt;Increment the value.&lt;/li&gt;
&lt;li&gt;Store the updated value back.
When multiple threads execute this statement simultaneously, they may read the same initial value before either writes it back, &lt;strong&gt;leading to lost updates&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This example demonstrates a race condition, but in practical applications, race conditions often involve more than just a counter. Let’s now look at a real-world scenario and test different ways to create a race condition.&lt;/p&gt;




&lt;h2&gt;
  
  
  A More Real-World Example: Bank Account Transactions
&lt;/h2&gt;

&lt;p&gt;In this example, we will use a bank account withdrawal system where multiple users attempt to withdraw money simultaneously. This allows us to observe how race conditions might affect financial transactions.&lt;/p&gt;

&lt;p&gt;If a race condition occurs, we might see an incorrect final balance, such as a negative value, indicating that multiple threads withdrew money at the same time without proper synchronization. If no race condition occurs, we would expect the final balance to be exactly 0 after all withdrawals are complete.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Threading;

class BankAccount
{
    private int _balance;

    public int GetBalance() =&amp;gt; _balance;
    public BankAccount(int initialBalance) {
        _balance = initialBalance;
    }

    public void Withdraw(int amount) {
        if (_balance &amp;gt; 0) {
            _balance -= amount;
        }
    }
}

class Program
{
    static void Main() {
        BankAccount account = new BankAccount(1000);
        int threadCount = 15;
        Thread[] threads = new Thread[threadCount];

        for (int i = 0; i &amp;lt; threadCount; i++) {
            threads[i] = new Thread(() =&amp;gt; account.Withdraw(100));
            threads[i].Start();
        }

        foreach (var thread in threads)
            thread.Join();

        Console.WriteLine($"Final account balance: {account.GetBalance()} (Expected: 0 or negative value if a race condition occurs)");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a race condition occurs, we might see an incorrect final balance, such as a negative value, indicating that multiple threads withdrew money at the same time without proper synchronization. If no race condition occurs, we would expect the final balance to be exactly 0 after all withdrawals are complete.&lt;/p&gt;

&lt;p&gt;Is this guaranteed to create a race condition however? The answer is no, because of thread-scheduling, CPU allocation and memory optimizations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thread Scheduling is Non-Deterministic
&lt;/h3&gt;

&lt;p&gt;The OS scheduler decides when to switch between threads, meaning that execution order is unpredictable. Sometimes, one thread may complete all its operations before another even starts, reducing contention. Other times, two or more threads might execute &lt;code&gt;_balance -= amount&lt;/code&gt; concurrently, leading to lost updates or corrupted values.&lt;/p&gt;

&lt;h3&gt;
  
  
  CPU Speed and Core Allocation
&lt;/h3&gt;

&lt;p&gt;If the CPU switches context too slowly, a single thread may complete multiple operations before another thread gets a chance to run, effectively eliminating the possibility of interleaved execution. Additionally, if the CPU schedules each thread to a separate core, operations may be executed in a sequential rather than interleaved manner, further reducing the likelihood of a race condition occurring.&lt;/p&gt;

&lt;h3&gt;
  
  
  JIT Optimizations &amp;amp; Memory Reordering
&lt;/h3&gt;

&lt;p&gt;The .NET JIT compiler may optimize code execution differently across runs. This means that even identical code can produce varying results based on how the compiler decides to optimize memory accesses. Additionally, modern CPUs may reorder memory writes, meaning that updates made by one thread may not be immediately visible to another, sometimes preventing or delaying expected conflicts.&lt;/p&gt;

&lt;p&gt;Evidently, this means that while race conditions &lt;strong&gt;can&lt;/strong&gt; happen, they are &lt;strong&gt;not guaranteed&lt;/strong&gt; every time the program is run.&lt;/p&gt;




&lt;h2&gt;
  
  
  What if we increase thread count to 1000?
&lt;/h2&gt;

&lt;p&gt;In an attempt to increase the likelihood of encountering a race condition, we can significantly raise the number of concurrent threads operating on the shared resource. The idea is that a higher number of threads will lead to more simultaneous access, increasing contention.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static void Main()
{
    BankAccount account = new BankAccount(1000);
    int threadCount = 1000;
    Thread[] threads = new Thread[threadCount];

    for (int i = 0; i &amp;lt; threadCount; i++)
    {
        threads[i] = new Thread(() =&amp;gt; account.Withdraw(100));
        threads[i].Start();
    }

    foreach (var thread in threads)
        thread.Join();

    Console.WriteLine($"Final account balance: {account.GetBalance()} (Expected: 0 or negative value if a race condition occurs)");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Increasing thread count makes issues more likely, but it does not &lt;strong&gt;guarantee&lt;/strong&gt; a race condition every time.&lt;/p&gt;

&lt;p&gt;In fact, instead of a clear race condition, you may just experience performance degradation. The final value of the counter may sometimes be close to the expected value if most threads execute sequentially rather than in parallel. However, due to &lt;strong&gt;random interleaving&lt;/strong&gt;, there may still be occasional incorrect values, making the race condition inconsistent and difficult to reproduce reliably.&lt;/p&gt;

&lt;p&gt;So, how do we reliably maximize our chances to create a race condition?&lt;/p&gt;




&lt;h2&gt;
  
  
  Ensuring a Race Condition Always Occurs
&lt;/h2&gt;

&lt;p&gt;Adding artificial delays such as &lt;code&gt;Thread.Sleep(1)&lt;/code&gt; inside the &lt;code&gt;Withdraw&lt;/code&gt; method increases the likelihood of a race condition happening every time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public void Withdraw(int amount)
{
    if (_balance &amp;gt; 0)
    {
        Thread.Sleep(1); // Force a small delay to increase interleaving
        _balance -= amount;
        Console.WriteLine($"{Thread.CurrentThread.Name} withdrawing... Current Balance After: {_balance}");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By pausing execution at a crucial moment, this approach ensures that multiple threads access and modify &lt;code&gt;_balance&lt;/code&gt; at the same time, making the race condition visible in every execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does this happen?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Thread.Sleep(1)&lt;/code&gt; causes a race condition because it forces the operating system to &lt;strong&gt;pause execution of the current thread&lt;/strong&gt; and allow other threads to run. This interruption occurs &lt;strong&gt;after the balance check (&lt;code&gt;_balance &amp;gt; 0&lt;/code&gt;) but before updating &lt;code&gt;_balance&lt;/code&gt;&lt;/strong&gt;, creating a window where another thread can enter the method, read the same (stale) balance, and proceed with its own withdrawal.&lt;/p&gt;

&lt;p&gt;Since both threads see the same initial balance before either updates it, they perform calculations based on stale data, leading to incorrect final values.&lt;/p&gt;

&lt;p&gt;Without &lt;code&gt;Thread.Sleep(1)&lt;/code&gt;, the race condition might occur unpredictably based on system load and thread scheduling, but with it, the problem is &lt;strong&gt;consistently reproduced&lt;/strong&gt;, making it easier to observe and debug.&lt;/p&gt;




&lt;h2&gt;
  
  
  A More Natural Way – Using a Semaphore to Force Contention
&lt;/h2&gt;

&lt;p&gt;While adding artificial delays – almost – &lt;strong&gt;guarantees a race condition&lt;/strong&gt;, using a semaphore provides a more structured way to test for it. Instead of relying on CPU timing and artificial delays, we can &lt;strong&gt;line up threads at the race point and use a semaphore to release them all simultaneously&lt;/strong&gt;. This method ensures that multiple threads enter the critical section together, increasing contention naturally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class BankAccount
{
    private int _balance;

    public int GetBalance() =&amp;gt; _balance;
    public BankAccount(int initialBalance) {
        _balance = initialBalance;
    }

    public void Withdraw(int amount) {
        if (_balance &amp;gt; 0) {
            _balance -= amount;
            Console.WriteLine($"{Thread.CurrentThread.Name} withdrawing... Current Balance After: {_balance}");
        }
    }
}

class Program
{
    static void Main() {
        BankAccount account = new BankAccount(1000);
        int threadCount = 150;
        Thread[] threads = new Thread[threadCount];
        SemaphoreSlim semaphore = new SemaphoreSlim(0); // Initially locked

        for (int i = 0; i &amp;lt; threadCount; i++) {
            threads[i] = new Thread(() =&amp;gt;
            {
                semaphore.Wait();
                account.Withdraw(100);
            });
            threads[i].Start();
        }

        Thread.Sleep(1000); //Wait for all threads to start

        Console.WriteLine("Releasing all threads at once...");
        semaphore.Release(threadCount); // Release all threads at once!

        foreach (var thread in threads) {
            thread.Join();
        }

        Console.WriteLine($"Final account balance: {account.GetBalance()} (Expected: Negative value due to race condition)");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using a semaphore to hold back all threads and then releasing them simultaneously, we increase the chances of multiple threads attempting to modify the shared resource at the exact same time. This forces the race condition to occur more predictably, making it easier to observe and analyze.&lt;/p&gt;

&lt;p&gt;Of course, in this specific example, it is likely that &lt;strong&gt;no race condition will actually occur when you run the code&lt;/strong&gt;. The reason is that the decrement operation (&lt;code&gt;_balance -= amount;&lt;/code&gt;) executes so quickly that the chances of interleaving between threads are extremely low. Since there are no artificial delays (&lt;code&gt;Thread.Sleep&lt;/code&gt;) or additional read-modify-write steps before the subtraction, most CPUs will execute each withdrawal sequentially rather than in parallel.&lt;/p&gt;

&lt;p&gt;The OS scheduler may also serialize execution across threads, reducing the possibility of lost updates. This means that while multiple threads are running, each thread is likely seeing and modifying the most recent value of &lt;code&gt;_balance&lt;/code&gt; rather than an outdated one.&lt;/p&gt;

&lt;p&gt;Despite that, this approach &lt;strong&gt;simulates contention in a structured way&lt;/strong&gt;, allowing you to test for race conditions without relying on unpredictable delays.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Detecting and understanding race conditions is crucial for writing reliable and thread-safe code. The most effective way to reliably reproduce a race condition is to synchronize all competing threads at a single execution point and release them simultaneously, as demonstrated with the semaphore approach.&lt;/p&gt;

&lt;p&gt;By structuring your tests to create contention at the right moment, you can gain insight into how concurrent operations interact and identify potential synchronization issues.&lt;/p&gt;

&lt;p&gt;If you are developing a multithreaded application, incorporating controlled race condition testing into your workflow can help prevent subtle concurrency bugs before they reach production.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>programming</category>
    </item>
    <item>
      <title>A consolidated guide to Well-Architected Frameworks</title>
      <dc:creator>thecodewrapper</dc:creator>
      <pubDate>Fri, 09 Aug 2024 00:08:15 +0000</pubDate>
      <link>https://dev.to/thecodewrapper/a-consolidated-guide-to-well-architected-frameworks-1399</link>
      <guid>https://dev.to/thecodewrapper/a-consolidated-guide-to-well-architected-frameworks-1399</guid>
      <description>&lt;p&gt;In this post, we will delve into the core principles and guidance of the Well-Architected Frameworks (WAF) of Azure, Amazon Web Services (AWS), and Google Cloud Platform (GCP). We’ll highlight the pillars that form the foundation of these frameworks: Cost Optimization, Operational Excellence, Performance Efficiency, Reliability, Security, and Sustainability. Each pillar provides a lens through which architects can evaluate their current setups and identify areas for enhancement. Together they will form a consolidated guide to Well-Architected frameworks.&lt;/p&gt;

&lt;p&gt;Moreover, as cloud strategies often span multiple cloud platforms, we’ll introduce steps and considerations for implementing these best practices within any cloud environment.&lt;/p&gt;

&lt;p&gt;This comprehensive approach will empower you to create a holistic, well-architected cloud infrastructure that leverages the strengths of Azure, AWS, or GCP.&lt;/p&gt;

&lt;p&gt;The following guide is meant to serve as an overview of various principles and approaches from the aforementioned WAFs, providing an understanding of how to build and maintain efficient, secure, and sustainable cloud infrastructures across different platforms.&lt;/p&gt;

&lt;h1&gt;
  
  
  Reliability
&lt;/h1&gt;

&lt;p&gt;The Reliability pillar in cloud architecture focuses on ensuring that workloads can recover from failures, meet customer demands, and remain operational over time. The goal is to build a resilient infrastructure that can handle disruptions gracefully, maintain consistent performance, and recover quickly from outages or issues. This pillar is crucial for providing a positive user experience and maintaining business continuity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design for Business Requirements&lt;/strong&gt;: Gather business requirements with a focus on the intended utility of the workload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design for Resilience&lt;/strong&gt;: The workload must continue to operate with full or reduced functionality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design for Recovery&lt;/strong&gt;: The workload must be able to anticipate and recover from most failures, of all magnitudes, with minimal disruption to the user experience and business objectives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design for Operations&lt;/strong&gt;: Shift left in operations to anticipate failure conditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep it Simple&lt;/strong&gt;: Avoid overengineering the architecture design, application code, and operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design for Failure
&lt;/h3&gt;

&lt;p&gt;To design for failure, adopt a distributed architecture by distributing workloads across multiple regions and availability zones to avoid a single point of failure. Ensure graceful degradation so that if part of the system fails, it does so without affecting overall functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement Backup and Restore Procedures
&lt;/h3&gt;

&lt;p&gt;Implementing backup and restore procedures involves scheduling regular backups for critical data. Regularly test the restore process to ensure data integrity and availability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Utilize Redundancy and Replication
&lt;/h3&gt;

&lt;p&gt;To utilize redundancy and replication, use redundant components like databases and servers to ensure system reliability. Implement data replication across different geographical locations to protect against data loss.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring and Alerting
&lt;/h3&gt;

&lt;p&gt;For effective monitoring and alerting, use tools for real-time monitoring to gain insights into system performance and health. Set up alerts and notifications for critical metrics and events to enable a prompt response to issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement Scalability and Elasticity
&lt;/h3&gt;

&lt;p&gt;To implement scalability and elasticity, utilize auto-scaling features to adjust resources based on demand. Employ load balancing to distribute incoming traffic across multiple servers, ensuring no single server is overwhelmed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ensure Disaster Recovery
&lt;/h3&gt;

&lt;p&gt;To ensure disaster recovery, develop and maintain a disaster recovery plan that includes RTO (Recovery Time Objective), RPO (Recovery Point Objective), and RLO (Recovery Level Objective) targets. Conduct DR drills regularly to ensure readiness and efficiency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automated and Manual Recovery Procedures
&lt;/h3&gt;

&lt;p&gt;For effective recovery procedures, implement automated healing mechanisms that detect and remediate failures automatically. Maintain detailed runbooks for manual intervention when automated recovery is insufficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security and Compliance
&lt;/h3&gt;

&lt;p&gt;To maintain security and compliance, implement strict access controls to protect infrastructure from unauthorized changes that can lead to failures. Ensure compliance monitoring to adhere to relevant regulations and standards, avoiding disruptions due to non-compliance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;p&gt;Adopting best practices involves decoupling components to reduce the impact of a failure in one component on the overall system. Use managed services offered by cloud providers to offload the operational burden and gain built-in reliability features. Keep systems up to date with regular updates and patching to avoid vulnerabilities and improve stability. Regularly conduct capacity planning to ensure the system can handle future growth. Implement failover mechanisms to switch to backup systems automatically during failures.&lt;/p&gt;

&lt;h1&gt;
  
  
  Security
&lt;/h1&gt;

&lt;p&gt;The Security pillar in cloud architecture emphasizes protecting data, systems, and assets through rigorous security controls. This includes ensuring confidentiality, integrity, and availability of data, implementing strong identity management, detecting and responding to threats, and maintaining compliance with regulatory requirements. Security is foundational to maintaining trust and protecting organizational assets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plan your security readiness&lt;/strong&gt;: Strive to adopt and implement security practices in architectural design decisions and operations with minimal friction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design to protect confidentiality&lt;/strong&gt;: Prevent exposure to privacy, regulatory, application, and proprietary information through access restrictions and obfuscation techniques.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design to protect integrity&lt;/strong&gt;: Prevent corruption of design, implementation, operations, and data to avoid disruptions that can stop the system from delivering its intended utility or cause it to operate outside the prescribed limits. The system should provide information assurance throughout the workload lifecycle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design to protect availability&lt;/strong&gt;: Prevent or minimize system and workload downtime and degradation in the event of a security incident by using strong security controls. You must maintain data integrity during the incident and after the system recovers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sustain and evolve your security posture&lt;/strong&gt;: Incorporate continuous improvement and apply vigilance to stay ahead of attackers who are continuously evolving their attack strategies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement Identity and Access Management (IAM)
&lt;/h3&gt;

&lt;p&gt;To effectively implement Identity and Access Management (IAM), apply the principle of least privilege by granting only the permissions necessary for users to perform their tasks. Enforce multi-factor authentication (MFA) for an additional layer of security. Conduct regular audits of permissions and access policies to maintain security integrity. Make use of techniques like Just-In-Time (JITA), Just-Enough (JEA) access.&lt;/p&gt;

&lt;h3&gt;
  
  
  Protect Data at Rest and in Transit
&lt;/h3&gt;

&lt;p&gt;To protect data at rest and in transit, use encryption to safeguard sensitive information. Implement robust key management practices and utilize managed key services. Data classification is essential to apply appropriate security controls based on the sensitivity of the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detect and Respond to Threats
&lt;/h3&gt;

&lt;p&gt;To detect and respond to threats, implement continuous monitoring to identify suspicious activities and potential threats. Develop and regularly update an incident response plan to ensure preparedness. Utilize Security Information and Event Management (SIEM) tools to aggregate and analyze security data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement Network Security Controls
&lt;/h3&gt;

&lt;p&gt;For effective network security controls, practice network segmentation to isolate critical resources and limit the impact of potential attacks. Use firewalls and security groups to manage inbound and outbound traffic. Employ Virtual Private Networks (VPNs) for secure connections to cloud resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maintain Compliance and Governance
&lt;/h3&gt;

&lt;p&gt;To maintain compliance and governance, implement security controls based on relevant compliance frameworks such as GDPR and HIPAA. Regularly audit and report on security practices to demonstrate compliance. Develop and enforce comprehensive policy management across the organization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;p&gt;Adopting best practices includes integrating security by design into the architecture’s design phase. Ensure regular patching and updates to keep systems and applications secure with the latest fixes. Provide ongoing security training and awareness programs for employees. Use automation to enforce security policies, monitor for threats, and respond to incidents. Engage in third-party audits to assess your security posture and identify potential vulnerabilities.&lt;/p&gt;

&lt;h1&gt;
  
  
  Cost optimization
&lt;/h1&gt;

&lt;p&gt;The Cost Optimization pillar focuses on controlling and minimizing costs while maximizing the value delivered by cloud investments. Effective cost management involves understanding and managing where money is being spent, selecting the right services for the right workloads, and continuously optimizing usage to align with business needs and budget constraints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Develop cost-management discipline&lt;/strong&gt;: Build a team culture that has awareness of budget, expenses, reporting, and cost tracking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design with a cost-efficiency mindset&lt;/strong&gt;: Spend only on what you need to achieve the highest return on your investments. Develop a “spend-it-like-it’s-yours” mentality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design for usage optimization&lt;/strong&gt;: Maximize the use of resources and operations. Apply them to the negotiated functional and nonfunctional requirements of the solution.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design for rate optimization&lt;/strong&gt;: Increase efficiency without redesigning, renegotiating, or sacrificing functional or nonfunctional requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitor and optimize over time&lt;/strong&gt;: Continuously right-size investment as your workload evolves with the ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement Cost Transparency and Monitoring
&lt;/h3&gt;

&lt;p&gt;To implement cost transparency and monitoring, you should tag resources to allocate costs to different projects, departments, or business units. Additionally, using cost management tools will help you monitor and visualize spending patterns. Setting up budgets and alerts is essential to track spending and avoid unexpected costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manage Resource Utilization
&lt;/h3&gt;

&lt;p&gt;Effective management of resource utilization involves regularly reviewing and managing your resource inventory to identify unused or underutilized resources. Using automated scheduling to start and stop resources based on usage patterns can greatly enhance efficiency. Moreover, archiving or deleting obsolete data and resources helps in reducing storage costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Right-Size Resources
&lt;/h3&gt;

&lt;p&gt;To ensure resources are right-sized, you should continuously monitor resource performance to avoid over-provisioning or underutilization. Implementing autoscaling can help adjust resource capacity based on demand, and selecting the most cost-effective instance types that meet performance requirements is crucial.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Appropriate Pricing Models
&lt;/h3&gt;

&lt;p&gt;Choosing appropriate pricing models can significantly cut costs. Committing to reserved instances or savings plans for predictable workloads is beneficial. For non-critical workloads that can tolerate interruptions, utilizing spot instances or preemptible VMs is ideal. Additionally, taking advantage of discount programs offered by cloud providers can lead to further savings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automate Cost Management
&lt;/h3&gt;

&lt;p&gt;Automate cost management by using automation tools to enforce cost management policies and optimize resource usage. Implementing policy enforcement can automatically terminate unused resources or downsize over-provisioned ones. Utilizing automated analysis tools can help identify cost-saving opportunities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;p&gt;To optimize storage costs, use appropriate storage classes and lifecycle policies. Leverage managed services to reduce operational overhead and infrastructure management costs. Conducting regular audits of resource usage and costs can uncover optimization opportunities. Enhancing training and awareness among teams on cost management best practices and the financial impacts of their decisions is also important. Finally, performing cost-benefit analyses for new services or architectural changes ensures they provide value for money.&lt;/p&gt;

&lt;h1&gt;
  
  
  Performance Efficiency
&lt;/h1&gt;

&lt;p&gt;The Performance Efficiency pillar focuses on using IT and computing resources efficiently to meet system requirements, and maintaining that efficiency as demand changes and technologies evolve. This involves selecting the right resource types and sizes, monitoring performance, and making informed choices to balance cost and performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Negotiate realistic performance targets&lt;/strong&gt;: The intended user experience is defined, and there’s a strategy to develop a benchmark and measure targets against the pre-established business requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design to meet capacity requirements&lt;/strong&gt;: Provide enough supply to address anticipated demand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Achieve and sustain performance&lt;/strong&gt;: Protect against performance degradation while the system is in use and as it evolves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Improve efficiency through optimization&lt;/strong&gt;: Improve system efficiency within the defined performance targets to increase workload value.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimize Resource Selection
&lt;/h3&gt;

&lt;p&gt;To optimize resource selection, practice right sizing by choosing appropriate resource types and sizes based on workload requirements. Leveraging managed services allows you to benefit from the expertise of cloud providers. Adopting modern architectures, such as serverless and microservices, is beneficial where appropriate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continuous Performance Monitoring
&lt;/h3&gt;

&lt;p&gt;Engage in continuous performance monitoring by tracking key performance metrics to ensure resources perform as expected. Implement logging and analysis to understand system performance and identify bottlenecks. Monitoring user experience metrics is crucial to ensure end-user satisfaction.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implement Elasticity
&lt;/h3&gt;

&lt;p&gt;To effectively implement elasticity, utilize auto-scaling to dynamically adjust resource capacity based on demand. Employ load balancing to distribute traffic efficiently across resources, preventing any single resource from being overwhelmed. Using buffering through queues and buffers helps manage load spikes and ensures smooth processing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continuous Optimization
&lt;/h3&gt;

&lt;p&gt;Engage in continuous optimization by regularly benchmarking your system to understand its performance under various conditions. Refine and improve resource allocation continuously based on performance data. Implement caching strategies to reduce latency and improve response times.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encourage Experimentation and Innovation
&lt;/h3&gt;

&lt;p&gt;Encourage experimentation and innovation by using A/B testing to compare different configurations and identify the best-performing setup. Prototype new architectures and services to discover the most efficient solutions. Stay updated with innovation in new technologies and approaches to continuously improve performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;p&gt;Adopting best practices means staying adaptable and ready to adapt to change by implementing new technologies and methodologies that can enhance performance. Continuously evaluate the cost-performance balance to ensure optimal resource utilization. Automation of performance monitoring and optimization processes ensures consistent and efficient application. Designing systems with a user-centric design ensures that performance improvements directly impact user satisfaction. Establish feedback loops to continually learn from performance data and make iterative improvements.&lt;/p&gt;

&lt;h1&gt;
  
  
  Operational Excellence
&lt;/h1&gt;

&lt;p&gt;The Operational Excellence pillar focuses on running and monitoring systems to deliver business value and continually improving processes and procedures. This includes designing and managing workloads that can adapt to changes, recovering quickly from failures, and providing insights into operations for better decision-making. Ensuring operational excellence involves automating tasks, managing configurations, monitoring operations, and improving processes through iteration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Embrace DevOps culture&lt;/strong&gt;: Empower development and operations teams to continuously improve their system design and processes by working together with a mindset of collaboration, shared responsibility, and ownership.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Establish development standards&lt;/strong&gt;: Optimize productivity by standardizing development practices, enforcing quality gates, and tracking progress and success through systematic change management.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Evolve operations with observability&lt;/strong&gt;: Gain visibility into the system, derive insight, and make data-driven decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploy with confidence&lt;/strong&gt;: Reach the desired state of deployment with predictability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automate for efficiency&lt;/strong&gt;: Replace repetitive manual tasks with software automation that completes them quicker, with greater consistency and accuracy, and reduces risks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adopt safe deployment practices&lt;/strong&gt;: Implement guardrails in the deployment process to minimize the effect of errors or unexpected conditions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gain Operational Insights
&lt;/h3&gt;

&lt;p&gt;To gain operational insights, implement comprehensive monitoring to gather data on system health and performance. Enable logging to capture detailed information about system operations and issues. Define key metrics and set up alerts for critical thresholds to detect and respond to issues quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automate Operational Tasks
&lt;/h3&gt;

&lt;p&gt;Automating operational tasks involves using Infrastructure as Code (IaC) to automate infrastructure provisioning and management. Implement automated testing to ensure systems are working as expected after changes, and use deployment automation to streamline and standardize application deployments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manage Configurations
&lt;/h3&gt;

&lt;p&gt;Effective configuration management requires using Configuration as Code to ensure consistency and enable version control. Centralize management to simplify updates and ensure compliance, and use secure methods for secret management to handle sensitive configuration data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Efficient Change Management
&lt;/h3&gt;

&lt;p&gt;For efficient change management, track changes to understand their impact on system operations. Use controlled rollouts such as phased rollouts and canary deployments to minimize the impact of changes. Establish rollback procedures to quickly revert changes if issues are detected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Continuous Improvement
&lt;/h3&gt;

&lt;p&gt;To foster continuous improvement, establish feedback loops to gather insights from operations and users. Conduct blameless analysis and debrief post-release and/or post-release reviews to identify root causes and preventive measures. Continuously make iterative improvements to enhance operational efficiency and reliability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;p&gt;Adopting best practices involves maintaining comprehensive documentation of operational procedures and best practices. Regularly train staff and encourage knowledge sharing to maintain high operational standards. Regularly test the resilience and recovery procedures to ensure they are effective. Establish performance benchmarks and review them regularly to ensure systems meet expected standards. Foster a culture of collaboration and communication among teams to enhance operational efficiency.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sustainability
&lt;/h1&gt;

&lt;p&gt;The Sustainability pillar focuses on minimizing the environmental impact of cloud operations. This involves reducing energy consumption, optimizing resource utilization, leveraging renewable energy sources, and designing applications and infrastructure that support sustainability goals. Achieving environmental sustainability requires a comprehensive approach that encompasses both technical and operational strategies to reduce the carbon footprint and promote efficient use of resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Energy Efficiency&lt;/strong&gt;: Reducing the energy consumption of cloud resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource Optimization&lt;/strong&gt;: Utilizing cloud resources efficiently to minimize waste.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sustainable Infrastructure&lt;/strong&gt;: Leveraging infrastructure that supports sustainability goals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Renewable Energy&lt;/strong&gt;: Using renewable energy sources to power cloud operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design for Sustainability&lt;/strong&gt;: Incorporating sustainability into the design of applications and services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Improve Energy Efficiency
&lt;/h3&gt;

&lt;p&gt;To improve energy efficiency, use energy-efficient hardware and leverage cloud providers’ efforts to enhance data center efficiency. Schedule workloads through workload scheduling to run during off-peak hours, reducing energy usage. Additionally, maximize server utilization to ensure efficient use of energy resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimize Resource Utilization
&lt;/h3&gt;

&lt;p&gt;For optimizing resource utilization, choose appropriate resource types and sizes through right-sizing to avoid over-provisioning. Implement auto-scaling to dynamically adjust resources based on demand. Regularly review and deprovision unused or underutilized resources to reduce waste.&lt;/p&gt;

&lt;h3&gt;
  
  
  Leverage Sustainable Infrastructure
&lt;/h3&gt;

&lt;p&gt;To leverage sustainable infrastructure, use cloud providers that operate green data centers with certifications like LEED. Select data center locations through geographical selection based on their environmental impact and energy sources. Adopt cloud providers’ sustainable practices, such as using water-efficient cooling systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Utilize Renewable Energy
&lt;/h3&gt;

&lt;p&gt;For utilizing renewable energy, support cloud providers that purchase renewable energy credits (RECs) to offset their carbon footprint. Choose providers that directly use renewable energy sources for their operations. Partner with cloud providers committed to achieving carbon-neutral goals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design for Sustainability
&lt;/h3&gt;

&lt;p&gt;To design for sustainability, engage in sustainable software development by designing applications to be energy-efficient and minimize resource consumption. Use serverless architectures to optimize resource usage and reduce idle capacity. Regularly review and continuously improve applications and infrastructure to enhance sustainability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices
&lt;/h3&gt;

&lt;p&gt;Adopt best practices by using monitoring tools to monitor energy consumption and identify opportunities for improvement. Educate teams about the importance of sustainability and best practices. Define and track sustainability metrics to measure progress and impact. Collaborate with cloud providers and industry partners through collaboration and partnerships to share best practices and drive sustainability initiatives. Continuously explore new technologies and approaches for innovation to further reduce environmental impact.&lt;/p&gt;

&lt;h1&gt;
  
  
  Resources
&lt;/h1&gt;

&lt;p&gt;Here are the links to dive deeper into the well-architected frameworks of each cloud provider:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Azure Well-Architected Framework&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/reliability/" rel="noopener noreferrer"&gt;Reliability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/security/" rel="noopener noreferrer"&gt;Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/cost-optimization/" rel="noopener noreferrer"&gt;Cost Optimization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/performance-efficiency/" rel="noopener noreferrer"&gt;Performance Efficiency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/operational-excellence/" rel="noopener noreferrer"&gt;Operational Excellence&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/sustainability/" rel="noopener noreferrer"&gt;Sustainability&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AWS Well-Architected Framework&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/welcome.html" rel="noopener noreferrer"&gt;Reliability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/security-pillar/welcome.html" rel="noopener noreferrer"&gt;Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/cost-optimization-pillar/welcome.html" rel="noopener noreferrer"&gt;Cost Optimization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/performance-efficiency-pillar/welcome.html" rel="noopener noreferrer"&gt;Performance Efficiency&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/operational-excellence-pillar/welcome.html" rel="noopener noreferrer"&gt;Operational Excellence&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/sustainability-pillar/sustainability-pillar.html" rel="noopener noreferrer"&gt;Sustainability&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Google Cloud Architecture Framework&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/architecture/framework/reliability" rel="noopener noreferrer"&gt;Reliability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/architecture/framework/security" rel="noopener noreferrer"&gt;Security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/architecture/framework/cost-optimization" rel="noopener noreferrer"&gt;Cost Optimization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/architecture/framework/performance-optimization" rel="noopener noreferrer"&gt;Performance Optimization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/architecture/framework/operational-excellence" rel="noopener noreferrer"&gt;Operational Excellence&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/architecture/framework/system-design/sustainability" rel="noopener noreferrer"&gt;Sustainability&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The well-architected frameworks from Azure, AWS, and Google Cloud are like your go-to guides for building great cloud systems. They cover everything you need to make sure your setups are reliable, secure, cost-effective, and ready for anything.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt;: Keep your systems solid and able to recover from issues with strategies like backups and monitoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt;: Protect your data and apps by setting up strong security measures and keeping an eye out for threats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Optimization&lt;/strong&gt;: Save money by picking the right resources, keeping track of spending, and optimizing where you can.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Efficiency&lt;/strong&gt;: Ensure your systems are responsive by choosing the right resources and scaling as needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational Excellence&lt;/strong&gt;: Run your systems smoothly by automating tasks, managing changes well, and always looking for improvements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sustainability&lt;/strong&gt;: Be environmentally friendly by using resources efficiently and opting for renewable energy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following these pillars, you can build cloud systems that are secure, efficient, cost-effective, and sustainable — basically, everything you need to keep your operations running smoothly and responsibly.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>cloud</category>
      <category>architecture</category>
      <category>cloudcomputing</category>
    </item>
    <item>
      <title>Unlock Your Potential: Building Custom Tools as a Developer</title>
      <dc:creator>thecodewrapper</dc:creator>
      <pubDate>Mon, 30 Oct 2023 15:32:02 +0000</pubDate>
      <link>https://dev.to/thecodewrapper/unlock-your-potential-building-custom-tools-as-a-developer-3a0n</link>
      <guid>https://dev.to/thecodewrapper/unlock-your-potential-building-custom-tools-as-a-developer-3a0n</guid>
      <description>&lt;p&gt;As a passionate advocate of custom tooling in the world of software development, I firmly believe that if you can build tools to make your work easier, you should go ahead and spend the time to do it. The dividends of investing in custom tools are immense and far-reaching. In this article, we’ll delve into the importance of building custom tools as a developer and explore how they can transform your work into a more efficient and rewarding endeavor.&lt;/p&gt;

&lt;p&gt;Custom tools empower you as a developer to streamline your workflows, increase your productivity, and conquer complex tasks with ease, all while ensuring long-term benefits that are well worth the initial investment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are custom tools?
&lt;/h2&gt;

&lt;p&gt;Firstly, custom tools are basically our sidekicks in the coding world. They’re tailor-made applications, scripts, or software solutions that we build to make our lives easier as developers. It’s like having a personal assistant that just gets you. We’re talking automation scripts that handle the boring, repetitive tasks, user-friendly graphical interfaces that turn complex problems into a walk in the park, plugins and extensions that supercharge our IDEs, data transformation tools that make sense of the mountains of data we deal with, and custom testing and debugging utilities that fit our projects like a glove.&lt;/p&gt;

&lt;p&gt;They aren’t solely focused on making business processes more efficient; instead, their primary purpose is to optimize the work you do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;They are a reflection of the developer’s ingenuity, problem-solving skills, and deep understanding of the project’s unique demands.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Efficiency and Productivity
&lt;/h2&gt;

&lt;p&gt;Now, why are these tools so darn great? They take our productivity to new heights, help us tackle even the most complex tasks with ease, and guarantee we’re set up for success in the long run. Plus, they’re just plain fun to build and use.&lt;/p&gt;

&lt;p&gt;Imagine you’re a developer in a project and part of your daily routine is to convert huge datasets from one format to another, which takes a few minutes each time you do it, and also, you do it, say, 10 times a day. With a custom tool, that because way easier! You’ve got your handy dandy data conversion tool that’s been fine-tuned to handle the specific formats you work with. It automates the whole process and even runs in the background while you focus on the good stuff. The result? You’ve just upped your efficiency and productivity big time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Addressing unique challenges
&lt;/h2&gt;

&lt;p&gt;Every project and development team faces unique challenges. These challenges may range from complex data processing to intricate debugging scenarios. Custom tools allow developers to design solutions specifically attuned to the demands of their projects. By creating tools tailored to handle specific issues or scenarios, developers can eliminate bottlenecks and roadblocks more effectively.&lt;/p&gt;

&lt;p&gt;For example, in your work, you may encounter complex challenges, such as managing inventory updates from multiple suppliers with varying data formats. By creating a custom data integration tool that understands each supplier’s format and automates the data import process, you can efficiently address this unique challenge, reducing errors and improving data accuracy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streamlining workflows
&lt;/h2&gt;

&lt;p&gt;And let’s not forget about streamlining workflows. Custom tools are like the missing puzzle piece that fits perfectly into our development stacks. They automate those tedious steps, help us maintain top-notch coding standards, and provide intuitive interfaces that let us focus on the creative aspects of our work.&lt;/p&gt;

&lt;p&gt;In a DevOps context, for example, a custom tool could be integrated into the CI/CD (Continuous Integration/Continuous Deployment) pipeline to automate the build, packaging, and deployment of applications and libraries. This ensures that code changes are automatically tested, built, and deployed, saving us heaps of time and maintaining consistency across development, testing, and production environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Skills Development
&lt;/h2&gt;

&lt;p&gt;Building custom tools isn’t just about solving immediate problems; it’s also about growing as professionals. We get to hone our problem-solving skills, dive deep into programming languages and frameworks, and let our creative flags fly. It’s a great way to flex our coding muscles and learn something new.&lt;/p&gt;

&lt;p&gt;Don’t be afraid to explore languages and frameworks you’ve never before worked with, to bring your tool to life. Diving deep into programming languages and frameworks, especially those we’re not super familiar with, is like opening a treasure trove of possibilities. It’s like, “Woah, I didn’t know this language could do that!” It’s a fantastic way to expand our toolkit and see which languages vibe with our way of solving problems. Plus, it gives us first-hand insights into the strengths and weaknesses of different languages. We might find that some languages are like our best friends for certain types of solutions, while others are more like that acquaintance we only call up for specific tasks.&lt;/p&gt;

&lt;p&gt;It’s a super fun way to flex our coding muscles and learn something new, all while creating our personalized toolbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost savings
&lt;/h2&gt;

&lt;p&gt;And, last, but not least, let’s talk about the financial side of things. While building custom tools does require a time investment upfront, the long-term benefits are well worth it. We can kiss those annoying licensing fees goodbye, cut down on the time we spend on manual tasks, and avoid those costly errors that can come from using inadequate tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, custom tools are like our secret weapons in the coding world. They empower us to work smarter, tackle any challenge that comes our way, and grow as professionals. Plus, they save us money in the long run. So, let’s embrace the power of custom tools and take our development game to new heights of efficiency and excellence!&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>software</category>
      <category>developers</category>
      <category>programming</category>
    </item>
    <item>
      <title>Simple Event-Sourcing with EF Core and SQL Server</title>
      <dc:creator>thecodewrapper</dc:creator>
      <pubDate>Tue, 17 Oct 2023 13:25:24 +0000</pubDate>
      <link>https://dev.to/thecodewrapper/simple-event-sourcing-with-ef-core-and-sql-server-34hd</link>
      <guid>https://dev.to/thecodewrapper/simple-event-sourcing-with-ef-core-and-sql-server-34hd</guid>
      <description>&lt;p&gt;In this article, I delve deeper into the architectural explorations that began with my initial post on ‘&lt;a href="https://thecodewrapper.com/dev/implementing-clean-architecture-in-aspnetcore-6/" rel="noopener noreferrer"&gt;Implementing a Clean Architecture in ASP.NET Core 6&lt;/a&gt;‘. The spotlight this time is on implementing a rudimentary event-sourcing mechanism using Entity Framework Core and SQL Server.&lt;/p&gt;

&lt;p&gt;While this article navigates the intricacies of my chosen implementation, it presumes you already have a foundational grasp of event sourcing, its association with event-driven architectures, and its synergy with patterns like Domain-Driven Design (DDD) and Command Query Responsibility Segregation (CQRS). If you’re looking to enhance your understanding of these underlying concepts or to garner insights on the merits and challenges of event-sourcing, I’ve assembled a list of resources at the conclusion of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Abstractions
&lt;/h2&gt;

&lt;p&gt;In event-sourcing, we often deal with large streams of events and need ways to efficiently recreate system state. Our three core interfaces handle these challenges: &lt;em&gt;IEventStore&lt;/em&gt; manages event persistence and retrieval, &lt;em&gt;IEventStoreSnapshotProvider&lt;/em&gt; uses snapshots for faster state reconstruction, and &lt;em&gt;IRetroactiveEventsService&lt;/em&gt; deals with changes to past events.&lt;/p&gt;

&lt;h3&gt;
  
  
  Event persistence
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;IEventStore&lt;/em&gt; is central to our event-sourced system, serving as the interface for storing and retrieving domain events tied to aggregates. It manages the persistence of domain events for an aggregate and provides capabilities to fetch domain events based on a version range.&lt;/p&gt;

&lt;h3&gt;
  
  
  Snapshots
&lt;/h3&gt;

&lt;p&gt;To optimize performance in scenarios with large event logs, &lt;em&gt;IEventStoreSnapshotProvider&lt;/em&gt; offers snapshot functionality. Snapshots capture an aggregate’s state at a specific point in time, reducing the need to replay every event for aggregate rehydration. This interface deals with both the retrieval of an aggregate’s state from a snapshot and the persistence of an aggregate’s current state as a snapshot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Retroactive events
&lt;/h3&gt;

&lt;p&gt;For instances where we need to make adjustments to historical event streams, &lt;em&gt;IRetroactiveEventsService&lt;/em&gt; comes into play. Whether for corrections, recalculations, or backdated operations, this interface facilitates the introduction of retroactive events into an existing event stream and returns the modified stream.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Event Store
&lt;/h2&gt;

&lt;p&gt;In this section, we’ll delve into the actual implementation details of our event store using Entity Framework. Our primary class, &lt;em&gt;EFEventStore&lt;/em&gt;, provides concrete implementations for the essential operations defined in our &lt;em&gt;IEventStore&lt;/em&gt; interface. With Entity Framework as its backbone, this class essentially persists and retrieves domain events to and from our data store.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context and DbSet
&lt;/h3&gt;

&lt;p&gt;At its core, &lt;em&gt;EFEventStore&lt;/em&gt; relies on the &lt;em&gt;EventStoreDbContext&lt;/em&gt; context. The context contains only one DbSet, the &lt;em&gt;EventEntity&lt;/em&gt; DbSet, which represents the table where the domain events are stored.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private readonly EventStoreDbContext _context;
private readonly DbSet&amp;lt;EventEntity&amp;gt; _events;

public EFEventStore(EventStoreDbContext context) {
    _context = context;
    _events = _context.Set&amp;lt;EventEntity&amp;gt;();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Loading Domain Events
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;LoadAsync&lt;/em&gt; method fetches a sequence of domain events for a specified aggregate, within a given version range. Each fetched event is then transformed from its storage format into its actual domain event type using the &lt;em&gt;DomainEventHelper.ConstructDomainEvent&lt;/em&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task&amp;lt;IReadOnlyCollection&amp;lt;IDomainEvent&amp;lt;TAggregateId&amp;gt;&amp;gt;&amp;gt; LoadAsync&amp;lt;TAggregateId&amp;gt;(TAggregateId aggregateRootId, string aggregateName, int fromVersion, int toVersion)
{
    Guard.Against.Negative(fromVersion, nameof(fromVersion));
    Guard.Against.Negative(toVersion, nameof(toVersion));
    if (fromVersion &amp;gt; toVersion)
    {
        throw new ArgumentException($"{nameof(fromVersion)} cannot be greater than {nameof(toVersion)}");
    }

    IQueryable&amp;lt;EventEntity&amp;gt; events = _events.Where(e =&amp;gt; e.AggregateId == aggregateRootId.ToString() &amp;amp;&amp;amp; e.AggregateName == aggregateName &amp;amp;&amp;amp; e.Version &amp;gt;= fromVersion &amp;amp;&amp;amp; e.Version &amp;lt;= toVersion).OrderBy(de =&amp;gt; de.Version);
    var domainEvents = new List&amp;lt;IDomainEvent&amp;lt;TAggregateId&amp;gt;&amp;gt;();

    //get events
    foreach (var @event in events)
    {
        var domainEvent = DomainEventHelper.ConstructDomainEvent&amp;lt;TAggregateId&amp;gt;(@event.Data, @event.AssemblyTypeName);
        domainEvents.Add(domainEvent);
    }

    return domainEvents.AsReadOnly();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Saving Domain Events
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;EFEventStore&lt;/em&gt; provides two &lt;em&gt;SaveAsync&lt;/em&gt; methods: one for saving a collection of domain events and another for saving a single event. Both methods create corresponding &lt;em&gt;EventEntity&lt;/em&gt; instances for each domain event and add them to the &lt;em&gt;EventEntity&lt;/em&gt; DbSet. The changes are then persisted to the database using the &lt;em&gt;SaveChangesAsync&lt;/em&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task SaveAsync&amp;lt;TAggregateId&amp;gt;(string aggregateName, int expectedVersion, IEnumerable&amp;lt;IDomainEvent&amp;lt;TAggregateId&amp;gt;&amp;gt; domainEvents)
{
    foreach (var domainEvent in domainEvents)
    {
        EventEntity eventEntity = ConstructEventEntity(domainEvent, expectedVersion, aggregateName);
        await _events.AddAsync(eventEntity);
    }
    await _context.SaveChangesAsync();
}

public async Task SaveAsync&amp;lt;TAggregateId&amp;gt;(string aggregateName, int expectedVersion, IDomainEvent&amp;lt;TAggregateId&amp;gt; domainEvent)
{

    EventEntity eventEntity = ConstructEventEntity(domainEvent, expectedVersion, aggregateName);
    await _events.AddAsync(eventEntity);
    await _context.SaveChangesAsync();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Event Entity Construction
&lt;/h3&gt;

&lt;p&gt;To assist in saving domain events, the private method &lt;em&gt;ConstructEventEntity&lt;/em&gt; is used. This method constructs an &lt;em&gt;EventEntity&lt;/em&gt; instance from a given domain event. The method also checks for potential concurrency issues by verifying the version of the incoming domain event against the expected version. The domain event is serialized into a string format for storage, and additional metadata, like the event’s type, is captured for future deserialization.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private EventEntity ConstructEventEntity&amp;lt;TAggregateId&amp;gt;(IDomainEvent&amp;lt;TAggregateId&amp;gt; domainEvent, int expectedVersion, string aggregateName)
{
    if (domainEvent.AggregateVersion &amp;gt; expectedVersion)
        throw new EventStoreException($"Concurrency issue detected when saving events. Event found with version {domainEvent.AggregateVersion} which is larger than maximum expected version {expectedVersion}");

    Type domainEventType = domainEvent.GetType();
    return new EventEntity()
    {
        Id = domainEvent.EventId,
        AggregateId = domainEvent.AggregateId.ToString(),
        AggregateName = aggregateName,
        Name = domainEventType.Name,
        AssemblyTypeName = domainEventType.AssemblyQualifiedName,
        Data = JsonConvert.SerializeObject(domainEvent),
        Version = domainEvent.AggregateVersion,
        CreatedAt = DateTime.UtcNow //duplicate. save or lose??
    };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Snapshot Provider
&lt;/h2&gt;

&lt;p&gt;As our system evolves and the number of events increases, snapshots become essential for performance optimization. The &lt;em&gt;EFEventStoreSnapshotProvider&lt;/em&gt; class gives us the capability to save the current state of an aggregate, allowing us to avoid reapplying a large number of events. Through this service, we can persist these snapshots and retrieve them when needed. The full implementation is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using CH.EventStore.Abstractions;
using CH.Domain.Abstractions;
using CH.EventStore.EntityFramework.Entities;
using System.Threading.Tasks;
using System;
using System.Linq;

namespace CH.EventStore.EntityFramework
{
    /// &amp;lt;summary&amp;gt;
    /// Implementation of &amp;lt;see cref="IEventStoreSnapshotProvider"/&amp;gt; using Entity Framework
    /// &amp;lt;/summary&amp;gt;
    internal class EFEventStoreSnapshotProvider : IEventStoreSnapshotProvider
    {
        private readonly EventStoreDbContext _context;
        private readonly DbSet&amp;lt;AggregateSnapshotEntity&amp;gt; _snapshots;
        private readonly JsonSerializerSettings _jsonSerializerSettings = new JsonSerializerSettings { ContractResolver = new PrivateSetterContractResolver() };

        public EFEventStoreSnapshotProvider(EventStoreDbContext context)
        {
            _context = context;
            _snapshots = context.Set&amp;lt;AggregateSnapshotEntity&amp;gt;();
        }

        public async Task&amp;lt;T&amp;gt; GetAggregateFromSnapshotAsync&amp;lt;T, TAggregateId&amp;gt;(TAggregateId aggregateId, string aggregateName) where T : class, IAggregateRoot&amp;lt;TAggregateId&amp;gt;
        {
            AggregateSnapshotEntity entity = await GetLatestSnapshotAsync(aggregateId, aggregateName);
            if (entity == null)
                return default;
            T aggregate = JsonConvert.DeserializeObject&amp;lt;T&amp;gt;(entity.Data, _jsonSerializerSettings);
            aggregate.ClearUncommittedEvents(); //to remove constructor creation event
            return aggregate;
        }

        public async Task SaveSnapshotAsync&amp;lt;T, TId&amp;gt;(T aggregate, Guid lastEventId) where T : class, IAggregateRoot&amp;lt;TId&amp;gt;
        {
            AggregateSnapshotEntity newSnapshot = new AggregateSnapshotEntity()
            {
                Data = JsonConvert.SerializeObject(aggregate),
                AggregateId = aggregate.Id.ToString(),
                LastAggregateVersion = aggregate.Version,
                AggregateName = typeof(T).Name,
                LastEventId = lastEventId
            };
            _snapshots.Add(newSnapshot);
            await _context.SaveChangesAsync();
        }

        private Task&amp;lt;AggregateSnapshotEntity&amp;gt; GetLatestSnapshotAsync&amp;lt;TAggregateId&amp;gt;(TAggregateId aggregateId, string aggregateName)
        {
            return _snapshots.Where(snap =&amp;gt; snap.AggregateId == aggregateId.ToString() &amp;amp;&amp;amp; snap.AggregateName == aggregateName).OrderByDescending(a =&amp;gt; a.LastAggregateVersion).FirstOrDefaultAsync();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Retroactive Events service
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;EFRetroactiveEventsService&lt;/em&gt; is responsible for the critical task of applying retroactive events to an event stream. Using this service, we can correct or enhance an existing event stream by interleaving or replacing events that may have occurred out-of-order, were rejected, or later identified as incorrect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Branch points
&lt;/h3&gt;

&lt;p&gt;In event-sourced systems, once an event is stored, it is immutable; it cannot be changed. However, there are times when you might discover that an event was incorrect, or perhaps you wish to insert additional events in a specific position within the event stream. That’s where &lt;strong&gt;branch points&lt;/strong&gt; come into play.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;BranchPointEntity&lt;/em&gt; class is a representation of such moments in the event stream where alterations are needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class BranchPointEntity : DataEntityBase&amp;lt;int&amp;gt;
{
    /// &amp;lt;summary&amp;gt;
    /// Branch point indicative name
    /// &amp;lt;/summary&amp;gt;
    public string Name { get; set; }

    /// &amp;lt;summary&amp;gt;
    /// FK for the Event Entity
    /// &amp;lt;/summary&amp;gt;
    public Guid EventId { get; set; }

    /// &amp;lt;summary&amp;gt;
    /// The type of the branch point
    /// &amp;lt;/summary&amp;gt;
    public BranchPointTypeEnum Type { get; set; }

    /// &amp;lt;summary&amp;gt;
    /// Navigation property of the event
    /// &amp;lt;/summary&amp;gt;
    public virtual EventEntity Event { get; set; }

    public virtual ICollection&amp;lt;RetroactiveEventEntity&amp;gt; RetroactiveEvents { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The properties of this &lt;em&gt;BranchPointEntity&lt;/em&gt; are described below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt;: This name helps in identifying or describing the purpose or reason for the branch point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EventId&lt;/strong&gt;: This is the foreign key that relates the branch point to a specific event in the event stream. It pinpoints the location in the event stream where retroactive changes need to be made.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type (BranchPointTypeEnum)&lt;/strong&gt;: This enumeration value represents the nature of the branch point. There can be several types, such as:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OutOfOrder&lt;/strong&gt;: Indicates that events should be interleaved within the existing stream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incorrect&lt;/strong&gt;: Indicates that events should be added as a replacement of the linked event defined in the branch point.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rejected&lt;/strong&gt;: Indicates that the event is not supposed to be part of the event stream.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Event (Navigation Property)&lt;/strong&gt;: This is the linked event in the event stream associated with the branch point.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;RetroactiveEvents&lt;/strong&gt;: It’s a collection of events that should be applied when this branch point is hit during the processing of the event stream. These are the events that will replace, precede, or follow the original event based on the branch point’s type.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Applying retroactive events to stream
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;ApplyRetroactiveEventsToStream&lt;/em&gt; method is a central piece in our implementation of retroactive events within the event-sourced system. It takes in an event stream and returns a potentially modified stream by applying the branch points, making it an essential part of the retroactivity logic. Lets describe what happens in here in more detail:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fetch Branch Points&lt;/strong&gt;: We start by querying all the branch points that match events in the provided event stream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sequential Stream Processing&lt;/strong&gt;: For each event in the input stream (processed in ascending order of their aggregate version):

&lt;ul&gt;
&lt;li&gt;We check if there’s an associated branch point for the event.&lt;/li&gt;
&lt;li&gt;If a branch point exists, based on the type of the branch point (&lt;em&gt;OutOfOrder&lt;/em&gt;, &lt;em&gt;Incorrect&lt;/em&gt;, &lt;em&gt;Rejected&lt;/em&gt;), we apply the necessary retroactive events from the branch point into the stream. This could mean adding new events, replacing existing ones, or even skipping certain events.&lt;/li&gt;
&lt;li&gt;If no branch point exists for the event, the event is added to the new stream unchanged.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Return New Stream&lt;/strong&gt;: After processing all events, the method returns a potentially modified event stream.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is important in event-sourcing implementations, especially in the context of retroactive changes, to remain faithful to two principles: &lt;strong&gt;immutability&lt;/strong&gt; and maintaining the &lt;strong&gt;order of events&lt;/strong&gt;. As you’ll notice in the implementation, the original event stream remains unchanged and a potential new stream with retroactive changes is created and returned. Also, it’s important to process the input stream in the correct order (i.e., ascending order of aggregate version) to ensure the retroactive events are injected at the right places.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;We’ve been discussing different parts of this architecture. Let’s take a moment to explore the synthesis of these elements, captured in our custom event-sourcing repository, &lt;em&gt;ESRepository&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;ESRepository&lt;/em&gt; class serves as a tailored repository for event-sourced entities. It’s built upon the concept of aggregate roots, represented by the &lt;em&gt;IAggregateRoot&lt;/em&gt; abstraction. For those who’ve been following along, you’ll recall that we touched on the &lt;em&gt;IAggregateRoot&lt;/em&gt; abstraction in a &lt;a href="https://thecodewrapper.com/dev/ef-core-effectively-decouple-the-data-and-domain-model/" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;. It forms the cornerstone of Domain-Driven Design (DDD) and the event sourcing patterns, encapsulating the business logic and ensuring changes are made in a consistent and valid way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;internal class ESRepository&amp;lt;T, TId&amp;gt; : IESRepository&amp;lt;T, TId&amp;gt; where T : class, IAggregateRoot&amp;lt;TId&amp;gt;
{
    private readonly ILogger&amp;lt;ESRepository&amp;lt;T, TId&amp;gt;&amp;gt; _logger;
    private readonly IEventStore _eventStore;
    private readonly IEventStoreSnapshotProvider _snapshotService;
    private readonly IRetroactiveEventsService _retroEventsService;
    private const int SNAPSHOT_FREQUENCY = 50;

    public ESRepository(ILogger&amp;lt;ESRepository&amp;lt;T, TId&amp;gt;&amp;gt; logger, IEventStore eventStore, IEventStoreSnapshotProvider snapshotProvider, IRetroactiveEventsService retroEventsService)
    {
        _logger = logger;
        _eventStore = eventStore;
        _snapshotService = snapshotProvider;
        _retroEventsService = retroEventsService;
    }

    public async Task&amp;lt;T&amp;gt; GetByIdAsync(TId id)
    {
        try
        {
            T aggregate = CreateEmptyAggregate();
            string aggregateName = typeof(T).Name;
            int fromVersion = 0;

            T snapshotAggregate = await _snapshotService.GetAggregateFromSnapshotAsync&amp;lt;T, TId&amp;gt;(id, aggregateName);
            if (snapshotAggregate != default)
            {
                aggregate = snapshotAggregate;
                fromVersion = snapshotAggregate.Version + 1;
            }

            var eventsForAggregate = await _eventStore.LoadAsync&amp;lt;TId&amp;gt;(id, aggregateName, fromVersion, int.MaxValue);

            //if no events are found, return default
            if (!eventsForAggregate.Any() &amp;amp;&amp;amp; snapshotAggregate == default) //if no events or snapshot is found
                throw new EventStoreAggregateNotFoundException($"Aggregate {aggregateName} with id {id} not found");

            eventsForAggregate = _retroEventsService.ApplyRetroactiveEventsToStream&amp;lt;T, TId&amp;gt;(eventsForAggregate);

            foreach (var @event in eventsForAggregate)
            {
                aggregate.ApplyEvent(@event, @event.AggregateVersion);
            }

            if (aggregate.IsDeleted)
                throw new EventStoreAggregateNotFoundException($"Aggregate {aggregateName} with id {id} not found");

            return aggregate;
        }
        catch (Exception ex)
        {
            _logger.LogError($"Error occured while retrieving from event source repository. Exception: {ex}");
            throw;
        }
    }

    public async Task SaveAsync(T aggregate)
    {
        try
        {
            IAggregateRoot&amp;lt;TId&amp;gt; aggregatePersistence = aggregate;
            string aggregateName = typeof(T).Name;
            var uncommittedEvents = aggregate.GetUncommittedEvents();

            if (!uncommittedEvents.Any())
            {
                return;
            }

            Guid lastEventId = uncommittedEvents.Last().EventId;

            await _eventStore.SaveAsync(aggregateName, aggregate.Version, uncommittedEvents);
            if (ShouldSnapshot(aggregate.Version, uncommittedEvents.Count()))
            {
                await _snapshotService.SaveSnapshotAsync&amp;lt;T, TId&amp;gt;(aggregate, lastEventId);
            }
            aggregatePersistence.ClearUncommittedEvents();
        }
        catch (Exception ex)
        {
            _logger.LogError($"Error occured while saving aggregate. Exception: {ex}");
            throw;
        }
    }

    private T CreateEmptyAggregate()
    {
        return (T)typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, Array.Empty&amp;lt;Type&amp;gt;(), Array.Empty&amp;lt;ParameterModifier&amp;gt;()).Invoke(Array.Empty&amp;lt;object&amp;gt;());
    }

    private bool ShouldSnapshot(int aggregateVersion, int numberOfeventsToCommit)
    {
        //Every N events we save a snapshot
        return ((aggregateVersion &amp;gt;= SNAPSHOT_FREQUENCY) &amp;amp;&amp;amp;
            (
                (numberOfeventsToCommit &amp;gt;= SNAPSHOT_FREQUENCY) ||
                (aggregateVersion % SNAPSHOT_FREQUENCY &amp;lt; numberOfeventsToCommit) ||
                (aggregateVersion % SNAPSHOT_FREQUENCY == 0)
            )
        );
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This repository provides a seamless interface to interact with event-sourced entities by orchestrating operations with the Event Store, Snapshot Service, and the Retroactive Events Service. It facilitates fetching the aggregate’s current state by replaying events and possibly leveraging snapshots for efficiency. Additionally, it handles the persistence of new domain events and decides when to take snapshots based on the number of events occurred previously.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Event Sourcing is a profound pattern that can offer immense value to applications, especially when dealing with complex domain logic. In this exploration, we’ve touched upon the various intricacies and components that contribute to a holistic event-sourcing architecture.&lt;/p&gt;

&lt;p&gt;To facilitate ease of integration and modularity, the abstractions have been packaged into one &lt;a href="https://www.nuget.org/packages/CH.EventStore.Abstractions/" rel="noopener noreferrer"&gt;Nuget package&lt;/a&gt;, while the Entity Framework Core implementations are encapsulated in another &lt;a href="https://www.nuget.org/packages/CH.EventStore.EntityFramework" rel="noopener noreferrer"&gt;separate package&lt;/a&gt;. For those eager to delve deeper into the inner workings, or perhaps even contribute, the source code is available in the &lt;a href="https://github.com/thecodewrapper/CH.Framework" rel="noopener noreferrer"&gt;repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Follow some of the links below if you are interested to know more about event-sourcing, its pros and cons, and when or why you should (or shouldn’t) use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Martin Fowler on Event Sourcing&lt;/strong&gt;: A comprehensive introduction to the event sourcing pattern from one of the industry’s leading figures. &lt;a href="https://martinfowler.com/eaaDev/EventSourcing.html" rel="noopener noreferrer"&gt;Read here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event Sourcing Pattern – Microsoft Azure&lt;/strong&gt;: Detailed insights into the pattern, especially in the context of cloud solutions. &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/event-sou" rel="noopener noreferrer"&gt;Check it out&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Greg Young – The art of destroying software&lt;/strong&gt;: A must-watch talk for anyone diving into event sourcing, where Greg Young elaborates on the importance of being able to rebuild your system at any point in time. &lt;a href="https://www.youtube.com/watch?v=-UKEPd2ipEk" rel="noopener noreferrer"&gt;Watch the talk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event Store&lt;/strong&gt;: An open-source, functional database that exclusively uses the event sourcing pattern. It provides a good reference for those looking to see event sourcing in action. &lt;a href="https://eventstore.com" rel="noopener noreferrer"&gt;Visit their website&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Domain-Driven Design&lt;/strong&gt;: While not exclusively about event sourcing, Eric Evans’ book on Domain-Driven Design touches on patterns and practices that align closely with event sourcing. A recommended read for anyone delving into domain modeling. &lt;a href="https://www.amazon.com/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215" rel="noopener noreferrer"&gt;Find the book here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event Sourcing Made Simple&lt;/strong&gt;: A blog post that breaks down the complexities of event sourcing into digestible bits. &lt;a href="https://kickstarter.engineering/event-sourcing-made-simple-4a2625113224" rel="noopener noreferrer"&gt;Read the post&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Event Sourcing Pattern – Microservices.io&lt;/strong&gt;: A concise breakdown of the event sourcing pattern in the context of microservices architecture. The page offers clear diagrams and explanations of key concepts. &lt;a href="https://microservices.io/patterns/data/event-sourcing.html" rel="noopener noreferrer"&gt;Dive in here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>eventsourcing</category>
      <category>entityframework</category>
      <category>sqlserver</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>The Value of Unit Testing in Software Development</title>
      <dc:creator>thecodewrapper</dc:creator>
      <pubDate>Tue, 17 Oct 2023 13:06:38 +0000</pubDate>
      <link>https://dev.to/thecodewrapper/the-value-of-unit-testing-in-software-development-4hmo</link>
      <guid>https://dev.to/thecodewrapper/the-value-of-unit-testing-in-software-development-4hmo</guid>
      <description>&lt;p&gt;In this post we’ll explore the importance and need for unit testing in software development. While other articles focus on the practical aspects of testing, such as how to write effective test cases and how to use testing tools, we’ll approach this topic from a different perspective. We will delve into the philosophical significance of testing and share personal experiences that highlight the importance of this crucial aspect of software development.&lt;/p&gt;

&lt;p&gt;A practical guide on how to properly write unit tests and the various tools to use is something I’ll maybe cover in another article. This is about the value of unit testing as such, and why you should make it a habit to have them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Always think ahead
&lt;/h2&gt;

&lt;p&gt;Personally, I’m not a very laid back person. I like to think things through. What I always say, and this applies to all things in my life, not just coding, is “always have a contingency plan”. If you think of all (or as many possible) outcomes a given situation may come to, then you can plan for them, and that is precious. One of my favorite quotes is “If you fail to plan, you plan to fail”. I like to sleep peacefully at nights, so I try to plan for as much as possible.&lt;/p&gt;

&lt;p&gt;A few years ago, I worked for a company (or two) which did not write any unit tests. We, the developers just focused on writing our code, doing some manual testing and just pushing it through. Essentially the only testing that was happening was by a QA team, or by the developers themselves. This meant that tasks finished quickly, but resulted in bugs in production or if we were lucky, bugs were picked during QA testing and returned back to us for fixing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time saver
&lt;/h2&gt;

&lt;p&gt;Now, let me clear something up. Unit testing does not replace manual developer testing. Both are equally important and time should be allocated for both of them. What unit tests do, is to diminish guess-work. This means that if something goes wrong, or a bug comes up and something isn’t working as it should, unit tests help with swiftly identifying the part of the code that has the issue. Also, by testing segments of your code as a unit, helps with identifying errors early in the development process, which saves valuable time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid code complexity
&lt;/h2&gt;

&lt;p&gt;Another byproduct of not having unit tests, is bad code. Whenever a bug is fixed in the absence of unit tests, the code tends to become more complex. When you write unit tests, you are forced to think about how your code should behave in isolation. This means you have to break down your code into smaller, more manageable units, which can help avoid the creation of large, complex code blocks.&lt;/p&gt;

&lt;p&gt;Unit tests can also help ensure that code changes do not create new complexity. Whenever a developer makes a change to the code, they can run the unit tests to ensure that the changes have not introduced any new bugs or unintended consequences. This helps ensure that the code remains simple and straightforward.&lt;/p&gt;

&lt;h2&gt;
  
  
  A matter of life and death
&lt;/h2&gt;

&lt;p&gt;Unit testing is important for several reasons, including identifying and fixing bugs early in the development cycle, improving code quality, and increasing confidence in the software being developed. However, perhaps one of the most compelling reasons for the importance of unit testing is the potential consequences of not doing it.&lt;/p&gt;

&lt;p&gt;The 1992 incident in London is a prime example of what can happen when software is not properly tested. In that year, London’s brand new computer-aided ambulance-dispatch system suffered a catastrophic failure due to a software error. The system was supposed to help ambulance crews respond to emergencies more efficiently, but instead, it caused chaos and confusion.&lt;/p&gt;

&lt;p&gt;A software error caused the dispatch system to fail, resulting in long delays and increased response times for emergency calls. In some cases, patients died while waiting for an ambulance to arrive. The failure of the system had far-reaching consequences and led to public outrage and an investigation into the cause of the failure.&lt;/p&gt;

&lt;p&gt;The cause of the failure was eventually traced back to a software error that had not been caught during testing. The failure of the ambulance dispatch system was a tragic reminder of the importance of proper testing and quality assurance in software development.&lt;/p&gt;

&lt;p&gt;Had the software been properly tested, the error would have been identified and fixed before the system went live. The tragic consequences of the London ambulance dispatch failure highlight the critical importance of unit testing and quality assurance in software development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.wired.com/2009/10/1026london-ambulance-computer-meltdown/" rel="noopener noreferrer"&gt;This article&lt;/a&gt; provides more details about the incident and its aftermath, including the investigation into the cause of the failure and the lessons learned from the tragedy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Unit testing is an essential practice in software development that should not be overlooked. It can help identify and fix bugs early in the development cycle, improve code quality, and increase confidence in the software being developed. Moreover, it can help prevent catastrophic failures like the one that occurred with London’s ambulance dispatch system in 1992.&lt;/p&gt;

&lt;p&gt;By prioritizing unit testing, developers can ensure that the software they create is reliable, secure, and ethical, and that it meets the needs of users and society as a whole. In short, the value of unit testing in software development cannot be understated, and it is essential for creating software that is of the highest quality.&lt;/p&gt;

</description>
      <category>unittest</category>
      <category>developer</category>
      <category>softwaredevelopment</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>How to: Send Microsoft Teams self-message using Graph SDK</title>
      <dc:creator>thecodewrapper</dc:creator>
      <pubDate>Sun, 19 Mar 2023 15:43:32 +0000</pubDate>
      <link>https://dev.to/thecodewrapper/how-to-send-microsoft-teams-self-message-using-graph-sdk-1ojm</link>
      <guid>https://dev.to/thecodewrapper/how-to-send-microsoft-teams-self-message-using-graph-sdk-1ojm</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Microsoft Teams has become an indispensable tool for businesses and organizations to communicate and collaborate. Sometimes, you might want to send a message to yourself as a reminder or to keep track of important information.In this tutorial, I will demonstrate how to send Microsoft Teams self-message using Graph SDK by creating a simple console application in .NET 6.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;For this you need the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Microsoft Teams account with a valid subscription.&lt;/li&gt;
&lt;li&gt;Microsoft Graph SDK installed in your development environment.&lt;/li&gt;
&lt;li&gt;A registered app in Azure Active Directory with required permissions.&lt;/li&gt;
&lt;li&gt;.NET Core installed on your development environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note: When registering your application in Azure AD, you need to add a redirect URI with the following properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform&lt;/strong&gt;: &lt;em&gt;Public client/native (Mobile &amp;amp; Desktop)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Value&lt;/strong&gt;: &lt;em&gt;&lt;a href="http://localhost" rel="noopener noreferrer"&gt;http://localhost&lt;/a&gt;&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is needed when authenticating using an interactive browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create a new .NET Core console application
&lt;/h2&gt;

&lt;p&gt;Open a command prompt or terminal, and create a new .NET Core console application using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet new console --name TeamsSelfMessage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to the newly created project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd TeamsSelfMessage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Add necessary packages
&lt;/h2&gt;

&lt;p&gt;To send a self-message on Microsoft Teams using the Graph SDK, first, install the necessary packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet add package Microsoft.Graph
dotnet add package Microsoft.Graph.Core
dotnet add package Microsoft.Identity.Client
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package Azure.Identity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Configure the app settings
&lt;/h2&gt;

&lt;p&gt;Add a new file to the project named appsettings.json. Make sure you configure the &lt;em&gt;Build Action&lt;/em&gt; to &lt;em&gt;Content&lt;/em&gt; and the &lt;em&gt;Copy to Output Directory&lt;/em&gt; to &lt;em&gt;Copy Always&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "appId": "YOUR_APP_ID",
  "tenantId": "YOUR_TENANT_ID",
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;em&gt;YOUR_APP_ID&lt;/em&gt; and &lt;em&gt;YOUR_TENANT_ID&lt;/em&gt; with your actual app registration details from Azure Active Directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Set up the Graph SDK and authenticate
&lt;/h2&gt;

&lt;p&gt;Open the &lt;em&gt;Program.cs&lt;/em&gt; file and add the following using statements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.IO;
using System.Threading.Tasks;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Graph;
using Microsoft.Graph.Models;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following field to hold the scopes for the authentication of the &lt;em&gt;GraphServiceClient&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private static string[] _graphScopes = new[] { "User.Read", "ChatMessage.Send", "Chat.ReadWrite" };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also add the following field which denotes the chat with yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private const string SELF_CHAT_ID = "48:notes";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following method to authenticate and initialize the Graph SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private static async Task&amp;lt;GraphServiceClient&amp;gt; GetGraphClient(IConfiguration configuration)
{
    var interactiveBrowserCredentialOptions = new InteractiveBrowserCredentialOptions
    {
        ClientId = configuration["appId"],
        TenantId = configuration["tenantId"]
    };
    var tokenCredential = new InteractiveBrowserCredential(interactiveBrowserCredentialOptions);

    var graphClient = new GraphServiceClient(tokenCredential, _graphScopes);
    _ = await graphClient.Me.GetAsync(); //trigger login
    return graphClient;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Implement the self-message functionality
&lt;/h2&gt;

&lt;p&gt;With the Graph SDK set up, we can now implement the functionality to send a self-message in Microsoft Teams. Add the following method to send a message to yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private static async Task&amp;lt;ChatMessage&amp;gt; SendMessageAsync(GraphServiceClient graphClient, string messageContent)
{
    var message = new ChatMessage
    {
        Body = new ItemBody
        {
            ContentType = BodyType.Html,
            Content = messageContent
        }
    };

    return await graphClient.Me.Chats[SELF_CHAT_ID].Messages.PostAsync(message);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Call the self-message methods from Main
&lt;/h2&gt;

&lt;p&gt;Modify the &lt;em&gt;Main&lt;/em&gt; method to call the self-message method as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public static async Task Main(string[] args)
{
    var configuration = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .Build();

    var graphClient = await GetGraphClient(configuration);

    string messageContent = "This is a message to myself!";
    ChatMessage sentMessage = await SendMessageAsync(graphClient, messageContent);
    Console.WriteLine($"Message sent with ID: {sentMessage.Id}");

    Console.WriteLine("Press any key to exit...");
    Console.ReadKey();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can run the console application using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The console application will prompt you for authentication and send a message to yourself on Microsoft Teams. You can check your Teams client to see the message.&lt;/p&gt;

&lt;p&gt;That’s it! You have successfully implemented self-messaging functionality in a .NET Core console application using the Microsoft Graph SDK.&lt;/p&gt;

&lt;p&gt;For completeness, here is the full code for the &lt;em&gt;Program.cs&lt;/em&gt; below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.IO;
using System.Threading.Tasks;
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Graph;
using Microsoft.Graph.Models;

namespace TeamsSelfMessage
{
    class Program
    {
        private static string[] _graphScopes = new[] { "User.Read", "ChatMessage.Send", "Chat.ReadWrite" };
        private const string SELF_CHAT_ID = "48:notes";

        public static async Task Main(string[] args)
        {
            var configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .Build();

            var graphClient = await GetGraphClient(configuration);

            string messageContent = "This is a message to myself!";
            ChatMessage sentMessage = await SendMessageAsync(graphClient, messageContent);
            Console.WriteLine($"Message sent with ID: {sentMessage.Id}");

            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }

        private static async Task&amp;lt;GraphServiceClient&amp;gt; GetGraphClient(IConfiguration configuration)
        {
            var interactiveBrowserCredentialOptions = new InteractiveBrowserCredentialOptions
            {
                ClientId = configuration["appId"],
                TenantId = configuration["tenantId"]
            };
            var tokenCredential = new InteractiveBrowserCredential(interactiveBrowserCredentialOptions);

            var graphClient = new GraphServiceClient(tokenCredential, _graphScopes);
            _ = await graphClient.Me.GetAsync(); //trigger login
            return graphClient;
        }

        private static async Task&amp;lt;ChatMessage&amp;gt; SendMessageAsync(GraphServiceClient graphClient, string messageContent)
        {
            var message = new ChatMessage
            {
                Body = new ItemBody
                {
                    ContentType = BodyType.Html,
                    Content = messageContent
                }
            };

            return await graphClient.Me.Chats[SELF_CHAT_ID].Messages.PostAsync(message);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also find the GitHub repository with the complete solution &lt;a href="https://github.com/thecodewrapper/CH.TeamsSelfMessage" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>microsoftgraph</category>
      <category>teams</category>
      <category>graphsdk</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Designing the domain model to support multiple persistence methods</title>
      <dc:creator>thecodewrapper</dc:creator>
      <pubDate>Sun, 19 Mar 2023 15:30:27 +0000</pubDate>
      <link>https://dev.to/thecodewrapper/designing-the-domain-model-to-support-multiple-persistence-methods-14oe</link>
      <guid>https://dev.to/thecodewrapper/designing-the-domain-model-to-support-multiple-persistence-methods-14oe</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This is a continuation of my earlier post &lt;a href="https://thecodewrapper.com/dev/tcw-clean-achitecture/" rel="noopener noreferrer"&gt;Implementing a Clean Architecture in ASP.NET Core 6&lt;/a&gt;, so if you haven’t read that yet, I suggest you do as it will provide a little more context, although it is not necessary to follow along.&lt;/p&gt;

&lt;p&gt;As mentioned in the previous post, I wanted to allow the use of a traditional state-oriented CRUD model for persistence, and at the same time have the ability to switch to an event sourcing method, or even use both side-by-side. So in this post, we’ll take a look at how to design our domain model so we can use both event-sourcing and a state persistence model together, or interchangeably.&lt;/p&gt;

&lt;p&gt;One key assumption for this is that we’ll be using EF Core with SQL Server for our relational persistence method.&lt;/p&gt;

&lt;h2&gt;
  
  
  The goal
&lt;/h2&gt;

&lt;p&gt;The main goal was to have domain entities within the project be able to be persisted either using a relational model or using event sourcing, or even both together without changing the underlying implementation of the domain entity itself. In that, I wanted to keep the structure of the core aligned with Domain-Driven Design practices.&lt;/p&gt;

&lt;p&gt;To make this work, it naturally had to be based on some form of event mechanism. In this case, domain events seems quite a good fit. To learn what domain events are and how they fit into the whole domain-driven design approach, take a look at some of the resources at the end of this post.&lt;/p&gt;

&lt;p&gt;Through my research prior to starting this journey, I found that there are quite a few ways domain events can be implemented. I’ve come to realize that there isn’t a “better” way to do this, the “best” way is what fits in the overall architecture and the purpose for which you want to utilize domain events.&lt;/p&gt;

&lt;p&gt;For this to be a viable solution, I’ve set out to achieve the following low-level goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encapsulate the handling logic of the event within the aggregate itself&lt;/li&gt;
&lt;li&gt;Handle the events in such a way that if event sourcing is used, the playback of events would allow the aggregate object to build itself into its latest state&lt;/li&gt;
&lt;li&gt;Have compile-time safety such that the event handlers are guaranteed to be implemented&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why domain events?
&lt;/h2&gt;

&lt;p&gt;I took the approach of having functions in the aggregates which contain some logic to only raise an event, and have that event handler internally, within the aggregate, perform any changes to the aggregate. This is because it serves the purpose of event-sourcing and non-event sourcing implementations at the same time; it caters to the reconstruction of the aggregate’s state from the event stream.&lt;/p&gt;

&lt;p&gt;Once again, I’ve looked into what is out there in terms of domain event implementations. Below some articles for domain events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://udidahan.com/2009/06/14/domain-events-salvation/" rel="noopener noreferrer"&gt;https://udidahan.com/2009/06/14/domain-events-salvation/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.innoq.com/de/blog/domain-events-versus-event-sourcing/" rel="noopener noreferrer"&gt;https://www.innoq.com/de/blog/domain-events-versus-event-sourcing/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lostechies.com/jimmybogard/2014/05/13/a-better-domain-events-pattern/" rel="noopener noreferrer"&gt;https://lostechies.com/jimmybogard/2014/05/13/a-better-domain-events-pattern/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://devblogs.microsoft.com/cesardelatorre/domain-events-vs-integration-events-in-domain-driven-design-and-microservices-architectures/" rel="noopener noreferrer"&gt;https://devblogs.microsoft.com/cesardelatorre/domain-events-vs-integration-events-in-domain-driven-design-and-microservices-architectures/&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Initial approach
&lt;/h2&gt;

&lt;p&gt;My initial approach to handle and apply domain events was to have internal functions called Apply inside the aggregates. Each function would take the event object its supposed to handle and do whatever work they are supposed to do.&lt;/p&gt;

&lt;p&gt;Whenever we would like an event to fire, we’ll call the &lt;em&gt;RaiseEvent&lt;/em&gt; function which is inherited by the &lt;em&gt;AggregateRootBase&lt;/em&gt; class. The &lt;em&gt;RaiseEvent&lt;/em&gt; function does some versioning related work and subsequently calls into the &lt;em&gt;ApplyEvent&lt;/em&gt; function (part of the &lt;em&gt;IAggregateRoot&lt;/em&gt; interface).&lt;/p&gt;

&lt;p&gt;In turn, this dynamically calls the appropriate Apply function on the aggregate, according to the type of the event passed in. The implementation of &lt;em&gt;RaiseEvent&lt;/em&gt; and the initial implementation of the &lt;em&gt;ApplyEvent&lt;/em&gt; function in the &lt;em&gt;AggregateRootBase&lt;/em&gt; looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected void RaiseEvent&amp;lt;TEvent&amp;gt;(TEvent @event)
            where TEvent : DomainEventBase&amp;lt;TId&amp;gt; {
            int version = Version + 1;
            IDomainEvent&amp;lt;TId&amp;gt; eventWithAggregate = @event.WithAggregate(
                Equals(Id, default(TId)) ? @event.AggregateId : Id,
                version);

            ((IAggregateRoot&amp;lt;TId&amp;gt;)this).ApplyEvent(eventWithAggregate, version);
            _uncommittedEvents.Add(eventWithAggregate);
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void IAggregateRoot&amp;lt;TId&amp;gt;.ApplyEvent(IDomainEvent&amp;lt;TId&amp;gt; @event, int version) {
            if (!_uncommittedEvents.Any(x =&amp;gt; Equals(x.EventId, @event.EventId))) {
                ((dynamic)this).Apply((dynamic)@event);
                Version = version;
            }
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below an example of how this would be raised by the aggregate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Order(string trackingNumber) {
            RaiseEvent(new OrderCreatedEvent(trackingNumber));
        }

internal void Apply(OrderCreatedEvent @event) {
            Id = @event.AggregateId;
            TrackingNumber = @event.TrackingNumber;
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, this has the benefit of good encapsulation by keeping the handling of the event internal, not exposed to the outside world. However, it does not give the benefit of compile-time checking in terms of actually having the implementation to handle the event in place. This would show up at runtime if I accidentally forgot to write the Apply function for a given event. We could argue that this is not so much of an issue, however being the picky person that I am, I couldn’t allow this 🙂&lt;/p&gt;

&lt;p&gt;Therefore, i tried a different approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Second attempt
&lt;/h2&gt;

&lt;p&gt;With this second approach, aggregates implement the IDomainEventHandler generic interface, which has just one function to handle the event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;internal interface IDomainEventHandler&amp;lt;T&amp;gt; where T: IDomainEvent
    {
        internal void Apply(T @event);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;AggregateRootBase&lt;/em&gt; contains the code which applies the actual event. This uses reflection to invoke the aggregate’s &lt;em&gt;Apply&lt;/em&gt; function for the particular domain event type, implemented via the interface internally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void IAggregateRoot&amp;lt;TId&amp;gt;.ApplyEvent(IDomainEvent&amp;lt;TId&amp;gt; @event, int version) {
            if (!_uncommittedEvents.Any(x =&amp;gt; Equals(x.EventId, @event.EventId))) {
                var handlerType = GetType()
                    .GetInterfaces()
                    .Single(i =&amp;gt; i.GetGenericTypeDefinition() == typeof(IDomainEventHandler&amp;lt;&amp;gt;) &amp;amp;&amp;amp; i.GetGenericArguments()[0] == @event.GetType());
                var handlerMethod = handlerType.GetTypeInfo().GetDeclaredMethod(nameof(IDomainEventHandler&amp;lt;IDomainEvent&amp;gt;.Apply));
                handlerMethod.Invoke(this, new object[] { @event });
            }
            Version = version;
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now, the way this would be handled by the aggregate turns into this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public Order(string trackingNumber) {
            RaiseEvent(new OrderCreatedEvent(trackingNumber));
        }

void IDomainEventHandler&amp;lt;OrderCreatedEvent&amp;gt;.Apply(OrderCreatedEvent @event) {
            Id = @event.AggregateId;
            TrackingNumber = @event.TrackingNumber;
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a couple of reasons I went in this direction.&lt;/p&gt;

&lt;p&gt;First, I wanted to keep the implementation of the handler inside the aggregate itself, and also provide compile-time safety in having to implement the &lt;em&gt;IDomainEventHandler&lt;/em&gt; interface.&lt;/p&gt;

&lt;p&gt;Secondly, I wanted to have the handler implementations access modifier be internal, since these handler implementations are contained within the domain entities, which themselves are visible to all outer layers. I don’t like having the handling of an event be visible to the outside world.&lt;/p&gt;

&lt;p&gt;I would say this approach provides good encapsulation in regards to raising and handling domain events, with the downside of been slower due to reflection, although the performance impact is fairly negligible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;

&lt;p&gt;Having said that, there is an issue with the above approach as well. The issue here is that any exception thrown within the &lt;em&gt;IDomainEventHandler&lt;/em&gt; implementation, actually bubbles up as a &lt;em&gt;TargetInvocationException&lt;/em&gt;, which is an exception thrown when methods are invoked using reflection. This type of exception wraps the original exception in the &lt;em&gt;InnerException&lt;/em&gt; property. Obviously this reduces testability, and is therefore not acceptable.&lt;/p&gt;

&lt;p&gt;To avoid this, I wrapped the invocation around a try/catch block which will re-throw the inner exception. So the final implementation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void IAggregateRoot&amp;lt;TId&amp;gt;.ApplyEvent(IDomainEvent&amp;lt;TId&amp;gt; @event, int version) {
            if (!_uncommittedEvents.Any(x =&amp;gt; Equals(x.EventId, @event.EventId))) {
                try {
                    var handlerType = GetType()
                    .GetInterfaces()
                    .Single(i =&amp;gt; i.GetGenericTypeDefinition() == typeof(IDomainEventHandler&amp;lt;&amp;gt;) &amp;amp;&amp;amp; i.GetGenericArguments()[0] == @event.GetType());
                    var handlerMethod = handlerType.GetTypeInfo().GetDeclaredMethod(nameof(IDomainEventHandler&amp;lt;IDomainEvent&amp;gt;.Apply));
                    handlerMethod.Invoke(this, new object[] { @event });
                }
                catch (TargetInvocationException ex) {
                    throw ex.InnerException;
                }
            }
            Version = version;
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For now, I can make my peace with this try/catch block, at least until any significant performance issues arise. I may look into doing some performance testing to see how that goes in the future.&lt;/p&gt;

&lt;p&gt;Below the entire &lt;em&gt;AggregateRootBase&lt;/em&gt; class, as it stands now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace CH.CleanArchitecture.Core.Domain
{
    public abstract class AggregateRootBase&amp;lt;TId&amp;gt; : IAggregateRoot&amp;lt;TId&amp;gt;
    {
        public const int NewAggregateVersion = -1;

        private readonly ICollection&amp;lt;IDomainEvent&amp;lt;TId&amp;gt;&amp;gt; _uncommittedEvents = new LinkedList&amp;lt;IDomainEvent&amp;lt;TId&amp;gt;&amp;gt;();

        /// &amp;lt;summary&amp;gt;
        /// The aggregate root Id
        /// &amp;lt;/summary&amp;gt;
        public TId Id { get; protected set; }

        /// &amp;lt;summary&amp;gt;
        /// The aggregate root current version
        /// &amp;lt;/summary&amp;gt;
        public int Version { get; private set; } = NewAggregateVersion;

        /// &amp;lt;summary&amp;gt;
        /// Indicates whether this aggregate is logically deleted
        /// &amp;lt;/summary&amp;gt;
        public bool IsDeleted { get; private set; }

        void IAggregateRoot&amp;lt;TId&amp;gt;.ApplyEvent(IDomainEvent&amp;lt;TId&amp;gt; @event, int version) {
            if (!_uncommittedEvents.Any(x =&amp;gt; Equals(x.EventId, @event.EventId))) {
                try {
                    InvokeHandler(@event);
                    Version = version;
                }
                catch (TargetInvocationException ex) {
                    throw ex.InnerException;
                }
            }
            Version = version;
        }

        void IAggregateRoot&amp;lt;TId&amp;gt;.ClearUncommittedEvents() {
            _uncommittedEvents.Clear();
        }

        IEnumerable&amp;lt;IDomainEvent&amp;lt;TId&amp;gt;&amp;gt; IAggregateRoot&amp;lt;TId&amp;gt;.GetUncommittedEvents() {
            return _uncommittedEvents.AsEnumerable();
        }

        protected void MarkAsDeleted() {
            IsDeleted = true;
        }

        protected void RaiseEvent&amp;lt;TEvent&amp;gt;(TEvent @event)
            where TEvent : DomainEventBase&amp;lt;TId&amp;gt; {
            int version = Version + 1;
            IDomainEvent&amp;lt;TId&amp;gt; eventWithAggregate = @event.WithAggregate(
                Equals(Id, default(TId)) ? @event.AggregateId : Id,
                version);

            ((IAggregateRoot&amp;lt;TId&amp;gt;)this).ApplyEvent(eventWithAggregate, version);
            _uncommittedEvents.Add(eventWithAggregate);
        }

        private void InvokeHandler(IDomainEvent&amp;lt;TId&amp;gt; @event) {
            var handlerMethod = GetEventHandlerMethodInfo(@event);
            handlerMethod.Invoke(this, new object[] { @event });
        }

        private MethodInfo GetEventHandlerMethodInfo(IDomainEvent&amp;lt;TId&amp;gt; @event) {
            var handlerType = GetType()
                    .GetInterfaces()
                    .Single(i =&amp;gt; i.GetGenericTypeDefinition() == typeof(IDomainEventHandler&amp;lt;&amp;gt;) &amp;amp;&amp;amp; i.GetGenericArguments()[0] == @event.GetType());
            return handlerType.GetTypeInfo().GetDeclaredMethod(nameof(IDomainEventHandler&amp;lt;IDomainEvent&amp;gt;.Apply));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As always, any feedback on this is much appreciate so please feel free to let me know what you think of this approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;To find out additional information about domain events, what they are, their benefits and how the fit into your design, follow one of the links below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/domain-events-design-implementation" rel="noopener noreferrer"&gt;https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/domain-events-design-implementation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf#page=25" rel="noopener noreferrer"&gt;https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf#page=25&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>dotnet</category>
      <category>efcore</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Why I “hate” optional parameters in C#</title>
      <dc:creator>thecodewrapper</dc:creator>
      <pubDate>Fri, 11 Mar 2022 11:00:07 +0000</pubDate>
      <link>https://dev.to/thecodewrapper/why-i-hate-optional-parameters-in-c-5ai9</link>
      <guid>https://dev.to/thecodewrapper/why-i-hate-optional-parameters-in-c-5ai9</guid>
      <description>&lt;p&gt;Let me start by saying that the title of this post is a little exaggerated in regards to how I feel about optional parameters. “Hate” is a strong word and I certainly don’t mean that optional parameters are a useless feature or that it is a bad practice to use them. It’s just that, I’ve rarely seen them used correctly and in almost all cases I see them being used, they bring more problems than solutions. After many long and painful debugging sessions, the sight of optional parameters in method declarations are accompanied by a despicable code smell the equivalent of a rotten egg, until proven otherwise. Allow me to elaborate and share my experiences.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are they about?
&lt;/h2&gt;

&lt;p&gt;Optional arguments, also known as default parameters (the most misleading AKA if I’ve ever heard one), were introduced in C# 4. Their intention was to provide a convenient and flexible way to omit function arguments for certain parameters whenever they are not required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional does not mean Default
&lt;/h2&gt;

&lt;p&gt;One of the biggest misuses of optional parameters is that of providing a default argument to a function. Although it is mandatory to provide a default value to an optional argument, it doesn’t mean that this feature should be used whenever you want to have some default value in a function. Specifying a default value is simply a requirement to use this feature, it is not the reason of it’s existence.&lt;/p&gt;

&lt;p&gt;In fact, take a close look at the definition of optional arguments in Microsoft’s official C# Guide, and you’ll notice that there is nothing mentioned of using it for the purpose of passing a default value to a function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optional arguments are compile-time constants
&lt;/h3&gt;

&lt;p&gt;There are quite a few issues that may arise when using optional arguments for the purpose of providing a default value to a function.&lt;/p&gt;

&lt;p&gt;Optional arguments are compile-time constants, meaning that the default value is embedded at the caller side, not the callee. This may lead to unexpected results at the caller side. Consider the following segment of code where a function DoSomething is defined in a class called LibA:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class LibA
{
   public string DoSomething(string optional = "FromLibA")
   {
       return optional;
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above class exists in its own separate library. Now, consider the following code where the DoSomething function is consumed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LibA libA = new LibA();
Console.Write(libA.DoSomething());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of the above will obviously be “FromLibA”. But now consider the scenario where a change is made in the LibA class, and the default value of the optional parameter is changed to something else. The above console output would still be “FromLibA”. In order for the change to “take” then both the LibA assembly and the consumer assembly need to be compiled.&lt;/p&gt;

&lt;h3&gt;
  
  
  Difference in polymorphic behavior through interfaces
&lt;/h3&gt;

&lt;p&gt;Furthermore, there may be a difference between the optional values when using them through an interface function and when using them through a concrete implementation, if the default value definition does not match.&lt;/p&gt;

&lt;p&gt;Consider the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface IMyInterface
{
    string DoSomething(string optional = "FromInterface");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class LibA : IMyInterface
{
    public string DoSomething(string optional = "FromLibA")
    {
        return optional;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LibA caller = new LibA();
IMyInterface interfaceCaller = new LibA();

Console.WriteLine(caller.DoSomething());
Console.WriteLine(interfaceCaller.DoSomething());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of the above two statements will be different. In the case of calling the concrete class (caller) the output will be “FromLibA”, and in the latter it will be “FromInterface”. This can become particularly confusing in cases you are using dependency injection through interfaces because it is not clear which default value is being used.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alternatives
&lt;/h3&gt;

&lt;p&gt;If you need to have a default parameter which can be optionally overridden by the caller, there are plenty of alternatives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use function overloading with the parameter in question and have the caller decide if they want to provide a value for that parameter by calling the appropriate overload. If the original function instead of the overloaded one is used, then the value for that parameter can be defined within the function body.&lt;/li&gt;
&lt;li&gt;In case of multiple properties, create a dedicated type to hold those properties and use that in a function overload. This would allow you to easily extend the parameters in the future if needed.&lt;/li&gt;
&lt;li&gt;Abstract away the default value so it is provided by another component or service. This is cleaner and decouples the actual value from the consumer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Same-type parameter ambiguity
&lt;/h2&gt;

&lt;p&gt;To add insult to injury, when you have an interface which specifies a default value for an optional parameter, you are allowed to ignore the parameter on a class that implements that interface. This can become a problem when your class implements multiple interfaces with functions which use the same number and type of parameters. In order to call the appropriate function you would need to call it through the interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  When its OK to use them
&lt;/h2&gt;

&lt;p&gt;To simplify the calling of class constructors, and other creational mechanism functions (i.e. factory) which have a long list of complex arguments, and to avoid having to create multiple convoluted overloads to cover all possible combinations. That’s strictly it. Notice that when I refer to a long list of arguments, I strictly mean that in the context of object creation, and even more so for class constructors.&lt;/p&gt;

&lt;p&gt;If you ever find yourself at a point where you have multiple optional parameters for non-constructors or non-factory object creation functions, then your function is almost certainly doing too much, and you should refactor your code to break things down in smaller pieces (see &lt;a href="https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html" rel="noopener noreferrer"&gt;SRP&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  When you MUST use them
&lt;/h2&gt;

&lt;p&gt;Almost never. There is literally no case (or at least I’ve never had a case in my years of coding) where I absolutely *&lt;em&gt;had *&lt;/em&gt; to use optional arguments. As mentioned above, optional arguments is a convenience mechanism, it is not a critical language feature which you cannot live without.&lt;/p&gt;

&lt;h2&gt;
  
  
  As a rule of thumb
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;When coming across optional parameters in code reviews, always treat them as a code smell, until you’ve taken a closer look at the implementation and ensured that they are prudently used. This is even more important when coming across optional parameters in public functions.&lt;/li&gt;
&lt;li&gt;Always prefer function overloading before optional parameters.&lt;/li&gt;
&lt;li&gt;If an argument alters the flow of execution within a function, it should unequivocally not be optional.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A piece of “harsh” advice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Avoid using same-type optional parameters sequentially. If you see some piece of code which is doing that, refactor immediately.&lt;/li&gt;
&lt;li&gt;Avoid using optional parameters in public functions altogether. Use them only for private/internal functions, and always with caution.&lt;/li&gt;
&lt;li&gt;When specifying values for multiple optional arguments, make it a habit to use named arguments when calling a function.&lt;/li&gt;
&lt;li&gt;Avoid using optional parameters in interface functions.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>csharp</category>
      <category>optionalparameters</category>
      <category>programming</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>EF Core: Effectively decouple the data and domain model</title>
      <dc:creator>thecodewrapper</dc:creator>
      <pubDate>Thu, 10 Feb 2022 10:38:56 +0000</pubDate>
      <link>https://dev.to/thecodewrapper/ef-core-effectively-decouple-the-data-and-domain-model-4h8j</link>
      <guid>https://dev.to/thecodewrapper/ef-core-effectively-decouple-the-data-and-domain-model-4h8j</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This is a continuation of my first post &lt;a href="https://thecodewrapper.com/dev/tcw-clean-achitecture" rel="noopener noreferrer"&gt;Implementing a Clean Architecture in ASP.NET Core 6&lt;/a&gt;, and describes the method I’ve used to achieve clear separation of data models and domain models. In this post, I’ll be expressing my views on the clear separation of persistence related models and business models and look into how we can effectively decouple the data and domain model when using EF Core.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, domain model. Second, data model.
&lt;/h2&gt;

&lt;p&gt;Over the last few EF Core iterations, there was a great deal of effort made to cater to tactical &lt;a href="https://martinfowler.com/bliki/DomainDrivenDesign.html" rel="noopener noreferrer"&gt;Domain-Driven Design&lt;/a&gt; practices when working with data entities (i.e. private field accessors). However, I personally feel that, even though we are now able to “combine” a data model and a domain model into one EF Core entity, the separation is still not entirely clear, and more often than not, you end up having persistence details “leaking” into your business/domain models (i.e. navigation properties). This can quickly lead to high-coupling of your behavioral model with the database domain.&lt;/p&gt;

&lt;p&gt;So my rule is this: Data entities and any persistence-related code should be kept ONLY in the Infrastructure layer and never be allowed to leave! For this reason, domain layer repositories shall never return anything that resembles a data entity. Conceptually, data entities are objects which represent the data in some form of persistence. They have no business – pun intended 🙂 – in the domain layer. Data entities should be anemic POCO objects which are strictly a representation of whatever persistence method you are using. I don’t fancy the idea of having attributes relating to persistence or navigation properties in my domain model.&lt;/p&gt;

&lt;p&gt;Additionally, you may need to persist some entities in your project, which are not conceptually part of your business domain layer. For example, application-wide configurations or tenant-specific parameters are very specific to the application itself and are best suited outside the application-independent business layer. However, from a management point-of-view you would still want to be able to add/remove/edit those entities from whatever presentation/UI medium you are using.&lt;/p&gt;

&lt;p&gt;For both use-cases above, we’ll be using repositories to cover them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best of both worlds
&lt;/h2&gt;

&lt;p&gt;The plan is to have two types of repositories, one that acts upon data entities and another that acts upon domain entities. For that, let first create a basic abstraction which both will depend on.&lt;/p&gt;

&lt;p&gt;To achieve this, I have an &lt;em&gt;IEntityRepository&lt;/em&gt; which operates on classes implementing the generic &lt;em&gt;IEntity&lt;/em&gt; interface. Notice that the &lt;em&gt;IEntityRepository&lt;/em&gt; also implements the &lt;em&gt;IReadRepository&lt;/em&gt; and &lt;em&gt;IWriteRepository&lt;/em&gt; generic interfaces.&lt;/p&gt;

&lt;p&gt;Below the code for all aforementioned interfaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public interface IEntity&amp;lt;TId&amp;gt;
    {
        TId Id { get; }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public interface IReadRepository&amp;lt;T&amp;gt;
    {
        bool Exists(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate);
        Task&amp;lt;bool&amp;gt; ExistsAsync(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate);
        IQueryable&amp;lt;T&amp;gt; GetAll();
        IQueryable&amp;lt;T&amp;gt; GetBy(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate);
        T GetFirst(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate);
        Task&amp;lt;T&amp;gt; GetFirstAsync(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate);
        T GetSingle(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate);
        Task&amp;lt;T&amp;gt; GetSingleAsync(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public interface IWriteRepository&amp;lt;T&amp;gt; : IRepository
    {
        void Add(T entity);
        ValueTask AddAsync(T entity);
        void AddRange(IEnumerable&amp;lt;T&amp;gt; entities);
        Task AddRangeAsync(IEnumerable&amp;lt;T&amp;gt; entities);
        void Delete(T entity);
        void DeleteRange(IEnumerable&amp;lt;T&amp;gt; entities);
        void Update(T entity);
        void UpdateRange(IEnumerable&amp;lt;T&amp;gt; entities);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public interface IRepository
    {
        IUnitOfWork UnitOfWork { get; }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public interface IEntityRepository&amp;lt;T, TId&amp;gt; : 
        IReadRepository&amp;lt;T&amp;gt;, 
        IWriteRepository&amp;lt;T&amp;gt;
        where T : class, IEntity&amp;lt;TId&amp;gt;
    {
        T Find(TId id);
        Task&amp;lt;T&amp;gt; FindAsync(TId id);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The data model repository
&lt;/h2&gt;

&lt;p&gt;So far we have a pretty basic, albeit rich, abstraction for a repository. Moving forward, what we want to do is make the distinction between data entities and domain entities become clear. Firstly, we’ll create a basic implementation of a repository for retrieving data entities from our persistence store. This repository will operate on classes implementing the generic &lt;em&gt;IDataEntity&lt;/em&gt; marker interface.&lt;/p&gt;

&lt;p&gt;The code for the interface and concrete implementation of the data entities repository is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface IDataEntity&amp;lt;TId&amp;gt; : IEntity&amp;lt;TId&amp;gt;
{ }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;internal class DataEntityRepository&amp;lt;T, TId&amp;gt; : IEntityRepository&amp;lt;T, TId&amp;gt; where T : class, IDataEntity&amp;lt;TId&amp;gt;
    {
        private readonly DbSet&amp;lt;T&amp;gt; _entities;
        private readonly ApplicationDbContext _context;
        public IUnitOfWork UnitOfWork =&amp;gt; _context;

        public DataEntityRepository(ApplicationDbContext context)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
            _entities = context.Set&amp;lt;T&amp;gt;();
        }

        public void Add(T entity)
        {
            Guard.Against.Null(entity, nameof(entity));
            _entities.Add(entity);
        }

        public async ValueTask AddAsync(T entity)
        {
            Guard.Against.Null(entity, nameof(entity));
            await _entities.AddAsync(entity);
        }

        public void AddRange(IEnumerable&amp;lt;T&amp;gt; entities)
        {
            _entities.AddRange(entities);
        }

        public Task AddRangeAsync(IEnumerable&amp;lt;T&amp;gt; entities)
        {
            return _entities.AddRangeAsync(entities);
        }

        public void Delete(T entity)
        {
            Guard.Against.Null(entity, nameof(entity));
            _entities.Remove(entity);
        }

        public void DeleteRange(IEnumerable&amp;lt;T&amp;gt; entities)
        {
            Guard.Against.Null(entities, nameof(entities));
            _entities.RemoveRange(entities);
        }

        public bool Exists(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate)
        {
            return _entities.Any(predicate);
        }

        public Task&amp;lt;bool&amp;gt; ExistsAsync(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate)
        {
            return _entities.AnyAsync(predicate);
        }

        public IQueryable&amp;lt;T&amp;gt; GetAll()
        {
            return GetEntities();
        }

        public IQueryable&amp;lt;T&amp;gt; GetBy(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate)
        {
            return GetEntities().Where(predicate);
        }

        public T GetFirst(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate)
        {
            return GetEntities().FirstOrDefault(predicate);
        }

        public async Task&amp;lt;T&amp;gt; GetFirstAsync(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate)
        {
            return await GetEntities().FirstOrDefaultAsync(predicate);
        }

        public T GetSingle(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate)
        {
            return GetEntities().SingleOrDefault(predicate);
        }

        public async Task&amp;lt;T&amp;gt; GetSingleAsync(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate)
        {
            return await GetEntities().SingleOrDefaultAsync(predicate);
        }

        public void Update(T entity)
        {
            Guard.Against.Null(entity, nameof(entity));
            _entities.Update(entity);
        }

        public void UpdateRange(IEnumerable&amp;lt;T&amp;gt; entities)
        {
            Guard.Against.Null(entities, nameof(entities));
            foreach (var entity in entities)
            {
                Update(entity);
            }
        }

        public T Find(TId id)
        {
            return _entities.Find(id);
        }

        public async Task&amp;lt;T&amp;gt; FindAsync(TId id)
        {
            return await _entities.FindAsync(id);
        }

        private IQueryable&amp;lt;T&amp;gt; GetEntities(bool asNoTracking = true)
        {
            if (asNoTracking)
                return _entities.AsNoTracking();
            return _entities;
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The domain model repository
&lt;/h2&gt;

&lt;p&gt;To handle retrieval and persistence actions of our domain entities, we’ll create a generic repository marker interface for aggregate root entities, called &lt;em&gt;IAggregateRepository&lt;/em&gt;. This operates only on classes implementing the &lt;em&gt;IAggregateRoot&lt;/em&gt; generic interface.&lt;/p&gt;

&lt;p&gt;Below the code for the aforementioned interfaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface IAggregateRoot&amp;lt;TId&amp;gt; : IEntity&amp;lt;TId&amp;gt;
    {
        int Version { get; }
        void ApplyEvent(IDomainEvent&amp;lt;TId&amp;gt; @event, int version);
        IEnumerable&amp;lt;IDomainEvent&amp;lt;TId&amp;gt;&amp;gt; GetUncommittedEvents();
        void ClearUncommittedEvents();
        bool IsDeleted { get; }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface IAggregateRepository&amp;lt;T, TId&amp;gt; : 
        IEntityRepository&amp;lt;T, TId&amp;gt; 
        where T : class, IAggregateRoot&amp;lt;TId&amp;gt;
    {
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;IAggregateRoot&lt;/em&gt; interface is pretty specific to the overall &lt;a href="https://github.com/thecodewrapper/CH.CleanArchitecture" rel="noopener noreferrer"&gt;architecture&lt;/a&gt;, as you may notice from the few domain-event-related functions in there. For more information regarding this, take a look at my earlier post &lt;a href="https://thecodewrapper.com/dev/designing-the-domain-model-to-support-multiple-persistence-methods/" rel="noopener noreferrer"&gt;Designing the domain model to support multiple persistence methods&lt;/a&gt;. Despite that, the point of this is to distinguish between data entities and domain entities so you should adjust this interface properly to your needs.&lt;/p&gt;

&lt;p&gt;You’ll notice that the &lt;em&gt;IAggregateRepository&lt;/em&gt; interface also implements the &lt;em&gt;IEntityRepository&lt;/em&gt; interface. Remember that the purpose of this aggregate repository is to solely act upon, and return, domain entities (specifically aggregate roots). In its generic abstract implementation, the aggregate repository (named &lt;a href="https://github.com/thecodewrapper/CH.CleanArchitecture/blob/master/Source/Infrastructure/CH.CleanArchitecture.Infrastructure/Repositories/EFRepository.cs" rel="noopener noreferrer"&gt;EFRepository&lt;/a&gt;) uses &lt;a href="https://automapper.org/" rel="noopener noreferrer"&gt;AutoMapper&lt;/a&gt; to map between data entities and domain objects, and can directly operate on domain objects using projections, returning IQueryable. To do that, &lt;a href="https://github.com/thecodewrapper/CH.CleanArchitecture/blob/master/Source/Infrastructure/CH.CleanArchitecture.Infrastructure/Repositories/EFRepository.cs" rel="noopener noreferrer"&gt;EFRepository&lt;/a&gt; makes use of a generic instance of &lt;em&gt;IEntityRepository&lt;/em&gt;, which is registered in the DI container with an open generic type of &lt;a href="https://github.com/thecodewrapper/CH.CleanArchitecture/blob/master/Source/Infrastructure/CH.CleanArchitecture.Infrastructure/Repositories/DataEntityRepository.cs" rel="noopener noreferrer"&gt;DataEntityRepository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The code for the concrete implementation of the abstract aggregate repository is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;internal abstract class EFRepository&amp;lt;T, M, TId&amp;gt; : IAggregateRepository&amp;lt;T, TId&amp;gt;
        where T : class, IAggregateRoot&amp;lt;TId&amp;gt;
        where M : class, IDataEntity&amp;lt;TId&amp;gt;
    {
        private readonly IMapper _mapper;
        private readonly IEntityRepository&amp;lt;M, TId&amp;gt; _persistenceRepo;
        public IUnitOfWork UnitOfWork =&amp;gt; _persistenceRepo.UnitOfWork;

        public EFRepository(IMapper mapper, IEntityRepository&amp;lt;M, TId&amp;gt; persistenceRepo) {
            _mapper = mapper;
            _persistenceRepo = persistenceRepo;
        }

        public virtual void Add(T entity) {
            Guard.Against.Null(entity, nameof(entity));

            var dataEntity = _mapper.Map&amp;lt;M&amp;gt;(entity);
            _persistenceRepo.Add(dataEntity);
        }

        public virtual async ValueTask AddAsync(T entity) {
            Guard.Against.Null(entity, nameof(entity));

            var dataEntity = _mapper.Map&amp;lt;M&amp;gt;(entity);
            await _persistenceRepo.AddAsync(dataEntity);
        }

        public virtual void AddRange(IEnumerable&amp;lt;T&amp;gt; entities) {
            var dataEntities = _mapper.Map&amp;lt;IEnumerable&amp;lt;M&amp;gt;&amp;gt;(entities);
            _persistenceRepo.AddRange(dataEntities);
        }

        public Task AddRangeAsync(IEnumerable&amp;lt;T&amp;gt; entities) {
            var dataEntities = _mapper.Map&amp;lt;IEnumerable&amp;lt;M&amp;gt;&amp;gt;(entities);
            return _persistenceRepo.AddRangeAsync(dataEntities);
        }

        public virtual void Delete(T entity) {
            Guard.Against.Null(entity, nameof(entity));

            var dataEntity = _mapper.Map&amp;lt;M&amp;gt;(entity);
            _persistenceRepo.Delete(dataEntity);
        }

        public virtual void DeleteRange(IEnumerable&amp;lt;T&amp;gt; entities) {
            Guard.Against.Null(entities, nameof(entities));

            var dataEntities = _mapper.Map&amp;lt;IEnumerable&amp;lt;M&amp;gt;&amp;gt;(entities);
            _persistenceRepo.DeleteRange(dataEntities);
        }

        public virtual bool Exists(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate) {
            var expression = _mapper.Map&amp;lt;Expression&amp;lt;Func&amp;lt;M, bool&amp;gt;&amp;gt;&amp;gt;(predicate);
            return _persistenceRepo.Exists(expression);
        }

        public virtual Task&amp;lt;bool&amp;gt; ExistsAsync(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate) {
            var expression = _mapper.Map&amp;lt;Expression&amp;lt;Func&amp;lt;M, bool&amp;gt;&amp;gt;&amp;gt;(predicate);
            return _persistenceRepo.ExistsAsync(expression);
        }

        public virtual IQueryable&amp;lt;T&amp;gt; GetAll() {
            return _mapper.ProjectTo&amp;lt;T&amp;gt;(_persistenceRepo.GetAll());
        }

        public virtual IQueryable&amp;lt;T&amp;gt; GetBy(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate) {
            var expression = _mapper.Map&amp;lt;Expression&amp;lt;Func&amp;lt;M, bool&amp;gt;&amp;gt;&amp;gt;(predicate);
            return _mapper.ProjectTo&amp;lt;T&amp;gt;(_persistenceRepo.GetBy(expression));
        }

        public virtual T GetFirst(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate) {
            var expression = _mapper.Map&amp;lt;Expression&amp;lt;Func&amp;lt;M, bool&amp;gt;&amp;gt;&amp;gt;(predicate);
            return _mapper.Map&amp;lt;T&amp;gt;(_persistenceRepo.GetFirst(expression));
        }

        public virtual async Task&amp;lt;T&amp;gt; GetFirstAsync(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate) {
            var expression = _mapper.Map&amp;lt;Expression&amp;lt;Func&amp;lt;M, bool&amp;gt;&amp;gt;&amp;gt;(predicate);
            return _mapper.Map&amp;lt;T&amp;gt;(await _persistenceRepo.GetFirstAsync(expression));
        }

        public virtual T GetSingle(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate) {
            var expression = _mapper.Map&amp;lt;Expression&amp;lt;Func&amp;lt;M, bool&amp;gt;&amp;gt;&amp;gt;(predicate);
            return _mapper.Map&amp;lt;T&amp;gt;(_persistenceRepo.GetSingle(expression));
        }

        public virtual async Task&amp;lt;T&amp;gt; GetSingleAsync(Expression&amp;lt;Func&amp;lt;T, bool&amp;gt;&amp;gt; predicate) {
            var expression = _mapper.Map&amp;lt;Expression&amp;lt;Func&amp;lt;M, bool&amp;gt;&amp;gt;&amp;gt;(predicate);
            return _mapper.Map&amp;lt;T&amp;gt;(await _persistenceRepo.GetSingleAsync(expression));
        }

        public virtual void Update(T entity) {
            Guard.Against.Null(entity, nameof(entity));

            var originalEntity = _persistenceRepo.Find(entity.Id);
            var updatedEntity = _mapper.Map(entity, originalEntity);
            _persistenceRepo.Update(updatedEntity);
        }

        public virtual async Task UpdateAsync(T entity) {
            Guard.Against.Null(entity, nameof(entity));

            var originalEntity = await _persistenceRepo.FindAsync(entity.Id);
            var updatedEntity = _mapper.Map(entity, originalEntity);
            _persistenceRepo.Update(updatedEntity);
        }


        public virtual void UpdateRange(IEnumerable&amp;lt;T&amp;gt; entities) {
            Guard.Against.Null(entities, nameof(entities));

            foreach (var entity in entities) {
                Update(entity);
            }
        }

        public T Find(TId id) {
            return _mapper.Map&amp;lt;T&amp;gt;(_persistenceRepo.Find(id));
        }

        public async Task&amp;lt;T&amp;gt; FindAsync(TId id) {
            return _mapper.Map&amp;lt;T&amp;gt;(await _persistenceRepo.FindAsync(id));
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Things to remember
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, all functions here take in a domain entity type and map that to their corresponding data entity type using &lt;a href="https://automapper.org/" rel="noopener noreferrer"&gt;AutoMapper&lt;/a&gt;. This is done for convenience, but keep in mind that the mapping might not always be straightforward between domain and data entities. You could adjust that for your own needs, either by working with AutoMapper custom &lt;a href="https://docs.automapper.org/en/stable/Custom-value-resolvers.html" rel="noopener noreferrer"&gt;value resolvers&lt;/a&gt; and &lt;a href="https://docs.automapper.org/en/stable/Custom-type-converters.html" rel="noopener noreferrer"&gt;type converters&lt;/a&gt; to do the proper mapping, or override the default implementation in-place to whatever you see fit.&lt;/p&gt;

&lt;p&gt;Furthermore, it is a &lt;a href="https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-design" rel="noopener noreferrer"&gt;good practice&lt;/a&gt; to have a dedicated repository interface/implementation for each aggregate or aggregate root in your domain. Therefore, you can inherit from &lt;em&gt;EFRepository&lt;/em&gt; to have a basic implementation of the repository functionality and override and/or add functions to cover your domain-specific needs. This is, after all, the purpose of this abstract repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;As with everything described in the earlier related posts, this particular approach is not a one-size-fits-all approach. Admittedly, this setup has some complexity in its initial setup, and largely depends on the project whether it justifies the overhead. Personally, it has been the way to go for a couple of my recent projects and has worked quite well for me so far.&lt;/p&gt;

&lt;p&gt;Feel free to drop a comment if you have any questions and let me know what you think of this approach.&lt;/p&gt;

</description>
      <category>efcore</category>
      <category>dotnet</category>
      <category>programming</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Implementing a Clean Architecture in ASP.NET Core 6</title>
      <dc:creator>thecodewrapper</dc:creator>
      <pubDate>Fri, 04 Feb 2022 07:54:27 +0000</pubDate>
      <link>https://dev.to/thecodewrapper/implementing-a-clean-architecture-in-aspnet-core-6-bdc</link>
      <guid>https://dev.to/thecodewrapper/implementing-a-clean-architecture-in-aspnet-core-6-bdc</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This post is the first part in a series of posts which describe my personal take on &lt;a href="https://thecodewrapper.com/dev/implementing-clean-architecture-in-aspnetcore-6/" rel="noopener noreferrer"&gt;implementing a Clean Architecture with ASP.NET Core 6&lt;/a&gt;, and hopefully give you some ideas on how to go about building your own clean architecture.&lt;/p&gt;

&lt;p&gt;This part is merely an overview of the overall architecture. More details on the internals and implementation will follow in separate posts.&lt;/p&gt;

&lt;p&gt;Also, this post will not describe the concepts of a clean architecture in detail, nor will be an introduction to Domain-Driven Design or best practices. If you need to learn more about clean architecture concepts in detail, you can follow one of the links on the bottom of this post.&lt;/p&gt;

&lt;p&gt;The intention of this design is to act as a starting point for building ASP.NET Core solutions. It is loosely-coupled, relies heavily on Dependency Inversion principle, and promotes &lt;a href="https://martinfowler.com/bliki/DomainDrivenDesign.html" rel="noopener noreferrer"&gt;Domain-Driven Design&lt;/a&gt; for organizing your core (although this is not forced). The goal for which I set out to do this, was to build a bare-bone, maintainable and extendable architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstraction is key
&lt;/h2&gt;

&lt;p&gt;My secondary goal was to abstract away concepts like domain events, &lt;a href="https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs" rel="noopener noreferrer"&gt;CQRS&lt;/a&gt; and &lt;a href="https://martinfowler.com/eaaDev/EventSourcing.html" rel="noopener noreferrer"&gt;event sourcing&lt;/a&gt; from dependencies on specific implementations – or even other abstractions brought by some well-known packages – such as &lt;a href="https://github.com/jbogard/MediatR" rel="noopener noreferrer"&gt;MediatR&lt;/a&gt;, &lt;a href="https://masstransit-project.com/" rel="noopener noreferrer"&gt;MassTransit&lt;/a&gt;, &lt;a href="https://www.eventstore.com/" rel="noopener noreferrer"&gt;EventStore&lt;/a&gt; etc.&lt;/p&gt;

&lt;p&gt;Another particular thing I wanted to do with this, is to be able to combine and easily switch between relational (or non-relational for that matter) persistence methods and a streaming or event sourcing method without affecting any of the underlying architecture. I’ve seen quite a few implementations where the decision to use event sourcing for persistence is deeply rooted (or at least largely apparent) in how the core is organized. I wanted to avoid that and abstract it.&lt;/p&gt;

&lt;p&gt;Although this is not a fully-fledged event store implementation (the default implementation is using Entity Framework Core with SQL Server to persist events, instead of something like an &lt;a href="https://www.eventstore.com/" rel="noopener noreferrer"&gt;EventStore&lt;/a&gt;, which would be more technically appropriate), it serves the purpose of the aforementioned abstraction. The specifics of the actual implementation of event sourcing using Entity Framework Core, and the inclusion of snapshots and retroactive events, will be detailed in a future post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;I do not claim that this design is in anyway better or cleaner than any others out there. This is simply what worked very well for me in several of my projects. Most software developers out there recognize that there is no single architecture which is best. The best architecture is the one that works well for you, your team and your type of project. This is by no means a production-ready solution, it is not a framework, it is merely a starting point.&lt;/p&gt;

&lt;p&gt;Furthermore, this solution is not by all means complete in terms of features. There are several features which can be beneficial depending on the type of project you are building (caching, analytics etc.) which are not included at the time of this writing, but can be easily added if needed. I try to maintain this and add new features as much as time permits, so if you have any suggestions feel free to ask or contribute.&lt;/p&gt;

&lt;h2&gt;
  
  
  History
&lt;/h2&gt;

&lt;p&gt;When I set out to build this bare-bone design I of course took input and advice from some very well-designed open-source architectures on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ardalis/CleanArchitecture" rel="noopener noreferrer"&gt;https://github.com/ardalis/CleanArchitecture&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/dotnet-architecture/eShopOnContainers" rel="noopener noreferrer"&gt;https://github.com/dotnet-architecture/eShopOnContainers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jasontaylordev/CleanArchitecture" rel="noopener noreferrer"&gt;https://github.com/jasontaylordev/CleanArchitecture&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/blazorhero/CleanArchitecture" rel="noopener noreferrer"&gt;https://github.com/blazorhero/CleanArchitecture&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is great article from Uncle Bob (Robert C. Martin), who is a huge advocate of clean code and clean architecture practices: &lt;a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html" rel="noopener noreferrer"&gt;https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html&lt;/a&gt;. His book &lt;a href="https://www.amazon.com/dp/0134494164" rel="noopener noreferrer"&gt;Clean Architecture&lt;/a&gt; is also a great resource of valuable information about universal rules of software architecture, so I definitely recommend giving it a read if you are interested in building maintainable, loosely-coupled, long-lasting solutions.&lt;/p&gt;

&lt;p&gt;There is also a great talk by Jason Taylor given at NDC Sydney in 2019 which talks about Clean Architecture in ASP.NET Core 3.0, in &lt;a href="https://www.youtube.com/watch?v=5OtUm1BLmG0&amp;amp;feature=emb_title" rel="noopener noreferrer"&gt;this video&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technologies used
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;ASP.NET Core 6&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/ef/core/" rel="noopener noreferrer"&gt;Entity Framework Core 6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://masstransit-project.com/" rel="noopener noreferrer"&gt;MassTransit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://automapper.org/" rel="noopener noreferrer"&gt;AutoMapper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/components/?view=aspnetcore-5.0" rel="noopener noreferrer"&gt;Razor Components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/overview?view=aspnetcore-5.0" rel="noopener noreferrer"&gt;ASP.NET Core MVC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ardalis/GuardClauses" rel="noopener noreferrer"&gt;GuardClauses&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://xunit.net/" rel="noopener noreferrer"&gt;xUnit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/moq/moq4" rel="noopener noreferrer"&gt;Moq&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fluentassertions.com/" rel="noopener noreferrer"&gt;Fluent Assertions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fakeiteasy.readthedocs.io/en/stable/quickstart/" rel="noopener noreferrer"&gt;FakeItEasy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;p&gt;The features of this particular solution are summarized briefly below, in no particular order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Localization for multiple language support&lt;/li&gt;
&lt;li&gt;Event sourcing using Entity Framework Core and SQL Server as persistent storage, including snapshots and retroactive events&lt;/li&gt;
&lt;li&gt;EventStore repository and DataEntity generic repository. Persistence can be swapped between them, fine-grained to individual entities&lt;/li&gt;
&lt;li&gt;Persistent application configurations with optional encryption&lt;/li&gt;
&lt;li&gt;Data operation auditing built-in (for entities which are not using the EventStore)&lt;/li&gt;
&lt;li&gt;Local user management with ASP.NET Core Identity&lt;/li&gt;
&lt;li&gt;Clean separation of data entities and domain objects and mapping between them for persistence/retrieval using AutoMapper&lt;/li&gt;
&lt;li&gt;ASP.NET Core MVC with Razor Components used for presentation&lt;/li&gt;
&lt;li&gt;CQRS using handler abstractions to support MassTransit or MediatR with very little change&lt;/li&gt;
&lt;li&gt;Service bus abstractions to support message-broker solutions like MassTransit or MediatR (default implementation uses MassTransit’s mediator)&lt;/li&gt;
&lt;li&gt;Unforcefully promoting Domain-Driven Design with aggregates, entities and domain event abstractions.&lt;/li&gt;
&lt;li&gt;Lightweight authorization framework using ASP.NET Core AuthorizationHandler&lt;/li&gt;
&lt;li&gt;Docker containerization support for SQL Server and Web app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some other goodies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Password generator implementation based on ASP.NET Core Identity password configuration&lt;/li&gt;
&lt;li&gt;Razor Class Library containing ready-made Blazor components for commonly used features such as CRUD buttons, toast functionality, modal components, Blazor Select2, DataTables integration and page loader&lt;/li&gt;
&lt;li&gt;Common library with various type extensions, result wrapper objects, paged result data structures, date format converter and more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Architecture Overview&lt;/p&gt;

&lt;p&gt;Further below is a figure depicting the overall architecture in terms of layers and their components. The figure is meant to be a representation of how the actual solution is layered and show the gradual interrelationships between the layers, as opposed to simply showing the logical structure of a clean architecture.&lt;/p&gt;

&lt;p&gt;In any case, the below diagram taken from Jason Taylor’s talk at NDC Sydney (2019), broadly depicts the logical structure of the architecture:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fca51d6kehevco0xy9jzy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fca51d6kehevco0xy9jzy.png" alt="Image description" width="700" height="695"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The actual solution consists of several projects, separated in folders representing the layers for convenience. The below figure is a code map generated from the solution. It shows the different layers and projects, along with their inter-dependencies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0m1ax7ndkmx9pqc0ehzc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0m1ax7ndkmx9pqc0ehzc.jpg" alt="Image description" width="700" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see from the arrows, all dependencies point downwards (or inwards if this was a circle diagram). there are no arrows pointing upwards between the Core, Infrastructure and Presentation layers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Layer
&lt;/h2&gt;

&lt;p&gt;The Core Layer is made up of two parts, the inner core and outer core. The inner core is the domain and the outer core is the application. This consists of two projects in the solution under the Core folder, the Application and the Domain projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inner Core (Domain Layer)
&lt;/h3&gt;

&lt;p&gt;This layer contains application-independent business logic. This is the part of the business logic which would be the same even if we weren’t building a software. It is a formulation of the core business rules.&lt;/p&gt;

&lt;p&gt;The organization of this project follows Domain-Driven design patterns, although this is a matter of preference and can be handled any way you see fit. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Aggregates, entities, value objects, custom domain exceptions, and interfaces for domain services.&lt;/li&gt;
&lt;li&gt;Interfaces for domain-driven design concepts (i.e. IAggregateRoot, IDomainEvent, IEntity).&lt;/li&gt;
&lt;li&gt;Base implementations of aggregate root and domain event. Also contains specific domain events pertaining to the business processes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Outer Core (Application Layer)
&lt;/h3&gt;

&lt;p&gt;This layer contains application-specific business logic. This contains the “what” the system should do. This includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interfaces for infrastructure components such as repositories, unit-of-work and event sourcing.&lt;/li&gt;
&lt;li&gt;Commands and Queries models and handlers&lt;/li&gt;
&lt;li&gt;Interfaces and DTOs for cross-cutting concerns (i.e. service bus)&lt;/li&gt;
&lt;li&gt;Authorization operations, requirements and handlers implementations&lt;/li&gt;
&lt;li&gt;Interfaces and concrete implementations of application-specific business logic services.&lt;/li&gt;
&lt;li&gt;Mapping profiles between domain entities and CQRS models&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Infrastructure Layer
&lt;/h2&gt;

&lt;p&gt;This layer contains details, concrete implementations for repositories, unit-of-work, event store, service bus implementations etc. This contains the “how” the system should do what is supposed to do. The decoupling between the application layer and the infrastructure layer is what allows solution structures like this one to change and/or add specific implementations as the project requirements change.&lt;/p&gt;

&lt;p&gt;In overview, this layer contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generic and specific repositories implementations&lt;/li&gt;
&lt;li&gt;EF DbContexts, data models and migrations&lt;/li&gt;
&lt;li&gt;Event sourcing persistence and services implementations&lt;/li&gt;
&lt;li&gt;Implementations for cross-cutting concerns (i.e, application configuration service, localization service etc.)&lt;/li&gt;
&lt;li&gt;Data entity auditing implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This consists of 3 projects in the solution under the Infrastructure folder, the Auditing, Data and Shared projects.&lt;/p&gt;

&lt;p&gt;The Auditing project consists of various extensions methods for DbContext, primarily related to SaveChanges. It is responsible for generating auditing records for each tracked entity’s changes.&lt;/p&gt;

&lt;p&gt;The Data project contains domain and generic (CRUD and event-sourcing) repository implementations, DbContexts, EF Core migrations, entity type configurations (if any), event store implementation (including snapshots), data entity to domain object mappings, and persistence related services (i.e. a database initializer service).&lt;/p&gt;

&lt;p&gt;The Resources project contains localized shared resources and resource keys, along with localization services implementations.&lt;/p&gt;

&lt;p&gt;The Shared project contains service implementations for cross-cutting concerns such as user management and authentication, file storage, service bus, localization, application configuration and a password generator.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Presentation Layer
&lt;/h2&gt;

&lt;p&gt;This layer essentially contains the I/O components of the system, the GUI, REST APIs, mobile applications or console shells, and anything directly related to them. It is the starting point of our application.&lt;/p&gt;

&lt;p&gt;For this starting point solution, it contains the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ASP.NET Core MVC web application using Razor Components&lt;/li&gt;
&lt;li&gt;A shared class library containing common Razor Components, such as toast notifications, modal components, Blazor Select2, DataTablesJS integration and CRUD buttons.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Common Layer
&lt;/h2&gt;

&lt;p&gt;This single class library contains common types used across all other layers. What you would typically include in this library is things that you would wrap up into a Nuget package and use in multiple solutions. For clarification, this does not represent the Shared Kernel strategic pattern in DDD. Some of these include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A generic Result wrapper&lt;/li&gt;
&lt;li&gt;Paged result models for query operations&lt;/li&gt;
&lt;li&gt;CLR type extensions&lt;/li&gt;
&lt;li&gt;Notification models&lt;/li&gt;
&lt;li&gt;Custom attributes and converters&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Yes, yes, give me the code
&lt;/h2&gt;

&lt;p&gt;The repository for the source code of this solution can be found &lt;a href="https://github.com/thecodewrapper/CH.CleanArchitecture" rel="noopener noreferrer"&gt;here&lt;/a&gt;. The solution includes an extremely basic domain model for an online shop application, as an example. The UI part for this is not included however. The order domain exists to simply showcase the different elements of the architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Just like any software solution, this is not a one-size-fits-all architecture. It is simply what worked very well for me in my projects. As mentioned in the introduction of this post, there is still quite a lot to include in this solution, therefore I will be updating the repo on GitHub, as well as this post, whenever something new is added.&lt;/p&gt;

&lt;p&gt;Please feel free to leave any feedback or suggestions on this, and if you would like to have anything in particular be included in the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future work
&lt;/h2&gt;

&lt;p&gt;In the near future, I’ve planned to make the following additions/changes to the solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add support for caching, with an implementation for &lt;a href="https://redis.io/" rel="noopener noreferrer"&gt;Redis&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Replace MVC for presentation with Blazor Server&lt;/li&gt;
&lt;li&gt;Include a RESTful API for domain business logic&lt;/li&gt;
&lt;li&gt;Additional implementation of event-sourcing using &lt;a href="https://www.eventstore.com/eventstoredb" rel="noopener noreferrer"&gt;EventStoreDB&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Here are some additional posts you may find helpful in understanding what clean architecture is all about:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.freecodecamp.org/news/a-quick-introduction-to-clean-architecture-990c014448d2/" rel="noopener noreferrer"&gt;https://www.freecodecamp.org/news/a-quick-introduction-to-clean-architecture-990c014448d2/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.oncehub.com/blog/explaining-clean-architecture" rel="noopener noreferrer"&gt;https://www.oncehub.com/blog/explaining-clean-architecture&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>cqrs</category>
      <category>ddd</category>
      <category>csharp</category>
    </item>
  </channel>
</rss>
