<?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: Anton B</title>
    <description>The latest articles on DEV Community by Anton B (@__3545da69e7b38a555).</description>
    <link>https://dev.to/__3545da69e7b38a555</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%2F3759654%2F86b527b9-dc68-44fe-9bd3-d050fe957eab.png</url>
      <title>DEV Community: Anton B</title>
      <link>https://dev.to/__3545da69e7b38a555</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/__3545da69e7b38a555"/>
    <language>en</language>
    <item>
      <title>AI Assistants — The Wiki-First Approach</title>
      <dc:creator>Anton B</dc:creator>
      <pubDate>Wed, 15 Apr 2026 08:00:29 +0000</pubDate>
      <link>https://dev.to/__3545da69e7b38a555/ai-assistants-the-wiki-first-approach-3k0h</link>
      <guid>https://dev.to/__3545da69e7b38a555/ai-assistants-the-wiki-first-approach-3k0h</guid>
      <description>&lt;p&gt;In modern projects, the codebase grows exponentially, and AI assistants have become an integral part of the development workflow. However, without a single source of truth, every developer (and every AI agent) is forced to learn the project from scratch, wasting context window limits, time, and resources. The &lt;strong&gt;wiki-first&lt;/strong&gt; approach solves this problem by turning documentation into a living, automatically maintained knowledge center that dictates how both humans and AI operate.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;Note:&lt;/strong&gt; All structural examples, file names (&lt;code&gt;QWEN.md&lt;/code&gt;, &lt;code&gt;.qwen/skills/&lt;/code&gt;), and workflows in this article are provided for the &lt;strong&gt;QWEN&lt;/strong&gt; ecosystem. The methodology itself is universal and can easily be adapted to any AI agent capable of working with Markdown instructions.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🔍 What is Wiki-First?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Wiki-first&lt;/strong&gt; is a methodology where, before any work with the code (writing features, reviewing, refactoring, making architectural decisions), the developer or AI agent &lt;strong&gt;must&lt;/strong&gt; first study the project's wiki documentation (&lt;code&gt;docs/wiki/&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Key principle:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Wiki = overview, Code = details&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The wiki stores condensed, structured knowledge: system architecture, accepted patterns, service dependencies, naming conventions, and DI rules. Specific method signatures, SQL queries, or test assertions are looked up directly in the code if the wiki does not cover the task.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Benefits of the Approach
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Without Wiki-First&lt;/th&gt;
&lt;th&gt;With Wiki-First&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Project Onboarding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Everyone learns the code from scratch&lt;/td&gt;
&lt;td&gt;Ready-made overview in 5 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Knowledge Storage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Knowledge stays in developers' heads&lt;/td&gt;
&lt;td&gt;Knowledge is centralized in the wiki, accessible to all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Onboarding&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Newcomers spend days deciphering the code&lt;/td&gt;
&lt;td&gt;Newcomers read the wiki → hours instead of days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI Agent Workflow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wastes context scanning thousands of lines&lt;/td&gt;
&lt;td&gt;Reads concise wiki pages, saving tokens and time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Documentation Freshness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Wiki falls behind the code, quickly becomes outdated&lt;/td&gt;
&lt;td&gt;Wiki is automatically updated after every change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Standard Compliance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Developers violate patterns out of ignorance&lt;/td&gt;
&lt;td&gt;AI and humans follow unified rules from the wiki&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🛠 How to Set Up Wiki-First in Your Project
&lt;/h2&gt;

&lt;p&gt;For quick and correct setup, use the &lt;a href="https://github.com/abaula/DevInsights/blob/main/AIAssistantWikiFirst/WIKI_FIRST_TEMPLATE.en.md" rel="noopener noreferrer"&gt;WIKI_FIRST_TEMPLATE.en.md&lt;/a&gt; template. It is designed &lt;strong&gt;for execution by an AI agent&lt;/strong&gt;, which will automatically create the entire knowledge infrastructure and skills.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1. Prepare Parameters
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Copy &lt;code&gt;WIKI_FIRST_TEMPLATE.en.md&lt;/code&gt; to the root of your repository.&lt;/li&gt;
&lt;li&gt;Open the file and fill the &lt;code&gt;INPUT DATA&lt;/code&gt; table with real values:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;project_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;WebArchiveBackend&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;project_description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Backend API for storing and indexing web pages&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tech_stack&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;C#, .NET 8, gRPC, PostgreSQL, Dapper&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;docs_path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docs/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wiki_path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;docs/wiki/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;skills_path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.qwen/skills/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;source_dirs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;src/, tests/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Step 2. Launch the AI Agent
&lt;/h3&gt;

&lt;p&gt;Pass the filled file to your AI assistant with the following prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Execute the instructions from &lt;code&gt;WIKI_FIRST_TEMPLATE.en.md&lt;/code&gt; strictly step-by-step. Create the wiki structure, AI skills, and the main rules file. Do not modify the existing project code. After creating the structure, perform the Initial Ingest (Step 6)."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The agent will automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the &lt;code&gt;docs/wiki/&lt;/code&gt; directory and three base files:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;index.md&lt;/code&gt; — catalog of all wiki pages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;log.md&lt;/code&gt; — change log (append-only)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Wiki Format.md&lt;/code&gt; — guide to page formatting&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Generate three specialized AI skills in &lt;code&gt;.qwen/skills/&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;wiki-workflow&lt;/code&gt; — knowledge management (ingest, query, lint, post-change lint)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;code-contributor&lt;/code&gt; — writing and modifying code according to standards&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;code-architecture&lt;/code&gt; — architectural review (SOLID, DI, layered architecture)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;QWEN.md&lt;/code&gt; in the project root with a strict wiki-first rule and source hierarchy: &lt;code&gt;docs/wiki/ &amp;gt; Project code&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3. Initial Project Analysis (Initial Ingest)
&lt;/h3&gt;

&lt;p&gt;After creating the structure, the agent will execute &lt;strong&gt;Step 6&lt;/strong&gt; from the template:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scans the source code directories (&lt;code&gt;source_dirs&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Generates initial wiki pages: &lt;code&gt;Architecture.md&lt;/code&gt;, &lt;code&gt;Components.md&lt;/code&gt;, &lt;code&gt;Database.md&lt;/code&gt;, &lt;code&gt;Code Patterns.md&lt;/code&gt;, &lt;code&gt;Testing.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Updates &lt;code&gt;index.md&lt;/code&gt;, adding new pages to the catalog.&lt;/li&gt;
&lt;li&gt;Adds an entry to &lt;code&gt;log.md&lt;/code&gt; about the initial knowledge load.&lt;/li&gt;
&lt;li&gt;⚠️ &lt;strong&gt;Important:&lt;/strong&gt; The wiki contains only overviews and relative links to files. Copying code from &lt;code&gt;.cs&lt;/code&gt;, &lt;code&gt;.proto&lt;/code&gt;, or &lt;code&gt;.sql&lt;/code&gt; files is prohibited.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4. Verify Installation
&lt;/h3&gt;

&lt;p&gt;After the agent finishes, verify the result using the checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;code&gt;QWEN.md&lt;/code&gt; contains the wiki-first rule and source priority&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;docs/wiki/&lt;/code&gt; contains &lt;code&gt;index.md&lt;/code&gt;, &lt;code&gt;log.md&lt;/code&gt;, &lt;code&gt;Wiki Format.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;.qwen/skills/&lt;/code&gt; contains 3 &lt;code&gt;SKILL.md&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;[ ] All skills explicitly state: &lt;code&gt;wiki &amp;gt; code&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Base wiki pages for architecture, components, and patterns are generated&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;index.md&lt;/code&gt; contains links to all created pages&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;log.md&lt;/code&gt; contains an entry about the first ingest with date and description&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔄 How to Work with Wiki-First After Setup
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Developer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Before a task → open &lt;code&gt;docs/wiki/index.md&lt;/code&gt;. After committing → update wiki + add an entry to &lt;code&gt;log.md&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI Agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Any request → wiki first. After code changes → post-change lint. Missing details → read code.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Review&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;If a PR didn't update the wiki → request changes. Wiki must not diverge from the code.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Typical AI agent workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Wiki-first&lt;/code&gt; → reads &lt;code&gt;index.md&lt;/code&gt; → finds relevant pages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Query/Ingest&lt;/code&gt; → clarifies details in code if needed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Code&lt;/code&gt; → writes feature/fix according to wiki patterns&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Post-Change Lint&lt;/code&gt; → updates wiki and &lt;code&gt;log.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Verify&lt;/code&gt; → checks links, frontmatter, code alignment&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🔄 Wiki Lifecycle: Maintenance, Consistency, and Skill Evolution
&lt;/h2&gt;

&lt;p&gt;Setting up wiki-first is not a one-time configuration, but the launch of a living process. Documentation left unattended quickly becomes a "knowledge graveyard." To ensure the approach delivers long-term value, the wiki must be continuously updated, and the AI agent must act as the primary guarantor of its consistency and completeness.&lt;/p&gt;

&lt;h3&gt;
  
  
  🤖 Agent as the Guardian of Integrity
&lt;/h3&gt;

&lt;p&gt;After the initial setup, responsibility for wiki freshness falls on the AI agent. With every code change, the agent must automatically run a &lt;strong&gt;post-change lint&lt;/strong&gt;: check if existing pages contradict the new implementation, update descriptions of affected components, and log changes in &lt;code&gt;log.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Require explicit synchronization confirmation from the agent. If a commit affects architecture, configuration, or adds new modules, the agent must not only fix the code but also update the wiki. This turns documentation from an "extra burden" into an integral part of the development cycle. Without this step, the wiki will quickly drift from the code, and the &lt;code&gt;wiki &amp;gt; code&lt;/code&gt; rule will lose its meaning.&lt;/p&gt;

&lt;h3&gt;
  
  
  📖 Documenting Complex Scenarios
&lt;/h3&gt;

&lt;p&gt;The true power of wiki-first emerges when not only static descriptions but also &lt;strong&gt;project-specific workflows&lt;/strong&gt; are added to the knowledge base.&lt;/p&gt;

&lt;p&gt;For example, adding a new configuration parameter in your project might require changes in five different places: from &lt;code&gt;appsettings.json&lt;/code&gt; to DI container objects, DTO models, and DB migrations. Instead of explaining this to the agent from scratch every time, document this scenario once as a separate wiki page or a section in &lt;code&gt;Code Patterns.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once a complex scenario is documented in the wiki, the agent begins executing it &lt;strong&gt;confidently, without hallucinations or missed steps&lt;/strong&gt;. When asked "Add parameter X", the agent:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Finds the instruction in the wiki via &lt;code&gt;index.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Strictly follows the described sequence of changes.&lt;/li&gt;
&lt;li&gt;Automatically updates the wiki if the scenario needs adaptation to new realities.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The more "routine" and "easily forgotten" scenarios you formalize in the wiki, the less time is spent micromanaging the agent, and the higher the stability of the results.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Model-Specific Nature of Wiki
&lt;/h3&gt;

&lt;p&gt;Different LLM models structure thoughts, choose priorities, and formulate instructions differently. This is &lt;strong&gt;not a bug, but a feature&lt;/strong&gt;. When an agent itself generates the wiki during initial analysis (&lt;code&gt;Initial Ingest&lt;/code&gt;) or subsequent updates, the documentation naturally "adapts" to its internal logic, tokenization, and attention patterns.&lt;/p&gt;

&lt;p&gt;A model will follow a wiki it generated or curated itself much more accurately and confidently than documentation written by another model or a human "for themselves." Therefore, do not strive for perfect "human" stylistics in the early stages. The main thing is that the structure is unambiguous, links work, and the update logic is maintained. Over time, you will notice that the wiki becomes the "native language" of your agent: it finds context faster, hallucinates less, and executes complex tasks more precisely.&lt;/p&gt;

&lt;h3&gt;
  
  
  🛠 Evolution of Skills and Rules
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;skills&lt;/code&gt; created during setup are not set in stone. As the project grows and experience with the agent accumulates, they must be adapted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the agent frequently misses a certain type of check, add the corresponding rule to &lt;code&gt;code-architecture&lt;/code&gt; or &lt;code&gt;wiki-workflow&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;If new tech stacks or patterns emerge, expand &lt;code&gt;code-contributor&lt;/code&gt; with examples and conventions.&lt;/li&gt;
&lt;li&gt;If the wiki structure becomes too bulky, refactor &lt;code&gt;index.md&lt;/code&gt;, introduce tags, or split monolithic pages into thematic blocks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;QWEN.md&lt;/code&gt; and &lt;code&gt;SKILL.md&lt;/code&gt; files themselves are part of the wiki ecosystem. They can and should be refactored to keep instructions precise, minimize context consumption, and match the current maturity stage of the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  💡 Core Principle: Wiki is a Living Organism
&lt;/h3&gt;

&lt;p&gt;The key idea of the approach is this: &lt;strong&gt;you cannot just create a wiki, you must continuously develop it&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maintain freshness:&lt;/strong&gt; every code change → wiki change + entry in &lt;code&gt;log.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enrich context:&lt;/strong&gt; turn verbal agreements, bug trackers, and complex manual procedures into formalized wiki instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Evolve the agent:&lt;/strong&gt; adapt skills, clarify priorities, and refine prompts for real project tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the wiki becomes the single source of truth, and the agent acts as its active curator, the team gets a scalable knowledge system. It doesn't "wither" over time but gains strength: the more you invest in it, the faster, more accurate, and more confident the AI works, reducing time spent on onboarding, code reviews, and bug fixing.&lt;/p&gt;




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

&lt;p&gt;The wiki-first approach transforms documentation from an "artifact that gets forgotten" into an &lt;strong&gt;active development tool&lt;/strong&gt;. AI agents operating under these rules save context, strictly adhere to architectural standards, and accelerate feature delivery.&lt;/p&gt;

&lt;p&gt;Set up wiki-first once using &lt;a href="https://github.com/abaula/DevInsights/blob/main/AIAssistantWikiFirst/WIKI_FIRST_TEMPLATE.en.md" rel="noopener noreferrer"&gt;WIKI_FIRST_TEMPLATE.en.md&lt;/a&gt;, integrate it into your CI/CD and onboarding processes, and you will get a scalable knowledge system that grows with your project rather than turning into legacy.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>assistant</category>
      <category>aiassistant</category>
    </item>
    <item>
      <title>Shadow Logging via Events - Complete Decoupling of Business Logic and Logging in DI Environment</title>
      <dc:creator>Anton B</dc:creator>
      <pubDate>Sun, 08 Feb 2026 09:51:27 +0000</pubDate>
      <link>https://dev.to/__3545da69e7b38a555/shadow-logging-via-events-complete-decoupling-of-business-logic-and-logging-in-di-environment-3kjf</link>
      <guid>https://dev.to/__3545da69e7b38a555/shadow-logging-via-events-complete-decoupling-of-business-logic-and-logging-in-di-environment-3kjf</guid>
      <description>&lt;p&gt;Hello, colleagues!&lt;/p&gt;

&lt;p&gt;I want to share an approach to logging that radically simplifies architecture and strengthens SOLID principles.&lt;/p&gt;

&lt;p&gt;I've created a code example on &lt;a href="https://github.com/abaula/MixedCode/blob/master/DecoupledLogging/src/Program.cs" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; to demonstrate how shadow decoupled logging works through C# events.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is Decoupled Logging?
&lt;/h1&gt;

&lt;p&gt;Decoupled logging is an architectural approach where business classes &lt;strong&gt;do not contain direct logger calls&lt;/strong&gt; (like &lt;code&gt;ILogger.LogInformation&lt;/code&gt;). Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Business classes generate &lt;strong&gt;events&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specialized logger classes&lt;/strong&gt; handle the events.&lt;/li&gt;
&lt;li&gt;Business classes know nothing about loggers.&lt;/li&gt;
&lt;li&gt;Logging is configured &lt;strong&gt;centrally&lt;/strong&gt; at the application level, without polluting domain logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Pros and Cons of Decoupled Logging
&lt;/h1&gt;

&lt;p&gt;There are opinions both "for" and "against" decoupled logging.&lt;/p&gt;

&lt;p&gt;Typical arguments "for":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SRP purity&lt;/strong&gt;: The class focuses on business logic; logging is an external concern.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testability&lt;/strong&gt;: No &lt;code&gt;ILogger&lt;/code&gt; mocks needed; classes work standalone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility and scalability&lt;/strong&gt;: One event can be handled in all required ways: logs, metrics, audit. Easy to change event handling logic and logging libraries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling for libraries&lt;/strong&gt;: Consumers of business logic classes decide themselves whether to log events and which ones. No hard dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Business class does not format data for logs. When implementing event handlers, use smart fast caching of incoming events with subsequent post-processing - it doesn't block business logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Critics' objections boil down to a simple thesis: don't complicate things if not really needed - "inject ILogger and don't worry." This opinion sounds reasonable; I agree with such criticism - if you have a simple application, don't complicate it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Extracting Logging from the Class
&lt;/h1&gt;

&lt;p&gt;The simplest way to separate business logic and logging is to write a Wrapper for the business class.&lt;/p&gt;

&lt;p&gt;Decorator/wrapper for logging is convenient but &lt;strong&gt;imposes usage rules&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client code must work through the wrapper.&lt;/li&gt;
&lt;li&gt;Refactoring becomes more complex.&lt;/li&gt;
&lt;li&gt;Duplication and inheritance issues arise.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach makes logging &lt;strong&gt;not "shadow"&lt;/strong&gt; - consumers indirectly know about it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Complete Separation
&lt;/h1&gt;

&lt;p&gt;Complete separation of business logic and logging is possible only using two independent objects: business class and log handler.&lt;/p&gt;

&lt;p&gt;Simple example:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderServiceLogger&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OrderServiceLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileLogger&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OrderService&lt;/span&gt; &lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderCreated&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&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;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogInformation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Order &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; created."&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;orderService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderService&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;fileLogger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FileLogger&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;orderServiceLogger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderServiceLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileLogger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach is straightforward, but if the application uses a DI container, it requires adaptation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Shadowing Logging
&lt;/h1&gt;

&lt;p&gt;DI containers perform 2 important tasks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Object factory&lt;/li&gt;
&lt;li&gt;Object lifetime management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Object creation is simple: the DI container will return 2 ready objects, with the log handler receiving the business class instance and logger instance upon creation.&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ServiceCollection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FileLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderServiceLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BuildServiceProvider&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;orderService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orderServiceLogger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderServiceLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is managing the lifetime of the log handler &lt;code&gt;OrderServiceLogger&lt;/code&gt;, i.e., explicitly storing a reference to the created object and synchronizing its lifetime with the business class &lt;code&gt;OrderService&lt;/code&gt; instance.&lt;/p&gt;

&lt;p&gt;If we do nothing else, we'll have to explicitly create a new &lt;code&gt;OrderServiceLogger&lt;/code&gt; instance wherever we create an &lt;code&gt;OrderService&lt;/code&gt; instance and ensure their lifetimes match - that's not the behavior we want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What we need:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use only business logic object instances in business logic, in our example &lt;code&gt;OrderService&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Business logic should know nothing about objects performing other tasks in the application, in our example logging via &lt;code&gt;OrderServiceLogger&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;When creating a business logic object, the application must guarantee all implemented service functions for it - if &lt;code&gt;OrderServiceLogger&lt;/code&gt; is implemented for &lt;code&gt;OrderService&lt;/code&gt;, it must be created in time and handle events.&lt;/li&gt;
&lt;li&gt;Correct service function operation includes optimal application resource management - the &lt;code&gt;OrderServiceLogger&lt;/code&gt; instance must be removed from memory after the associated &lt;code&gt;OrderService&lt;/code&gt; object is destroyed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These requirements are easy to implement, even within a DI container. We've sorted object creation; now we need to implement lifetime synchronization using weak references.&lt;/p&gt;

&lt;p&gt;We need to ensure the created &lt;code&gt;OrderServiceLogger&lt;/code&gt; object lives no less than the &lt;code&gt;OrderService&lt;/code&gt; instance and is removed when no longer needed.&lt;/p&gt;

&lt;p&gt;For this, we need an application-level object that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stores references to both dependent objects.&lt;/li&gt;
&lt;li&gt;Monitors their lifetimes.&lt;/li&gt;
&lt;li&gt;Removes &lt;code&gt;OrderServiceLogger&lt;/code&gt; as soon as &lt;code&gt;OrderService&lt;/code&gt; is removed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can implement such a class ourselves, where there is a &lt;strong&gt;key object&lt;/strong&gt; and &lt;strong&gt;dependent objects&lt;/strong&gt;. The architecture of such a class is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Key object(s) are stored as weak references, which do not prevent the object from being garbage collected.&lt;/li&gt;
&lt;li&gt;Dependent objects are stored as strong references, which prevent the garbage collector from destroying them.&lt;/li&gt;
&lt;li&gt;The state of key objects is periodically checked - if they are removed, dependent objects are also removed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the simple case, we can use the &lt;a href="https://learn.microsoft.com/ru-ru/dotnet/api/system.runtime.compilerservices.conditionalweaktable-2?view=net-8.0" rel="noopener noreferrer"&gt;ConditionalWeakTable&lt;/a&gt; class from the &lt;code&gt;System.Runtime.CompilerServices&lt;/code&gt; namespace, which already implements this logic.&lt;/p&gt;

&lt;h1&gt;
  
  
  Writing DI Logic
&lt;/h1&gt;

&lt;p&gt;Let's implement an extension method for &lt;code&gt;ServiceCollection&lt;/code&gt; and examine how it works.&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="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;ServiceCollectionExtensions&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;ServiceCollection&lt;/span&gt; &lt;span class="n"&gt;AddScopedWithLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TServiceInstance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TServiceLogger&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;ServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
        &lt;span class="nc"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TServiceInstance&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TService&lt;/span&gt;
        &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TServiceLogger&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Register TServiceInstance.&lt;/span&gt;
        &lt;span class="nc"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TServiceInstance&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Register TServiceLogger.&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TServiceLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Register TService.&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TServiceInstance&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TServiceLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;conditionalWeakTable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConditionalWeakTable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
            &lt;span class="c1"&gt;// Put instance and logger into ConditionalWeakTable.&lt;/span&gt;
            &lt;span class="n"&gt;conditionalWeakTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;services&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;AddScopedWithLogger&lt;/code&gt; method does all the necessary work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Registers all types in DI.&lt;/li&gt;
&lt;li&gt;Implements the logic for creating and linking the business class and its event handler class.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt; - in the DI container, it is necessary to separate the logic for creating the business class object itself from the logic for creating the instance with all its shadow objects. For this, it is best to use business class contracts (interfaces).&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderEventArgs&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventArgs&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;int&lt;/span&gt; &lt;span class="n"&gt;OrderId&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;;&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="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IOrderService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderEventArgs&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OrderCreated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CreateOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;customer&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;Thus, the DI container will pass the &lt;code&gt;OrderService&lt;/code&gt; instance to the &lt;code&gt;OrderServiceLogger&lt;/code&gt; constructor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In business logic, only contracts must be used&lt;/strong&gt;, which is a recommended approach.&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderManager&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IOrderManager&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;OrderManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt; &lt;span class="n"&gt;orderService&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="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;Correctly register all types in the container:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ServiceCollection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScopedWithLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OrderServiceLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, creating an object for its contract &lt;code&gt;IOrderService&lt;/code&gt; will trigger the following code from the extension method:&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;// Register TService.&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TServiceInstance&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TServiceLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;conditionalWeakTable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConditionalWeakTable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Put instance and logger into ConditionalWeakTable.&lt;/span&gt;
    &lt;span class="n"&gt;conditionalWeakTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;instance&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;Here's the breakdown for the parameter combination &lt;code&gt;IOrderService, OrderService, OrderServiceLogger&lt;/code&gt;:&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="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IOrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;sp&lt;/span&gt; &lt;span class="p"&gt;=&amp;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;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderServiceLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;conditionalWeakTable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConditionalWeakTable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Put instance and logger into ConditionalWeakTable.&lt;/span&gt;
    &lt;span class="n"&gt;conditionalWeakTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;instance&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;As you can see, it's simple. &lt;code&gt;OrderService&lt;/code&gt; and &lt;code&gt;OrderServiceLogger&lt;/code&gt; objects are created with all dependencies, then both objects are saved in the &lt;code&gt;ConditionalWeakTable&amp;lt;object, object&amp;gt;&lt;/code&gt;.&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;conditionalWeakTable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConditionalWeakTable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Put instance and logger into ConditionalWeakTable.&lt;/span&gt;
&lt;span class="n"&gt;conditionalWeakTable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;logger&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;ConditionalWeakTable&amp;lt;object, object&amp;gt;&lt;/code&gt; object itself must be registered in the DI container with a lifetime equal to or greater than &lt;code&gt;OrderService&lt;/code&gt; and &lt;code&gt;OrderServiceLogger&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I recommend using &lt;code&gt;Scoped&lt;/code&gt; if the registered objects live no longer. &lt;code&gt;Singleton&lt;/code&gt; is not necessary.&lt;/p&gt;

&lt;p&gt;And the last piece of the puzzle - at the application level, create a &lt;code&gt;ConditionalWeakTable&amp;lt;object, object&amp;gt;&lt;/code&gt; instance that lives no less than the objects stored in it.&lt;/p&gt;

&lt;p&gt;Simplest example:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&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;services&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ServiceCollection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConditionalWeakTable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Registration of all types and other application service code&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;

        &lt;span class="c1"&gt;// Instance of ConditionalWeakTable that holds references to shadow objects.&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;conditionalWeakTable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetRequiredService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ConditionalWeakTable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Start application work.&lt;/span&gt;
        &lt;span class="nf"&gt;Run&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;strong&gt;Advantages of the approach as I see them:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logger is automatically bound to a specific class instance.&lt;/li&gt;
&lt;li&gt;Weak references guarantee operation without memory leaks.&lt;/li&gt;
&lt;li&gt;Centralized subscription in the DI container.&lt;/li&gt;
&lt;li&gt;Ability to flexibly extend the number of shadow services and manage them.&lt;/li&gt;
&lt;li&gt;Strong SOLID with minimal compromises.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I recommend using it for serious projects where quality architecture provides a tangible advantage.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>dotnet</category>
      <category>backend</category>
      <category>csharp</category>
    </item>
  </channel>
</rss>
