<?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: Matthias Friedrich</title>
    <description>The latest articles on DEV Community by Matthias Friedrich (@matzefriedrich).</description>
    <link>https://dev.to/matzefriedrich</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%2F3933106%2F2c96efdb-1298-4864-8499-3c3eef40d468.jpg</url>
      <title>DEV Community: Matthias Friedrich</title>
      <link>https://dev.to/matzefriedrich</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matzefriedrich"/>
    <language>en</language>
    <item>
      <title>Part 7: Testing with Confidence: Mocking and Recap</title>
      <dc:creator>Matthias Friedrich</dc:creator>
      <pubDate>Sat, 16 May 2026 00:42:11 +0000</pubDate>
      <link>https://dev.to/matzefriedrich/part-7-testing-with-confidence-mocking-and-recap-49eg</link>
      <guid>https://dev.to/matzefriedrich/part-7-testing-with-confidence-mocking-and-recap-49eg</guid>
      <description>&lt;h3&gt;
  
  
  The Role of Mocks in Unit Testing
&lt;/h3&gt;

&lt;p&gt;In the previous parts of this series, we focused on building and validating complex dependency graphs for production environments. However, a robust architecture is only half the battle; ensuring that individual components behave correctly in isolation is equally critical.&lt;/p&gt;

&lt;p&gt;Unit testing in Go often relies on &lt;strong&gt;Mock Objects&lt;/strong&gt;. Mocks allow you to replace real service implementations with controlled placeholders that simulate specific behaviors, return predetermined values, or record how they were called. This isolation is essential for testing edge cases—like network failures or database errors—without setting up expensive infrastructure.&lt;/p&gt;

&lt;p&gt;In this final part of our series, we explore how the &lt;strong&gt;Parsley CLI&lt;/strong&gt; simplifies mock generation and how you can use these mocks to write highly expressive tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Generating Mocks with Parsley CLI
&lt;/h3&gt;

&lt;p&gt;Manually writing mock implementations for every interface in your project is tedious and error-prone. Parsley addresses this by providing a dedicated &lt;code&gt;generate mocks&lt;/code&gt; command.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Interface Annotation
&lt;/h4&gt;

&lt;p&gt;To enable mock generation, add a &lt;code&gt;//go:generate&lt;/code&gt; directive to your interface definition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt;

&lt;span class="c"&gt;//go:generate parsley-cli generate mocks&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;DataService&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;FetchData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&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;h4&gt;
  
  
  Step 2: Code Generation
&lt;/h4&gt;

&lt;p&gt;When you run &lt;code&gt;go generate ./...&lt;/code&gt;, the Parsley CLI scans for the directive and generates a &lt;code&gt;mock.g.go&lt;/code&gt; file. This file contains a mock struct (e.g., &lt;code&gt;dataServiceMock&lt;/code&gt;) that implements your interface and provides hooks for behavior configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Configuring and Verifying Mocks
&lt;/h3&gt;

&lt;p&gt;The generated mocks are designed to be used as drop-in replacements in your test suites. They use a "function-override" pattern that keeps your tests clean and readable.&lt;/p&gt;

&lt;h4&gt;
  
  
  Practical Example: Testing with Mocks
&lt;/h4&gt;

&lt;p&gt;Suppose we want to test a service that depends on our &lt;code&gt;DataService&lt;/code&gt;. We can use the generated &lt;code&gt;NewDataServiceMock()&lt;/code&gt; to simulate a successful data fetch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestService_Process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// 1. Arrange: Initialize the mock and define its behavior&lt;/span&gt;
    &lt;span class="n"&gt;mock&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewDataServiceMock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FetchDataFunc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&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="s"&gt;"mock-data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// 2. Act: Pass the mock to the component under test&lt;/span&gt;
    &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewMyService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// 3. Assert: Verify expectations&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NoError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Verify that FetchData was called exactly once with argument "123"&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FunctionFetchData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TimesOnce&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
        &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"123"&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;h4&gt;
  
  
  Assertion Helpers
&lt;/h4&gt;

&lt;p&gt;Parsley provides built-in helpers to verify method invocations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Counter Helpers:&lt;/strong&gt; &lt;code&gt;TimesOnce()&lt;/code&gt;, &lt;code&gt;TimesNever()&lt;/code&gt;, &lt;code&gt;TimesExactly(n)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Argument Matchers:&lt;/strong&gt; &lt;code&gt;Exact(val)&lt;/code&gt;, &lt;code&gt;IsAny()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach allows you to assert not just that a method was called, but that it was called with the correct parameters, providing much deeper confidence in your component interactions.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Decoupling Code Generation from DI Runtime
&lt;/h3&gt;

&lt;p&gt;A key design philosophy of Parsley is flexibility. While the &lt;code&gt;Validator&lt;/code&gt; and &lt;code&gt;Resolver&lt;/code&gt; are powerful for runtime dependency injection, the CLI tools—like proxy and mock generation—can be used &lt;strong&gt;independently&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can leverage Parsley's mock generation to simplify testing even if you prefer manual dependency wiring in your production code. This makes it a versatile addition to any Go developer's toolkit, regardless of their choice of DI framework.&lt;/p&gt;




&lt;h3&gt;
  
  
  Series Recap: Mastering Dependency Injection
&lt;/h3&gt;

&lt;p&gt;We have reached the end of our journey. Over eight articles, we have transformed a basic Go application into a modular, validated, and testable system.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;a href="https://dev.to/matzefriedrich/part-0-the-case-for-dependency-injection-in-go-4kl3"&gt;The Case for Dependency Injection in Go&lt;/a&gt;:&lt;/strong&gt; Established the theoretical foundation and addressed common misconceptions.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;a href="https://dev.to/matzefriedrich/part-1-mastering-dependency-injection-in-go-a-quick-start-guide-2igh"&gt;Introduction and Quick Start&lt;/a&gt;:&lt;/strong&gt; Explored the core concepts of IoC and set up our first registry.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;a href="https://dev.to/matzefriedrich/part-2-mastering-service-registration-in-go-with-parsley-16dh"&gt;Service Registration Fundamentals&lt;/a&gt;:&lt;/strong&gt; Learned how to register constructors and pre-existing instances.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;a href="https://dev.to/matzefriedrich/part-3-mastering-lifetimes-and-scopes-in-go-with-parsley-482c"&gt;Understanding Lifetimes and Scopes&lt;/a&gt;:&lt;/strong&gt; Mastered Transient, Scoped, and Singleton behaviors.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;a href="https://dev.to/matzefriedrich/part-4-advanced-registration-patterns-in-go-with-parsley-3563"&gt;Advanced Registration Patterns&lt;/a&gt;:&lt;/strong&gt; Organized our code using Modules, Factory Functions, and Named Services.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;a href="https://dev.to/matzefriedrich/part-5-mastering-dependency-resolution-in-go-with-parsley-9e4"&gt;Mastering Dependency Resolution&lt;/a&gt;:&lt;/strong&gt; Dived into Lazy Proxies, Service Lists, and Dynamic Overrides.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;a href="https://dev.to/matzefriedrich/part-6-ensuring-reliability-validation-and-proxies-with-parsley-32fo"&gt;Ensuring Reliability: Validation and Proxies&lt;/a&gt;:&lt;/strong&gt; Caught configuration errors early and separated cross-cutting concerns.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Testing with Confidence:&lt;/strong&gt; (This article) Simplified isolation testing with generated mocks.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Join the Community
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/matzefriedrich/parsley" rel="noopener noreferrer"&gt;Parsley&lt;/a&gt; is an open-source project driven by the community. If you found this series helpful, there are several ways you can support the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leave a Star:&lt;/strong&gt; If you use Parsley or appreciate the design, head over to the &lt;a href="https://github.com/matzefriedrich/parsley" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; and leave a star. It helps other developers discover the library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spread the Word:&lt;/strong&gt; Share your experience with Parsley on social media or with your team.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contribute:&lt;/strong&gt; Whether it's reporting a bug, improving the documentation, or proposing a new feature, your contributions are always welcome.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for following along with this series. I hope Parsley helps you build cleaner, more maintainable Go applications!&lt;/p&gt;

&lt;h4&gt;
  
  
  Next Steps
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Try generating a mock for one of your interfaces today.&lt;/li&gt;
&lt;li&gt;Explore the &lt;a href="https://matzefriedrich.github.io/parsley-docs/advanced-features/generate-mocks/" rel="noopener noreferrer"&gt;Mocking Made Easy&lt;/a&gt; documentation for advanced verification patterns.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>go</category>
      <category>testing</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Part 6: Ensuring Reliability - Validation and Proxies with Parsley</title>
      <dc:creator>Matthias Friedrich</dc:creator>
      <pubDate>Sat, 16 May 2026 00:35:03 +0000</pubDate>
      <link>https://dev.to/matzefriedrich/part-6-ensuring-reliability-validation-and-proxies-with-parsley-32fo</link>
      <guid>https://dev.to/matzefriedrich/part-6-ensuring-reliability-validation-and-proxies-with-parsley-32fo</guid>
      <description>&lt;h3&gt;
  
  
  The Challenge of Complexity
&lt;/h3&gt;

&lt;p&gt;As your dependency graph grows, so does the risk of subtle configuration errors. A missing registration or a circular dependency might remain hidden during development, only to manifest as a runtime failure in production. Furthermore, as you add cross-cutting concerns like logging, metrics, or auditing, your core business logic often becomes cluttered with boilerplate code that has nothing to do with the actual domain.&lt;/p&gt;

&lt;p&gt;In our previous article, &lt;a href="https://dev.to/matzefriedrich/part-5-mastering-dependency-resolution-in-go-with-parsley-9e4"&gt;Part 5: Mastering Dependency Resolution in Go with Parsley&lt;/a&gt;, we explored advanced ways to resolve services. In this sixth part, we shift our focus to &lt;strong&gt;Reliability and Maintainability&lt;/strong&gt;. We will learn how to use Parsley's &lt;code&gt;Validator&lt;/code&gt; to catch configuration issues before your application starts and how to use &lt;strong&gt;Generated Proxies&lt;/strong&gt; to cleanly separate cross-cutting concerns from your business logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Validator: Catching Errors Early
&lt;/h3&gt;

&lt;p&gt;The Parsley &lt;code&gt;Validator&lt;/code&gt; is a specialized service designed to inspect your &lt;code&gt;ServiceRegistry&lt;/code&gt; and identify structural flaws in your dependency graph. It specifically targets two common pitfalls: &lt;strong&gt;Missing Dependencies&lt;/strong&gt; and &lt;strong&gt;Circular Dependencies&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Missing Dependencies
&lt;/h4&gt;

&lt;p&gt;A missing dependency occurs when a service's constructor requires another service that hasn't been registered. Without validation, Parsley will attempt to resolve the dependency at runtime and fail, potentially causing a panic if not handled gracefully.&lt;/p&gt;

&lt;h4&gt;
  
  
  Circular Dependencies
&lt;/h4&gt;

&lt;p&gt;A circular dependency happens when two or more services depend on each other (e.g., A needs B, and B needs A). This creates an infinite loop during resolution, leading to a stack overflow. While Parsley's resolver has runtime checks for this, the &lt;code&gt;Validator&lt;/code&gt; provides much better error messaging and allows you to catch the loop during startup or even in a unit test.&lt;/p&gt;

&lt;h4&gt;
  
  
  Practical Example: Validating at Startup
&lt;/h4&gt;

&lt;p&gt;The best practice is to run the validator immediately after completing your service registrations in &lt;code&gt;main.go&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServiceRegistry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// ... Register your services and modules ...&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Validate the complete registry&lt;/span&gt;
    &lt;span class="n"&gt;registryValidator&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServiceRegistrationsValidator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;registryValidator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Fail fast: prevent the application from starting with a broken graph&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Service registration failed: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Proceed to create the resolver&lt;/span&gt;
    &lt;span class="n"&gt;resolver&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewResolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; For large projects, consider writing a unit test that calls &lt;code&gt;Validate(registry)&lt;/code&gt; on your production modules. This ensures that every pull request is checked for dependency integrity before it even reaches the CI environment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Generated Proxies: Intercepting Method Calls
&lt;/h3&gt;

&lt;p&gt;Separation of Concerns is a fundamental principle of clean architecture. However, implementing features like logging or performance tracing often requires modifying every service method, leading to "code rot."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/matzefriedrich/parsley" rel="noopener noreferrer"&gt;Parsley&lt;/a&gt; addresses this through &lt;strong&gt;Generated Proxies&lt;/strong&gt; and &lt;strong&gt;Method Interceptors&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Architecture of Interception
&lt;/h4&gt;

&lt;p&gt;By using the Parsley CLI, you can generate a "Proxy" for any interface. This proxy implements the same interface as your service but wraps the actual implementation. It provides hooks that allow &lt;code&gt;MethodInterceptor&lt;/code&gt; services to execute logic before (&lt;strong&gt;Enter&lt;/strong&gt;), after (&lt;strong&gt;Exit&lt;/strong&gt;), or when an error occurs (&lt;strong&gt;OnError&lt;/strong&gt;) during a method call.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Annotating and Generating
&lt;/h4&gt;

&lt;p&gt;To enable proxy generation, add the &lt;code&gt;//go:generate&lt;/code&gt; annotation to your interface definition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;//go:generate parsley-cli generate proxy&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Greeter&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;SayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&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;Running &lt;code&gt;go generate ./...&lt;/code&gt; will invoke the &lt;code&gt;parsley-cli&lt;/code&gt; and produce a &lt;code&gt;greeter.proxy.g.go&lt;/code&gt; file containing the &lt;code&gt;GreeterProxy&lt;/code&gt; interface and its implementation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Implementing an Interceptor
&lt;/h4&gt;

&lt;p&gt;An interceptor is a standard Go service that implements the &lt;code&gt;features.MethodInterceptor&lt;/code&gt; interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;loggingInterceptor&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InterceptorBase&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;loggingInterceptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParameterInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Entering method: %s with params: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;loggingInterceptor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methodName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReturnValueInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Exiting method: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methodName&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;h4&gt;
  
  
  Step 3: Wiring it Together
&lt;/h4&gt;

&lt;p&gt;You register the actual implementation, the generated proxy, and the list of interceptors in your registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewGreeter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LifetimeTransient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewGreeterProxyImpl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LifetimeTransient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Enable list resolution for interceptors&lt;/span&gt;
&lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MethodInterceptor&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newLoggingInterceptor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LifetimeSingleton&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you resolve &lt;code&gt;GreeterProxy&lt;/code&gt;, Parsley injects the real &lt;code&gt;Greeter&lt;/code&gt; and all registered interceptors into the proxy. Your business logic remains untouched, yet it is now fully instrumented with logging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational Considerations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Performance of Proxies
&lt;/h4&gt;

&lt;p&gt;Generated proxies use reflection-based parameter mapping to provide context to interceptors. While highly optimized, this does introduce a small overhead compared to direct method calls. For high-frequency, low-latency hot paths, evaluate if the benefits of separation outweigh the performance cost.&lt;/p&gt;

&lt;h4&gt;
  
  
  Validator in CI/CD
&lt;/h4&gt;

&lt;p&gt;Running the validator is idempotent and fast. It is highly recommended to include a "sanity check" test in your CI pipeline that initializes your production &lt;code&gt;ServiceRegistry&lt;/code&gt; and runs the &lt;code&gt;Validator&lt;/code&gt;. This prevents "silent" failures where an application builds successfully but fails to start due to a missing environment-specific registration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tradeoffs and Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Binary Size:&lt;/strong&gt; Generating proxies for every service in a large application will increase your binary size. Only generate proxies for services where cross-cutting concerns are actually needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity of Interceptors:&lt;/strong&gt; Interceptors have access to method parameters as &lt;code&gt;any&lt;/code&gt; types. While flexible, this requires careful handling (and potentially type assertions) if you need to inspect specific values, which can lead to less type-safe code if overused.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Reliability isn't just about writing bug-free code; it's about building systems that are easy to verify and maintain.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Validator&lt;/strong&gt; provides a safety net, ensuring your dependency graph is sound before your application handles its first request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generated Proxies&lt;/strong&gt; allow you to implement powerful, reusable cross-cutting concerns without polluting your core domain logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next part of this series, we will dive into &lt;strong&gt;Effective Mocking for Testing&lt;/strong&gt;, where we show how the Parsley CLI can simplify your unit testing workflow by generating sophisticated mocks.&lt;/p&gt;

&lt;h4&gt;
  
  
  Next Steps
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Install the Parsley CLI: &lt;code&gt;go install github.com/matzefriedrich/parsley/cmd/parsley-cli&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;Validator&lt;/code&gt; call to your application's startup sequence.&lt;/li&gt;
&lt;li&gt;Explore the &lt;a href="https://matzefriedrich.github.io/parsley-docs/advanced-features/service-registration-validation/" rel="noopener noreferrer"&gt;Validating Service Registrations&lt;/a&gt; documentation for more details.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>go</category>
      <category>softwareengineering</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Part 5: Mastering Dependency Resolution in Go with Parsley</title>
      <dc:creator>Matthias Friedrich</dc:creator>
      <pubDate>Sat, 16 May 2026 00:27:58 +0000</pubDate>
      <link>https://dev.to/matzefriedrich/part-5-mastering-dependency-resolution-in-go-with-parsley-9e4</link>
      <guid>https://dev.to/matzefriedrich/part-5-mastering-dependency-resolution-in-go-with-parsley-9e4</guid>
      <description>&lt;h3&gt;
  
  
  Navigating Complex Dependency Graphs
&lt;/h3&gt;

&lt;p&gt;As applications evolve, so does the complexity of their dependency graphs. A simple "register and resolve" pattern is often sufficient for small projects, but production-grade systems frequently encounter scenarios that require more granular control over the resolution process.&lt;/p&gt;

&lt;p&gt;You might have a service that is expensive to initialize and only needed in rare edge cases. You might need to aggregate results from multiple implementations of the same interface. Or perhaps you need to override a specific dependency at runtime for a specialized task.&lt;/p&gt;

&lt;p&gt;In this fifth part of our series, we dive into &lt;strong&gt;Advanced Dependency Resolution Techniques&lt;/strong&gt;. Building on our knowledge of &lt;a href="https://dev.to/matzefriedrich/part-4-advanced-registration-patterns-in-go-with-parsley-3563"&gt;Part 4: Advanced Registration Patterns in Go with Parsley&lt;/a&gt;, we will explore how &lt;a href="https://github.com/matzefriedrich/parsley" rel="noopener noreferrer"&gt;Parsley&lt;/a&gt; handles lazy loading, service lists, and manual dependency provision.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Lazy Proxies: Deferring Heavy Initialization
&lt;/h3&gt;

&lt;p&gt;In a standard DI container, resolving a service usually triggers the activation of its entire dependency tree. For resource-intensive services—such as those establishing multiple network connections or performing heavy disk I/O—this can lead to unnecessary overhead if the service isn't actually used during a specific execution path.&lt;/p&gt;

&lt;p&gt;Parsley solves this with &lt;strong&gt;Lazy Proxies&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  How it Works
&lt;/h4&gt;

&lt;p&gt;A lazy proxy acts as a lightweight placeholder. When you resolve a lazy service, Parsley returns a &lt;code&gt;features.Lazy[T]&lt;/code&gt; instance instead of the actual service. The real service is only activated when you explicitly call its &lt;code&gt;Value(ctx)&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Register a service with a lazy proxy&lt;/span&gt;
&lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterLazy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Greeter&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewGreeter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LifetimeTransient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Resolve the proxy&lt;/span&gt;
&lt;span class="n"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResolveRequiredService&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Lazy&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Greeter&lt;/span&gt;&lt;span class="p"&gt;]](&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// The actual NewGreeter constructor is only called here:&lt;/span&gt;
&lt;span class="n"&gt;greeter&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;lazy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once activated, the proxy caches the instance. Subsequent calls to &lt;code&gt;Value()&lt;/code&gt; return the same object, ensuring consistent behavior while optimizing resource usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Service Lists: Managing Multiple Implementations
&lt;/h3&gt;

&lt;p&gt;In modular architectures, it is common to have multiple implementations of a single interface. Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A data aggregator that fetches from multiple storage backends.&lt;/li&gt;
&lt;li&gt;A validation pipeline that runs several independent checks.&lt;/li&gt;
&lt;li&gt;A plugin system where different modules contribute to a core process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While you can resolve these services individually by name (as seen in Part 4), Parsley's &lt;code&gt;RegisterList[T]&lt;/code&gt; provides a more ergonomic way to inject all implementations as a single slice.&lt;/p&gt;

&lt;h4&gt;
  
  
  Practical Example: The Aggregator Pattern
&lt;/h4&gt;

&lt;p&gt;Suppose we have multiple &lt;code&gt;DataService&lt;/code&gt; implementations. We can group them into a list and inject them into an aggregator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServiceRegistry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Register individual implementations&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewLocalDataService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LifetimeTransient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewRemoteDataService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LifetimeTransient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Group all DataService registrations into a list&lt;/span&gt;
    &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DataService&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Register the aggregator that expects a []DataService slice&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newAggregator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LifetimeTransient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;aggregator&lt;/span&gt; &lt;span class="k"&gt;struct&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;DataService&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;newAggregator&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;DataService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;aggregator&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;aggregator&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="o"&gt;:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using &lt;code&gt;RegisterList&lt;/code&gt;, the &lt;code&gt;aggregator&lt;/code&gt; is automatically provided with every registered implementation of &lt;code&gt;DataService&lt;/code&gt;. This allows you to add new implementations to your application without modifying the aggregator's code—a perfect example of the &lt;strong&gt;Open-Closed Principle&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Dynamic Overrides with &lt;code&gt;ResolveWithOptions&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Sometimes, you need to provide a specific instance to the resolver that wasn't registered in the container, or you want to temporarily override a registered dependency for a single resolution call. This is particularly useful for passing runtime configurations or injecting mock objects during testing.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ResolveWithOptions&lt;/code&gt; method allows you to pass "hints" to the resolver via the &lt;code&gt;WithInstance&lt;/code&gt; option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Create a specific transport instance at runtime&lt;/span&gt;
&lt;span class="n"&gt;customTransport&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Resolve a client, but force it to use our custom transport instance&lt;/span&gt;
&lt;span class="n"&gt;resolveType&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MakeServiceType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Client&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;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResolveWithOptions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolveType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithInstance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;customTransport&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This technique ensures that the resolved &lt;code&gt;Client&lt;/code&gt; uses the provided &lt;code&gt;customTransport&lt;/code&gt;, even if a different transport was previously registered in the &lt;code&gt;ServiceRegistry&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational Considerations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Performance vs. Complexity
&lt;/h4&gt;

&lt;p&gt;Lazy proxies improve startup time and reduce memory footprint for unused services. However, they add a layer of indirection. Use them for truly "heavy" services rather than every dependency.&lt;/p&gt;

&lt;h4&gt;
  
  
  Slice Injection and Order
&lt;/h4&gt;

&lt;p&gt;When using &lt;code&gt;RegisterList&lt;/code&gt;, the order of services in the slice typically matches the order of registration. If your application logic depends on a specific order (e.g., a middleware chain), ensure your registration sequence reflects this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tradeoffs and Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lazy Proxy Indirection:&lt;/strong&gt; Every access to the service through a lazy proxy involves a call to &lt;code&gt;Value(ctx)&lt;/code&gt;. While the overhead is minimal after activation, it is a non-zero cost compared to direct injection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Safety in Options:&lt;/strong&gt; &lt;code&gt;ResolveWithOptions&lt;/code&gt; returns an &lt;code&gt;any&lt;/code&gt; type, requiring a runtime type assertion. Additionally, &lt;code&gt;WithInstance&lt;/code&gt; must match the exact type expected by the constructor. If a constructor expects an interface and you provide a concrete pointer, the resolution may fail if the types are not perfectly aligned with Parsley's internal reflection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service List Exclusivity:&lt;/strong&gt; &lt;code&gt;RegisterList&lt;/code&gt; is primarily designed for injecting all implementations. If you only need a subset of implementations, you should continue using &lt;strong&gt;Named Services&lt;/strong&gt; or custom factory functions.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Advanced resolution techniques provide the flexibility needed to handle the realities of production software. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lazy Proxies&lt;/strong&gt; defer resource consumption until necessary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Service Lists&lt;/strong&gt; enable powerful aggregation and plugin patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Overrides&lt;/strong&gt; give you precise control over dependency injection at runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next part of this series, we will look at &lt;strong&gt;Reliability and Validation&lt;/strong&gt;, exploring how Parsley's built-in validator can catch configuration errors before your application even starts.&lt;/p&gt;

&lt;h4&gt;
  
  
  Next Steps
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Identify a resource-intensive service in your app and experiment with &lt;code&gt;RegisterLazy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;RegisterList&lt;/code&gt; to implement a simple plugin or strategy pattern.&lt;/li&gt;
&lt;li&gt;Check the &lt;a href="https://matzefriedrich.github.io/parsley-docs/registration/register-lists/" rel="noopener noreferrer"&gt;Service Lists&lt;/a&gt; documentation for more advanced use cases.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>go</category>
      <category>softwareengineering</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Part 4: Advanced Registration Patterns in Go with Parsley</title>
      <dc:creator>Matthias Friedrich</dc:creator>
      <pubDate>Sat, 16 May 2026 00:19:03 +0000</pubDate>
      <link>https://dev.to/matzefriedrich/part-4-advanced-registration-patterns-in-go-with-parsley-3563</link>
      <guid>https://dev.to/matzefriedrich/part-4-advanced-registration-patterns-in-go-with-parsley-3563</guid>
      <description>&lt;h3&gt;
  
  
  Scaling Beyond Simple Registrations
&lt;/h3&gt;

&lt;p&gt;In the previous parts of this series, we explored the basics of service registration and the critical role of lifetimes and scopes. As your Go application grows from a handful of services to dozens or even hundreds, managing all registrations in a single &lt;code&gt;main.go&lt;/code&gt; function becomes impractical. It leads to "god files" that are difficult to navigate and maintain.&lt;/p&gt;

&lt;p&gt;Furthermore, real-world applications often require more than just static wiring. You might need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group related services into logical, reusable units.&lt;/li&gt;
&lt;li&gt;Pass runtime configuration parameters to services during initialization.&lt;/li&gt;
&lt;li&gt;Manage multiple implementations of the same interface (e.g., different storage backends).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we will explore &lt;strong&gt;Advanced Registration Patterns&lt;/strong&gt; in &lt;a href="https://github.com/matzefriedrich/parsley" rel="noopener noreferrer"&gt;Parsley&lt;/a&gt; that address these challenges: &lt;strong&gt;Modules&lt;/strong&gt;, &lt;strong&gt;Factory Functions&lt;/strong&gt;, and &lt;strong&gt;Named Services&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Service Modules: Organizing for Growth
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://matzefriedrich.github.io/parsley-docs/registration/register-module/" rel="noopener noreferrer"&gt;Parsley Modules&lt;/a&gt; provide a structured way to group related service registrations. Instead of cluttering your entry point, you can encapsulate the registration logic for a specific feature or package within a module function.&lt;/p&gt;

&lt;h4&gt;
  
  
  Architecture: The Module Pattern
&lt;/h4&gt;

&lt;p&gt;A Parsley module is simply a function that accepts a &lt;code&gt;types.ServiceRegistry&lt;/code&gt; and returns an &lt;code&gt;error&lt;/code&gt;. This approach allows you to keep implementation types private while exposing only the interfaces and the registration module.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;greeterModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ServiceRegistry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Register related services here&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NewGreeter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LifetimeTransient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Practical Example: Registering a Module
&lt;/h4&gt;

&lt;p&gt;You integrate a module into your registry using the &lt;code&gt;RegisterModule&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/matzefriedrich/parsley/pkg/registration"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/matzefriedrich/parsley/pkg/types"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServiceRegistry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Group registrations into a logical unit&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Conditional Registration
&lt;/h4&gt;

&lt;p&gt;Parsley also supports &lt;code&gt;RegisterModuleIf&lt;/code&gt;, allowing you to enable or disable entire sets of services based on environment variables or configuration flags—ideal for feature flagging or environment-specific mocks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Register the DebugModule only in development&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterModuleIf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ENV"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DebugModule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Factory Functions: Dynamic Configuration
&lt;/h3&gt;

&lt;p&gt;Standard constructor functions are excellent for static dependency wiring. However, sometimes you need to inject values that aren't known until registration time, such as a specific API endpoint or a localized salutation.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Pattern: Functions Returning Constructors
&lt;/h4&gt;

&lt;p&gt;In Parsley, a "Factory Function" is a pattern where you create a function that returns a constructor. This allows you to "bake in" configuration values via closures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewGreeterFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;salutation&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;Greeter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;Greeter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;salutation&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;salutation&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;h4&gt;
  
  
  Registration and Usage
&lt;/h4&gt;

&lt;p&gt;When you register the result of this factory, Parsley treats the returned anonymous function as the actual service constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Register the greeter with a specific salutation&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterTransient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewGreeterFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hi"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Named Services: Managing Multiple Implementations
&lt;/h3&gt;

&lt;p&gt;A common architectural requirement is having multiple implementations of the same interface coexist. For example, you might have a &lt;code&gt;DataService&lt;/code&gt; that reads from a local cache and another that fetches from a remote API.&lt;/p&gt;

&lt;h4&gt;
  
  
  Registering Named Services
&lt;/h4&gt;

&lt;p&gt;You can associate a unique name with each implementation using &lt;code&gt;RegisterNamed&lt;/code&gt;. This allows you to specify different implementations and even different lifetimes for each.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterNamed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;DataService&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NamedServiceRegistration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;NewRemoteDataService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LifetimeTransient&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NamedServiceRegistration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;NewLocalDataService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;types&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LifetimeTransient&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Resolving via Service Factory
&lt;/h4&gt;

&lt;p&gt;To resolve a specific named implementation, Parsley provides a powerful "Service Factory" resolution pattern. You resolve a function that takes a &lt;code&gt;string&lt;/code&gt; (the name) and returns the service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Resolve the factory function&lt;/span&gt;
&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResolveRequiredService&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DataService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)](&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;// Request the specific implementation by name&lt;/span&gt;
&lt;span class="n"&gt;remoteService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;localService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"local"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Implementation types registered with names are also automatically available as a list. We will explore &lt;strong&gt;Service Lists&lt;/strong&gt; in detail in the next part of this series.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Operational Considerations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Encapsulation and Security
&lt;/h4&gt;

&lt;p&gt;By using modules, you can keep implementation structs unexported in your packages. This enforces the use of interfaces and prevents developers from bypassing the DI container to instantiate services manually, leading to a more consistent architecture.&lt;/p&gt;

&lt;h4&gt;
  
  
  Separation of Concerns
&lt;/h4&gt;

&lt;p&gt;Advanced registration patterns help separate your &lt;strong&gt;Application Logic&lt;/strong&gt; from your &lt;strong&gt;Wiring Logic&lt;/strong&gt;. Your services remain clean and unaware of how they are being registered or grouped, making them more portable and easier to test in isolation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tradeoffs and Limitations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; While named services and factory functions offer great flexibility, they can make the dependency graph harder to visualize. Use them sparingly for clear use cases rather than as a default for every service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Safety:&lt;/strong&gt; When resolving via the service factory &lt;code&gt;func(string) (T, error)&lt;/code&gt;, passing an incorrect name string will only be caught at runtime. Ensure you have proper error handling or use constants for service names.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Advanced registration patterns transform Parsley from a simple DI container into a robust tool for managing complex application architectures. Modules provide organization, factory functions enable dynamic configuration, and named services offer the flexibility to manage multiple implementations of the same contract.&lt;/p&gt;

&lt;p&gt;In the next part of this series, we will explore &lt;strong&gt;Dependency Resolution Techniques&lt;/strong&gt;, where we dive into lazy loading, resolving lists of services, and providing manual dependencies during resolution.&lt;/p&gt;

&lt;h4&gt;
  
  
  Next Steps
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Implement a module in your current project to group related services.&lt;/li&gt;
&lt;li&gt;Try using a factory function to inject a configuration value into a service.&lt;/li&gt;
&lt;li&gt;Explore the &lt;a href="https://matzefriedrich.github.io/parsley-docs/registration/register-module/" rel="noopener noreferrer"&gt;Register Module&lt;/a&gt; documentation for more advanced examples.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>go</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Part 3: Mastering Lifetimes and Scopes in Go with Parsley</title>
      <dc:creator>Matthias Friedrich</dc:creator>
      <pubDate>Sat, 16 May 2026 00:10:22 +0000</pubDate>
      <link>https://dev.to/matzefriedrich/part-3-mastering-lifetimes-and-scopes-in-go-with-parsley-482c</link>
      <guid>https://dev.to/matzefriedrich/part-3-mastering-lifetimes-and-scopes-in-go-with-parsley-482c</guid>
      <description>&lt;h3&gt;
  
  
  Managing State and Lifecycle in Distributed Systems
&lt;/h3&gt;

&lt;p&gt;In modern Go backend development, managing the lifecycle of a service is as critical as its implementation. For instance, a database connection pool should ideally be shared across the entire application to minimize overhead and maintain connection limits. Conversely, an authentication context or a request-specific logger should only exist for the duration of a single HTTP request to avoid state leakage between users.&lt;/p&gt;

&lt;p&gt;In the previous article, &lt;a href="https://dev.to/matzefriedrich/part-2-mastering-service-registration-in-go-with-parsley-16dh"&gt;Part 2: Mastering Service Registration in Go with Parsley&lt;/a&gt;, we discussed how to register services using constructors and pre-existing instances. However, registration is only half the story. The other half is determining &lt;em&gt;when&lt;/em&gt; these services are created and &lt;em&gt;how long&lt;/em&gt; they persist.&lt;/p&gt;

&lt;p&gt;Without a structured dependency injection container, developers often end up passing &lt;code&gt;context.Context&lt;/code&gt; everywhere or manually managing global variables—both of which increase complexity and hinder testability. Parsley addresses this by providing explicit control over service lifetimes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recap: Singleton vs. Registered Instance
&lt;/h3&gt;

&lt;p&gt;In Part 2, we saw two ways to achieve singleton behavior, but they differ in one key aspect: &lt;strong&gt;Activation Timing&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Registered Instance (&lt;code&gt;RegisterInstance&lt;/code&gt;):&lt;/strong&gt; This is an &lt;strong&gt;eager singleton&lt;/strong&gt;. You create the object yourself before registration. Parsley simply stores and provides the pre-existing instance. This is useful for third-party clients or legacy objects.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Singleton Registration (&lt;code&gt;RegisterSingleton&lt;/code&gt;):&lt;/strong&gt; This is a &lt;strong&gt;lazy singleton&lt;/strong&gt;. You provide a constructor function, and Parsley only calls it the first time the service is requested.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Lazy initialization is generally preferred. It reduces application startup time and ensures that heavy services are only created if they are actually needed by the current execution path.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Three Pillars of Parsley Lifetimes
&lt;/h3&gt;

&lt;p&gt;Parsley provides three distinct lifetime behaviors to manage your services effectively:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Transient:&lt;/strong&gt; A new instance is created every time the service is requested. This is ideal for lightweight, stateless objects where you want to ensure a clean state for every consumer.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Scoped:&lt;/strong&gt; A single instance is created and reused within a specific scope. In a web server, this usually corresponds to a single HTTP request.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Singleton:&lt;/strong&gt; A single instance is created once and shared throughout the entire application for the lifetime of the resolver.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Architecture: The Role of Scoped Context
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Scoped&lt;/strong&gt; lifetime is the backbone of request-based architectures. It ensures that all dependencies within a single request—such as a database transaction, a user service, and an audit logger—share the same instance of a request-specific resource.&lt;/p&gt;

&lt;p&gt;Parsley manages this by attaching instances to a &lt;code&gt;context.Context&lt;/code&gt;. When you create a new scope using &lt;code&gt;resolving.NewScopedContext(ctx)&lt;/code&gt;, Parsley creates a specialized context that acts as a container for that specific lifecycle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Practical Example: Observing Service Activation
&lt;/h3&gt;

&lt;p&gt;To demonstrate how lifetimes affect your application, let's create a &lt;code&gt;Greeter&lt;/code&gt; service and observe how many times it is instantiated under different scopes.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Define the Service
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Greeter&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;SayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;greeter&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;SayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, %s! (Instance ID: %s)&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewGreeter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;Greeter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="c"&gt;// Use the pointer address as a simple unique ID&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%p"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"New Greeter instance activated: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&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;g&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Resolve with Scopes
&lt;/h4&gt;

&lt;p&gt;In the following example, we register the &lt;code&gt;Greeter&lt;/code&gt; as &lt;strong&gt;Scoped&lt;/strong&gt;. We then resolve it multiple times within two different scopes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/matzefriedrich/parsley/pkg/registration"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/matzefriedrich/parsley/pkg/resolving"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServiceRegistry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Register the constructor with a Scoped lifetime&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterScoped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewGreeter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;resolver&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewResolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// --- Scope A ---&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"--- Starting Scope A ---"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;scopeA&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewScopedContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;g1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResolveRequiredService&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Greeter&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;scopeA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;g1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;g2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResolveRequiredService&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Greeter&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;scopeA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;g2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// --- Scope B ---&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- Starting Scope B ---"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;scopeB&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewScopedContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;g3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResolveRequiredService&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Greeter&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;scopeB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;g3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SayHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Charlie"&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;&lt;strong&gt;Output Analysis:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--- Starting Scope A ---
New Greeter instance activated: 0xc0000120a8
Hello, Alice! (Instance ID: 0xc0000120a8)
Hello, Bob! (Instance ID: 0xc0000120a8)

--- Starting Scope B ---
New Greeter instance activated: 0xc0000120b0
Hello, Charlie! (Instance ID: 0xc0000120b0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within &lt;strong&gt;Scope A&lt;/strong&gt;, the instance is reused for both "Alice" and "Bob". When we switch to &lt;strong&gt;Scope B&lt;/strong&gt;, Parsley detects a new context and activates a fresh instance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational Considerations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  When to Use Transient?
&lt;/h4&gt;

&lt;p&gt;Use transient for services that carry no internal state or are cheap to create. This prevents accidental state leakage between different parts of your application and is the safest default if you are unsure.&lt;/p&gt;

&lt;h4&gt;
  
  
  When to Use Scoped?
&lt;/h4&gt;

&lt;p&gt;Scoped is the "sweet spot" for backend services. Use it for anything that should be consistent across a single request but isolated from other requests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database transactions&lt;/li&gt;
&lt;li&gt;Request-specific loggers with correlation IDs&lt;/li&gt;
&lt;li&gt;User identity providers&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  When to Use Singleton?
&lt;/h4&gt;

&lt;p&gt;Use singletons for heavy resources that should persist for the life of the process. Examples include database connection pools (&lt;code&gt;*sql.DB&lt;/code&gt;), configuration managers, and external API clients that manage their own internal connection pooling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tradeoffs and Limitations
&lt;/h3&gt;

&lt;p&gt;While automated lifetime management reduces boilerplate, it introduces specific engineering responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context Hygiene:&lt;/strong&gt; You must ensure that &lt;code&gt;resolving.NewScopedContext&lt;/code&gt; is called at the correct entry points (e.g., in a middleware). If you inadvertently use the root context for all resolutions, your scoped services will effectively behave as singletons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Management:&lt;/strong&gt; Singletons stay in memory until the &lt;code&gt;Resolver&lt;/code&gt; is destroyed. Be cautious about registering large objects as singletons if they are only needed occasionally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thread Safety:&lt;/strong&gt; Singleton and Scoped services might be accessed by multiple goroutines concurrently (especially in high-concurrency web servers). Ensure your implementations are thread-safe.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Understanding lifetimes and scopes is essential for building scalable Go applications. By leveraging Transient, Scoped, and Singleton behaviors, you can ensure that your services are created at the right time and shared only when appropriate, leading to cleaner code and more predictable resource usage.&lt;/p&gt;

&lt;p&gt;In the next part of this series, we will explore &lt;strong&gt;Advanced Registration Patterns&lt;/strong&gt;, where we discuss how to organize your services into modules and use custom factory functions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Next Steps
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Explore the &lt;a href="https://matzefriedrich.github.io/parsley-docs/resolving/lifetime-scopes/" rel="noopener noreferrer"&gt;Lifetime Scopes&lt;/a&gt; documentation for more details.&lt;/li&gt;
&lt;li&gt;Review your application's &lt;code&gt;main.go&lt;/code&gt; and determine which services could benefit from a Scoped lifetime.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>backend</category>
      <category>distributedsystems</category>
      <category>go</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Part 2: Mastering Service Registration in Go with Parsley</title>
      <dc:creator>Matthias Friedrich</dc:creator>
      <pubDate>Sat, 16 May 2026 00:01:43 +0000</pubDate>
      <link>https://dev.to/matzefriedrich/part-2-mastering-service-registration-in-go-with-parsley-16dh</link>
      <guid>https://dev.to/matzefriedrich/part-2-mastering-service-registration-in-go-with-parsley-16dh</guid>
      <description>&lt;h3&gt;
  
  
  The Challenge of Scalable Service Wiring
&lt;/h3&gt;

&lt;p&gt;In the previous article, &lt;a href="https://dev.to/matzefriedrich/part-1-mastering-dependency-injection-in-go-a-quick-start-guide-2igh"&gt;Part 1: Mastering Dependency Injection in Go: A Quick Start Guide&lt;/a&gt;, we explored how Parsley simplifies the instantiation of services. However, as an application grows, the dependency graph becomes more intricate. You no longer just have standalone services; you have services that depend on database pools, configuration providers, and external API clients.&lt;/p&gt;

&lt;p&gt;Manually wiring these dependencies in a factory function is error-prone. If a constructor requires five different dependencies, you must ensure each is correctly initialized and passed in the right order. Furthermore, handling errors during this initialization often leads to nested &lt;code&gt;if&lt;/code&gt; statements that clutter your startup logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing Parsley’s Registration System
&lt;/h3&gt;

&lt;p&gt;Parsley’s registration system is designed to handle this complexity by leveraging Go’s reflection capabilities. Instead of you calling the constructor, you tell Parsley &lt;em&gt;about&lt;/em&gt; the constructor. &lt;a href="https://github.com/matzefriedrich/parsley" rel="noopener noreferrer"&gt;Parsley&lt;/a&gt; then takes responsibility for resolving the arguments and invoking the function.&lt;/p&gt;

&lt;p&gt;This approach centers around two primary registration methods:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Constructor Functions:&lt;/strong&gt; For services that Parsley should instantiate.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Pre-existing Instances:&lt;/strong&gt; For objects already created outside the container.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Architecture: How Parsley Sees Your Services
&lt;/h3&gt;

&lt;p&gt;When you register a function, Parsley inspects its signature at runtime. It identifies the return type as the service being provided and the input parameters as the required dependencies.&lt;/p&gt;

&lt;h4&gt;
  
  
  Constructor Function Signatures
&lt;/h4&gt;

&lt;p&gt;Parsley supports several idiomatic Go patterns for constructors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard:&lt;/strong&gt; &lt;code&gt;func(...) T&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error-aware:&lt;/strong&gt; &lt;code&gt;func(...) (T, error)&lt;/code&gt; — Parsley propagates the error during resolution if the constructor fails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context-aware:&lt;/strong&gt; &lt;code&gt;func(context.Context, ...) (T, error)&lt;/code&gt; — Useful for accessing the resolution context or handling initialization timeouts.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When using &lt;code&gt;context.Context&lt;/code&gt;, it must be the first parameter in the function signature.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Practical Example: Wiring a Data Layer
&lt;/h3&gt;

&lt;p&gt;Let's look at a realistic scenario where a &lt;code&gt;UserService&lt;/code&gt; depends on a &lt;code&gt;Repository&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Define the Components
&lt;/h4&gt;

&lt;p&gt;We define the contracts as interfaces. This allows us to swap implementations (e.g., switching from a SQL database to a mock for testing) without changing the business logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Repository&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;FindUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;GetProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Profile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="n"&gt;Repository&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="n"&gt;Repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;UserService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;repo&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;h4&gt;
  
  
  2. Registering the Services
&lt;/h4&gt;

&lt;p&gt;You register these services with the &lt;code&gt;ServiceRegistry&lt;/code&gt;. Parsley automatically detects that &lt;code&gt;NewUserService&lt;/code&gt; requires an implementation of &lt;code&gt;Repository&lt;/code&gt; and will resolve it before calling the constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/matzefriedrich/parsley/pkg/registration"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServiceRegistry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Register the repository implementation (assuming NewSqlRepository exists)&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterTransient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewSqlRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Register the service; Parsley resolves the repository automatically&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterTransient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewUserService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// ... continue with resolving and using services ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Registering Pre-existing Instances
&lt;/h3&gt;

&lt;p&gt;There are cases where you cannot use a simple constructor function. For example, when you have a third-party client that requires complex configuration or a legacy object that is already initialized elsewhere. A common use case is registering a database connection pool that was initialized at application startup.&lt;/p&gt;

&lt;p&gt;For these scenarios, Parsley provides the &lt;code&gt;RegisterInstance&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Register a pre-configured *sql.DB instance as a singleton&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connStr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterInstance&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Any service registered via &lt;code&gt;RegisterInstance&lt;/code&gt; is treated as a &lt;strong&gt;Singleton&lt;/strong&gt;. The same instance is reused throughout the application, maintaining its state and consistency.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will revisit this database example in a later part of this series when we discuss &lt;strong&gt;Factory Functions&lt;/strong&gt;, which provide a more flexible way to handle complex initializations within the container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Operational Considerations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Interface-Driven Design
&lt;/h4&gt;

&lt;p&gt;Parsley works most effectively when you register services against interfaces. This promotes decoupling and makes your application easier to test. While you can register concrete types, doing so ties your components to specific implementations.&lt;/p&gt;

&lt;h4&gt;
  
  
  Error Handling
&lt;/h4&gt;

&lt;p&gt;Parsley encourages explicit error handling. If a constructor returns an error, Parsley will catch it during the resolution process and return it to the caller. This ensures that your application doesn't start with partially initialized or broken services.&lt;/p&gt;

&lt;h4&gt;
  
  
  Validation
&lt;/h4&gt;

&lt;p&gt;To build confidence in your configuration, Parsley includes built-in validation for service registrations. This feature helps prevent runtime errors caused by missing dependencies or circular references by verifying the entire dependency graph during startup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Understanding service registration is the first step toward mastering Parsley. By using constructor functions, you delegate the complexity of dependency wiring to the framework. For more complex integration needs, manual instance registration provides a reliable escape hatch.&lt;/p&gt;

&lt;p&gt;In the next part of this series, we will explore &lt;strong&gt;Understanding Lifetimes and Scopes&lt;/strong&gt;, where we discuss how to manage the lifecycle of your services effectively.&lt;/p&gt;

&lt;h4&gt;
  
  
  Next Steps
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Explore the &lt;a href="https://matzefriedrich.github.io/parsley-docs/registration/register-constructor-functions/" rel="noopener noreferrer"&gt;Parsley Documentation&lt;/a&gt; for more advanced registration techniques.&lt;/li&gt;
&lt;li&gt;Review the &lt;a href="https://github.com/matzefriedrich/parsley-docs/tree/main/examples/registration-concepts" rel="noopener noreferrer"&gt;Registration Concepts Example&lt;/a&gt; in the official docs repository.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>go</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Part 1: Mastering Dependency Injection in Go: A Quick Start Guide</title>
      <dc:creator>Matthias Friedrich</dc:creator>
      <pubDate>Fri, 15 May 2026 23:50:41 +0000</pubDate>
      <link>https://dev.to/matzefriedrich/part-1-mastering-dependency-injection-in-go-a-quick-start-guide-2igh</link>
      <guid>https://dev.to/matzefriedrich/part-1-mastering-dependency-injection-in-go-a-quick-start-guide-2igh</guid>
      <description>&lt;h3&gt;
  
  
  The Challenge of Manual Dependency Management in Go
&lt;/h3&gt;

&lt;p&gt;In our introductory article, &lt;a href="https://dev.to/matzefriedrich/part-0-the-case-for-dependency-injection-in-go-4kl3"&gt;Part 0: The Case for Dependency Injection in Go&lt;/a&gt;, we discussed why managing dependencies manually becomes a significant burden as Go applications grow.&lt;/p&gt;

&lt;p&gt;While this approach is transparent and easy to trace, it presents several challenges as the dependency graph deepens:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Boilerplate overhead:&lt;/strong&gt; You often find yourself writing extensive setup code in &lt;code&gt;main.go&lt;/code&gt; to instantiate and wire dozens of services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifetime management complexity:&lt;/strong&gt; Ensuring that a database pool is a singleton while a request logger is scoped to a specific transaction requires careful manual tracking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactoring friction:&lt;/strong&gt; Adding a new dependency to a low-level service requires updating every intermediate factory function in the chain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You may have evaluated tools like &lt;code&gt;google/wire&lt;/code&gt; for compile-time generation or &lt;code&gt;uber-go/dig&lt;/code&gt; for runtime reflection-based injection. &lt;a href="https://github.com/matzefriedrich/parsley" rel="noopener noreferrer"&gt;Parsley&lt;/a&gt; provides a balanced, reflection-based alternative designed to bridge the gap between simple configuration and automated service activation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction to Parsley
&lt;/h3&gt;

&lt;p&gt;Parsley is a reflection-based dependency injection package for Go, hosted on &lt;a href="https://github.com/matzefriedrich/parsley" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, that implements the Inversion of Control (IoC) principle. It allows you to define how your services should be created and let the framework handle the wiring and lifetime management.&lt;/p&gt;

&lt;p&gt;Unlike code-generation tools that can clutter your workspace, Parsley works at runtime, providing flexibility without requiring additional build steps. It is particularly well-suited for developers transitioning from ecosystems like C# (.NET) or Java (Spring) who value a clear, registry-based approach to DI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architectural Overview
&lt;/h3&gt;

&lt;p&gt;Parsley centers around two primary concepts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Service Registry:&lt;/strong&gt; A container where you define service mappings, constructor functions, and lifetime behaviors.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Resolver:&lt;/strong&gt; The component that traverses the dependency graph and instantiates services as needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Parsley supports three distinct lifetime behaviors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Singleton:&lt;/strong&gt; A single instance is created and shared across the entire application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoped:&lt;/strong&gt; An instance is created once per scope (e.g., per HTTP request).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transient:&lt;/strong&gt; A new instance is created every time the service is requested.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Practical Example: A Simple Greeter
&lt;/h3&gt;

&lt;p&gt;To see Parsley in action, let's build a simple application that greets the user.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Define the Abstraction
&lt;/h4&gt;

&lt;p&gt;We start by defining a &lt;code&gt;Greeter&lt;/code&gt; interface. This follows the Go practice of defining contracts at the consumer level (or close to it) to enable decoupling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Greeter&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Implement the Service
&lt;/h4&gt;

&lt;p&gt;We implement a concrete &lt;code&gt;greeter&lt;/code&gt; type. The struct can remain unexported because it will be instantiated via a constructor function that returns the exported interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;greeter&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;string&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;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, %s!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewGreeter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="n"&gt;Greeter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;greeter&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;h4&gt;
  
  
  3. Register and Resolve the Service
&lt;/h4&gt;

&lt;p&gt;The following example demonstrates how to set up the registry, register the service as transient, and resolve it using the &lt;code&gt;resolving&lt;/code&gt; package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/matzefriedrich/parsley/pkg/registration"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/matzefriedrich/parsley/pkg/resolving"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Initialize the registry&lt;/span&gt;
    &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServiceRegistry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Register the constructor function&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterTransient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NewGreeter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Create a resolver and a scope&lt;/span&gt;
    &lt;span class="n"&gt;resolver&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewResolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewScopedContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// Resolve the Greeter service&lt;/span&gt;
    &lt;span class="n"&gt;greeterService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;resolving&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResolveRequiredService&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Greeter&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;greeterService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Parsley"&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;h3&gt;
  
  
  Operational Considerations
&lt;/h3&gt;

&lt;p&gt;When adopting a reflection-based DI container like Parsley, keep the following operational concerns in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling:&lt;/strong&gt; Parsley encourages explicit error handling. While the registry functions return errors, you should also ensure your constructor functions return errors if they can fail during initialization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Startup Performance:&lt;/strong&gt; Reflection has a minor performance cost at startup. For most backend applications, this is negligible compared to database connections or network initialization, but it should be measured in latency-sensitive environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Management:&lt;/strong&gt; Always use &lt;code&gt;NewScopedContext&lt;/code&gt; to ensure that scoped services are properly tracked and disposed of if they implement any cleanup logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tradeoffs and Limitations
&lt;/h3&gt;

&lt;p&gt;Parsley is designed for simplicity and ease of use, but it involves tradeoffs common to reflection-based DI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Runtime vs. Compile-time:&lt;/strong&gt; Unlike &lt;code&gt;wire&lt;/code&gt;, Parsley cannot catch missing dependencies at compile-time. You should use Parsley's built-in validation features (which we will cover in a later part of this series) during your CI/CD process or application startup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reflection:&lt;/strong&gt; While Go's reflection is efficient, it is still slower than direct instantiation. Parsley optimizes this by caching resolution plans, but the very first resolution of a type will incur a small overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Parsley offers a robust way to implement Inversion of Control in Go applications without the complexity of code generation. By leveraging constructor functions and automated lifetime management, it reduces boilerplate and allows developers to focus on business logic rather than wiring.&lt;/p&gt;

&lt;p&gt;In the next part of this series, we will dive deeper into &lt;strong&gt;Service Registration Fundamentals&lt;/strong&gt;, exploring how to register preexisting instances and handle more complex constructor signatures.&lt;/p&gt;

&lt;h4&gt;
  
  
  Next Steps
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Explore the &lt;a href="https://matzefriedrich.github.io/parsley-docs/" rel="noopener noreferrer"&gt;Parsley Documentation&lt;/a&gt; on GitHub.&lt;/li&gt;
&lt;li&gt;Experiment with different lifetimes (Singleton vs. Scoped) in your own projects.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>beginners</category>
      <category>go</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Part 0: The Case for Dependency Injection in Go</title>
      <dc:creator>Matthias Friedrich</dc:creator>
      <pubDate>Fri, 15 May 2026 23:39:25 +0000</pubDate>
      <link>https://dev.to/matzefriedrich/part-0-the-case-for-dependency-injection-in-go-4kl3</link>
      <guid>https://dev.to/matzefriedrich/part-0-the-case-for-dependency-injection-in-go-4kl3</guid>
      <description>&lt;h3&gt;
  
  
  The Simplicity of Go
&lt;/h3&gt;

&lt;p&gt;Go is celebrated for its simplicity. We value explicit code over "magic," composition over complex inheritance hierarchies, and the ability to understand a program by tracing its execution path from &lt;code&gt;main()&lt;/code&gt;. This philosophy is precisely why Dependency Injection (DI) is often a controversial topic in the Go community.&lt;/p&gt;

&lt;p&gt;Many Go developers associate DI with the heavy, reflection-heavy frameworks found in Java or C#. They fear that introducing a DI container will obscure the application's logic, making it harder to debug and maintain.&lt;/p&gt;

&lt;p&gt;However, as applications grow from small microservices into complex production systems, manual dependency management becomes a burden. In this introductory "Part 0" of this series, we will examine the case for DI in Go and establish why a lightweight container like &lt;a href="https://github.com/matzefriedrich/parsley" rel="noopener noreferrer"&gt;Parsley&lt;/a&gt; might be the right choice for your next project.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Problem: The "Dependency Hell" of Manual Wiring
&lt;/h3&gt;

&lt;p&gt;In a small Go application, manual wiring is straightforward. You create your database connection, pass it to your repository, pass the repository to your service, and finally pass the service to your HTTP handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewUserRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
  &lt;span class="n"&gt;userService&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewUserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  &lt;span class="n"&gt;requestHandler&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewUserHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;requestHandler&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;This is explicit and clear. However, consider what happens when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your service now requires a logger, a metrics client, and an email provider.&lt;/li&gt;
&lt;li&gt;You have 50 different handlers, each requiring a different subset of 20 shared services.&lt;/li&gt;
&lt;li&gt;You need to manage different service lifetimes (e.g., a shared cache vs. a per-request transaction).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suddenly, your &lt;code&gt;main()&lt;/code&gt; function or initialization logic becomes a massive "wiring" section. Adding a single dependency to a low-level service requires updating every intermediate constructor in the chain. This is &lt;strong&gt;tight coupling&lt;/strong&gt; in its most tedious form.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. DI is a Pattern, Not Just a Library
&lt;/h3&gt;

&lt;p&gt;It is important to distinguish between &lt;strong&gt;Dependency Injection (the pattern)&lt;/strong&gt; and &lt;strong&gt;DI Containers (the tool)&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Pattern:&lt;/strong&gt; Passing dependencies into a struct rather than having the struct create them itself. This is idiomatic Go and essential for testability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Container:&lt;/strong&gt; A tool that automates the process of creating and injecting those dependencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/matzefriedrich/parsley" rel="noopener noreferrer"&gt;Parsley&lt;/a&gt; is a DI container, but it is built specifically for the Go ecosystem. It doesn't use XML configuration or runtime "magic" that hides the control flow. Instead, it provides a &lt;strong&gt;Service Registry&lt;/strong&gt; where you define how to create your services using standard Go constructor functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Bridging the Gap: How Parsley Respects Go Idioms
&lt;/h3&gt;

&lt;p&gt;When transitioning from languages like C# or Java, developers expect the container to do everything. In Go, we want to maintain control. Parsley balances these needs by focusing on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explicitness:&lt;/strong&gt; You explicitly register every service. There is no "auto-discovery" that scans your packages and guesses what you need.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Safety:&lt;/strong&gt; Parsley uses Go's type system to ensure that the dependencies provided match the dependencies requested.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimal Abstraction:&lt;/strong&gt; You continue to write standard Go constructors. Parsley simply orchestrates their execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. The Engineering Tradeoffs
&lt;/h3&gt;

&lt;p&gt;Choosing to use a DI container is an engineering decision with real tradeoffs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Advantages
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling:&lt;/strong&gt; Services no longer need to know how to construct their dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testability:&lt;/strong&gt; Swapping a real database for a mock becomes trivial because the service only cares about the interface it receives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle Management:&lt;/strong&gt; Containers handle the complexities of Singleton vs. Transient services, ensuring resources are shared or recreated as needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Non-Goals &amp;amp; Limitations
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Startup Overhead:&lt;/strong&gt; Parsley performs type checking and dependency graph construction at startup. While extremely fast, it is a cost not present in manual wiring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning Curve:&lt;/strong&gt; Developers need to understand concepts like service lifetimes and scopes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Summary
&lt;/h3&gt;

&lt;p&gt;Dependency Injection isn't about making Go look like Java. It's about managing complexity so you can focus on writing business logic instead of boilerplate wiring code. By using a tool that aligns with Go's values, you can build systems that are both highly modular and easy to understand.&lt;/p&gt;

&lt;p&gt;In the next part, &lt;strong&gt;&lt;a href="https://dev.to/matzefriedrich/part-1-mastering-dependency-injection-in-go-a-quick-start-guide-2igh"&gt;Part 1: Mastering Dependency Injection in Go: A Quick Start Guide&lt;/a&gt;&lt;/strong&gt;, we will get our hands dirty and see how to set up your first Parsley registry.&lt;/p&gt;

&lt;h4&gt;
  
  
  Next Steps
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Evaluate your current project: How many lines of code are dedicated purely to manual dependency wiring?&lt;/li&gt;
&lt;li&gt;Read more about the &lt;a href="https://matzefriedrich.github.io/parsley-docs/" rel="noopener noreferrer"&gt;Inversion of Control (IoC)&lt;/a&gt; principle in the official Parsley documentation.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>architecture</category>
      <category>go</category>
      <category>softwareengineering</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
