<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Optivem Journal: Hexagonal Architecture]]></title><description><![CDATA[Hexagonal Architecture]]></description><link>https://journal.optivem.com/s/hexagonal-architecture</link><image><url>https://substackcdn.com/image/fetch/$s_!0CjJ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F9abead4c-3f54-46b1-96aa-7033849416df_200x200.png</url><title>Optivem Journal: Hexagonal Architecture</title><link>https://journal.optivem.com/s/hexagonal-architecture</link></image><generator>Substack</generator><lastBuildDate>Mon, 20 Apr 2026 02:29:31 GMT</lastBuildDate><atom:link href="https://journal.optivem.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Valentina Jemuović, Optivem]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[optivem@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[optivem@substack.com]]></itunes:email><itunes:name><![CDATA[Valentina Jemuović]]></itunes:name></itunes:owner><itunes:author><![CDATA[Valentina Jemuović]]></itunes:author><googleplay:owner><![CDATA[optivem@substack.com]]></googleplay:owner><googleplay:email><![CDATA[optivem@substack.com]]></googleplay:email><googleplay:author><![CDATA[Valentina Jemuović]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Hexagonal Architecture: Ports Are NOT Just Interfaces]]></title><description><![CDATA[Code Example]]></description><link>https://journal.optivem.com/p/hexagonal-architecture-ports-are</link><guid isPermaLink="false">https://journal.optivem.com/p/hexagonal-architecture-ports-are</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Thu, 09 Apr 2026 06:02:31 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0006a123-2c1a-41fd-9df9-a85f7e798fc5_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>When developers hear &#8220;port,&#8221; most think interface.</p><p>Add an interface, call it a port, check the box.</p><p>But here&#8217;s the trap: <strong>just adding interfaces doesn&#8217;t make your domain independent.<br></strong>Abstraction alone doesn&#8217;t automatically enforce architecture.</p><p>If your dependencies point the wrong way, your domain depends on the database, API, or external services &#8212; instead of defining what the system should do.</p><p>In Hexagonal Architecture, the question isn&#8217;t just &#8220;do I have a port?&#8221;</p><p>It&#8217;s: <strong>who does the domain call, and who implements it?</strong></p><p>Do your dependencies point the right way?</p><div><hr></div><p>&#128640; <strong>Register now</strong>: <a href="https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop">Acceptance Testing Workshop</a><br>Get 100 EUR off with code <strong>EARLYBIRD100</strong></p><div><hr></div><h2>&#10060; Domain depends on Infrastructure</h2><ul><li><p>Domain directly uses infrastructure classes like <code>JpaOrderRepository</code> or <code>PaymentApiClient</code></p></li><li><p>Business logic becomes tied to storage or APIs</p></li><li><p>Changing the database or API can break domain code</p></li></ul><h2><strong>&#9989;</strong> Domain defines the contract, infrastructure implements the contract</h2><ul><li><p>Domain defines driven ports &#8212; interfaces that describe what it needs done</p></li><li><p>Driven adapters implement the details</p></li><li><p>Domain logic is stable, infrastructure can change without affecting it</p></li></ul><p>Driven ports exist to control this dependency direction.</p><p>Abstraction alone doesn&#8217;t enforce architecture &#8212; direction does.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IiW-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd7fc230-cf10-4ca2-8a38-afe964b0307f_1025x1264.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IiW-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd7fc230-cf10-4ca2-8a38-afe964b0307f_1025x1264.png 424w, https://substackcdn.com/image/fetch/$s_!IiW-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd7fc230-cf10-4ca2-8a38-afe964b0307f_1025x1264.png 848w, https://substackcdn.com/image/fetch/$s_!IiW-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd7fc230-cf10-4ca2-8a38-afe964b0307f_1025x1264.png 1272w, https://substackcdn.com/image/fetch/$s_!IiW-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd7fc230-cf10-4ca2-8a38-afe964b0307f_1025x1264.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IiW-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd7fc230-cf10-4ca2-8a38-afe964b0307f_1025x1264.png" width="1025" height="1264" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cd7fc230-cf10-4ca2-8a38-afe964b0307f_1025x1264.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1264,&quot;width&quot;:1025,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:143675,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/193263907?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd7fc230-cf10-4ca2-8a38-afe964b0307f_1025x1264.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IiW-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd7fc230-cf10-4ca2-8a38-afe964b0307f_1025x1264.png 424w, https://substackcdn.com/image/fetch/$s_!IiW-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd7fc230-cf10-4ca2-8a38-afe964b0307f_1025x1264.png 848w, https://substackcdn.com/image/fetch/$s_!IiW-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd7fc230-cf10-4ca2-8a38-afe964b0307f_1025x1264.png 1272w, https://substackcdn.com/image/fetch/$s_!IiW-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcd7fc230-cf10-4ca2-8a38-afe964b0307f_1025x1264.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h1>Real-life Example: Ordering System</h1><p>Applying Hexagonal Architecture on the backend (for an e-commerce system).</p>
      <p>
          <a href="https://journal.optivem.com/p/hexagonal-architecture-ports-are">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Hexagonal Architecture: Do NOT mock everything]]></title><description><![CDATA[Stop Mocking. Start Using Fakes, Stubs, and Spies.]]></description><link>https://journal.optivem.com/p/hexagonal-architecture-do-not-mock-everything</link><guid isPermaLink="false">https://journal.optivem.com/p/hexagonal-architecture-do-not-mock-everything</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Thu, 19 Feb 2026 07:01:12 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5ba7bc75-490b-45ff-b8e9-0b7cd01a3dcd_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128197; Join me for <strong><a href="https://optivem.thinkific.com/products/live_events/2026-02-25-acceptance-testing-live-training">Acceptance Testing (Live Training)</a></strong> on Wed 25th Feb (17:00 - 19:00 CET) <em>(100% discount for Optivem Journal members)</em></p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>The most common mistake in Hexagonal Architecture is mocking everything outside the domain.</p><p>Tests break after the tiniest change.</p><p>Refactors become nightmares.</p><p>And your &#8220;clean architecture&#8221; is now just a pile of mocks.</p><h2>&#10060; Over-Mocking Everything</h2><p>Okay, Order Service Test&#8230; let&#8217;s mock all the driven ports:  </p><pre><code><code>- Repository? mock
- Payment Gateway? mock
- Email Service? mock</code></code></pre><p>By the time your test runs, it&#8217;s fragile spaghetti:</p><ul><li><p>Change a method signature in the repository? Test fails.</p></li><li><p>Change a method signature in the payment gateway? Test fails.</p></li></ul><p>In the repository, I had a method <code>add(int id, OrderData data)</code>. I refactored it to be <code>add(Order order)</code>&#8230; tests fail because the mock is now out-of-date.</p><p>In the payment gateway, I used the method <code>getAllInvoices()</code> and then did in-memory filtering to find an invoice. In my source code I decided to use the method <code>getInvoice(String invoiceId)</code>&#8230; tests fail because the mock now is out-of-date.</p><p>You now have <strong>hexagonal architecture&#8230; and 47 failing tests</strong>.</p><p>Why it&#8217;s bad:</p><ul><li><p>Tests describe implementation, not behavior</p></li><li><p>Ports are treated as a checklist of mocks, not boundaries</p></li><li><p>Refactoring domain logic becomes a nightmare</p></li><li><p>Cognitive load goes through the roof</p></li></ul><h2>&#9989; Testing What Matters</h2><p>Focus on <strong>behavior, not mocks</strong>:</p><ul><li><p>&#9989; Fakes</p></li><li><p>&#9989; Stubs</p></li><li><p>&#9989; Spies</p></li><li><p>&#10060; Mocks</p></li></ul><p>For the same Order Service:</p><pre><code><code>- Repository &#8594; in-memory fake
- Payment Gateway &#8594; simple stub that can be configured to maybe payment succeed or fail
- Email Service &#8594; spy, whereby we collect emails in a queue, and inspect emails sent</code></code></pre><p>Notice the difference?</p><ul><li><p>Tests now describe what the domain does, not how infrastructure works.</p></li><li><p>Refactoring domain code rarely breaks tests.</p></li><li><p>Cognitive load drops &#8212; you&#8217;re testing one thing at a time.</p></li></ul><blockquote><p>Fake data, not behavior. Test behavior, not calls.</p></blockquote><h1>&#128161;Order Test Without Mock Overload</h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nfP-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nfP-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 424w, https://substackcdn.com/image/fetch/$s_!nfP-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 848w, https://substackcdn.com/image/fetch/$s_!nfP-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 1272w, https://substackcdn.com/image/fetch/$s_!nfP-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nfP-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png" width="1456" height="1753" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1753,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:331079,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/187517057?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nfP-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 424w, https://substackcdn.com/image/fetch/$s_!nfP-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 848w, https://substackcdn.com/image/fetch/$s_!nfP-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 1272w, https://substackcdn.com/image/fetch/$s_!nfP-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>
      <p>
          <a href="https://journal.optivem.com/p/hexagonal-architecture-do-not-mock-everything">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[So You Think You’re Doing Hexagonal Architecture?]]></title><description><![CDATA[What goes in each folder?]]></description><link>https://journal.optivem.com/p/hexagonal-architecture-folder-structure</link><guid isPermaLink="false">https://journal.optivem.com/p/hexagonal-architecture-folder-structure</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 02 Jan 2026 07:00:48 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/4d50249d-da56-4c48-9621-a5902675fdf4_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128197; Join me for <strong><a href="https://optivem.thinkific.com/products/live_events/2026-02-25-acceptance-testing-live-training">Acceptance Testing (Live Training)</a></strong> on Wed 25th Feb (17:00 - 19:00 CET) <em>(100% discount for Optivem Journal members)</em></p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>I&#8217;ve lost count of how many times I&#8217;ve heard this:</p><blockquote><p>&#8220;Yeah, this project uses Hexagonal Architecture.&#8221;</p></blockquote><p>Then I open the repo.</p><p>And immediately feel confused.</p><p>Controllers mixed with use cases.<br>Repositories sitting next to domain objects.<br>Tests that need half the infrastructure just to run.</p><p>I stare at the screen and think: <em>What is going on here?</em></p><p>The problem usually isn&#8217;t the architecture.</p><p>It&#8217;s the <strong>folder structure</strong>.</p><h2>The One Mental Model That Finally Clicked for Me</h2><p>I stopped thinking in terms of &#8220;layers&#8221;.</p><p>Instead, I started asking one simple question:</p><p><strong>Is this part of my application&#8217;s core&#8212;or just a way to talk to the outside world?</strong></p><p>Everything in the heart goes into the <strong>App folder</strong>.<br>Everything else&#8212;adapters, controllers, integrations&#8212;lives outside.</p><p>Suddenly, the structure made sense.</p><h2>How I Actually Organize Things</h2><pre><code>&#128193; App (Hexagon)
 &#9500;&#9472;&#9472; &#128193; Driving Ports
 &#9474;    &#9492;&#9472;&#9472; &#128193; DTOs
 &#9492;&#9472;&#9472; &#128193; Core
 &#9474;    &#9500;&#9472;&#9472; &#128193; Application
 &#9474;    &#9492;&#9472;&#9472; &#128193; Domain
 &#9492;&#9472;&#9472; &#128193; Driven Ports

&#128193; Driving Adapters

&#128193; Driven Adapters
 &#9500;&#9472;&#9472; &#128193; Real Driven Adapters
 &#9492;&#9472;&#9472; &#128193; Fake Driven Adapters</code></pre><h2>Inside a Shipping App&#8212;Pro Example</h2><p>Let&#8217;s walk through a shipment.</p>
      <p>
          <a href="https://journal.optivem.com/p/hexagonal-architecture-folder-structure">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Modern Hexagonal Architecture: Testing for Backend]]></title><description><![CDATA[How can we test the Backend?]]></description><link>https://journal.optivem.com/p/modern-hexagonal-architecture-testing-for-backend</link><guid isPermaLink="false">https://journal.optivem.com/p/modern-hexagonal-architecture-testing-for-backend</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 28 Nov 2025 07:02:27 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1db0b116-cf88-4e50-8ac7-cc9d24654521_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128640; Join the <strong><a href="https://atdd.optivem.com/">ATDD Accelerator waitlist</a></strong></p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>Backend Component Tests</h2><p>Backend Component Tests verify whether the Backend as a whole, works correctly (this spans all logic - presentation, business, infrastructure logic). They:</p><ul><li><p>Target the Backend API</p></li><li><p>Stub out External Systems</p></li></ul><p>These tests are agnostic to the architecture inside the Backend. It doesn&#8217;t matter whether the Backend is a Big Ball of Mud, or Hexagonal Architecture, or Vertical Architecture, etc. The Component Tests don&#8217;t care, they don&#8217;t know, because they are blackbox tests - they are testing the Backend as a blackbox.</p><p>Example: <code>OrderControllerTest</code> sets up the <code>Stripe WireMock Stub</code> that it returns some invoice number, we call the Backend REST API endpoint POST <code>/api/orders</code> with request body <code>{ sku=ABC, quantity=4 }</code>, and then verify that it returns an order number.</p><p><em>In Legacy Code, we can write Component Tests from Day 1.</em></p><h2>Backend Hexagonal Architecture Testing</h2><p>If the Backend is structured using Hexagonal Architecture, then we can go a level down, to do this kind of testing:</p><ol><li><p>Unit Tests - testing the Hexagon</p></li><li><p>Narrow Integration Tests - testing the Adapters</p></li></ol><p><em>Note: In Legacy Code, if your Backend is a Big Ball of Mud, you&#8217;ll you need to have Component Tests first, then you&#8217;ll be able to safely redesign your Backend from Big Ball of Mud towards Hexagonal Architecture, and then you&#8217;ll be able to introduce these tests.</em></p><h3>Unit Tests - Hexagon</h3><p>Unit Tests verify Business Logic. They:</p><ul><li><p>Target Driving Ports</p></li><li><p>Stub out Driven Ports</p></li></ul><p><em>Therefore, they span the Hexagon, i.e. testing the Hexagon in isolation.</em></p><p>Example I: With coarse-grained ports (Application Services): <code>OrderServiceTest</code> tests the business logic inside <code>OrderService</code>, whilst stubbing out the <code>OrderRepository</code>. </p><p>Example II: With fine-grained ports (Request Handlers): <code>PlaceOrderTest</code> tests the business logic inside <code>PlaceOrderHandler</code>, whilst stubbing out the <code>OrderRepository</code>.</p><h3>Narrow Integration Tests - Driving Adapters</h3><p>Driving Adapter Narrow Integration Tests verify Presentation Logic. They:</p><ul><li><p>Target Driving Adapters</p></li><li><p>Stub out Driven Ports</p></li></ul><p><em>Therefore, they span the Driving Adapters, i.e. testing the Driving Adapters in isolation.</em></p><p>Example: <code>OrderControllerTest</code> tests the presentation logic inside the <code>OrderController</code>, e.g. how it maps success/failure to http status codes and responses.</p><h3>Narrow Integration Tests - Driven Adapters</h3><p>Driven Adapter Narrow Integration Tests verify Infrastructure Logic. They:</p><ul><li><p>Target Driven Adapters via Driven Ports</p></li><li><p>Stub out External Systems</p></li></ul><p><em>Therefore, they span the Driven Adapters, i.e., testing the Driven Adapters in isolation.</em></p><p>Example I: <code>OrderRepositoryTest</code> tests the infrastructure logic of the OrderRepository. It&#8217;s executed against the <code>SqlOrderRepository</code> (using real DB) and the <code>FakeOrderRepository</code> adapters. </p><p>Example II: <code>PaymentGatewayTest</code> tests the infrastructure logic of the <code>PaymentGateway</code>. It&#8217;s executed against the <code>StripePaymentGateway</code> (using WireMock instead of Stripe API) and the <code>FakePaymentGateway</code> adapters.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2khi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc056d3c8-eb59-4650-ac86-8f890c042dd6_1837x1076.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2khi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc056d3c8-eb59-4650-ac86-8f890c042dd6_1837x1076.png 424w, https://substackcdn.com/image/fetch/$s_!2khi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc056d3c8-eb59-4650-ac86-8f890c042dd6_1837x1076.png 848w, https://substackcdn.com/image/fetch/$s_!2khi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc056d3c8-eb59-4650-ac86-8f890c042dd6_1837x1076.png 1272w, https://substackcdn.com/image/fetch/$s_!2khi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc056d3c8-eb59-4650-ac86-8f890c042dd6_1837x1076.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2khi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc056d3c8-eb59-4650-ac86-8f890c042dd6_1837x1076.png" width="1456" height="853" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c056d3c8-eb59-4650-ac86-8f890c042dd6_1837x1076.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:853,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:146914,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/179833947?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc056d3c8-eb59-4650-ac86-8f890c042dd6_1837x1076.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2khi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc056d3c8-eb59-4650-ac86-8f890c042dd6_1837x1076.png 424w, https://substackcdn.com/image/fetch/$s_!2khi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc056d3c8-eb59-4650-ac86-8f890c042dd6_1837x1076.png 848w, https://substackcdn.com/image/fetch/$s_!2khi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc056d3c8-eb59-4650-ac86-8f890c042dd6_1837x1076.png 1272w, https://substackcdn.com/image/fetch/$s_!2khi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc056d3c8-eb59-4650-ac86-8f890c042dd6_1837x1076.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><div><hr></div><h2>Real-life Example: Ordering System</h2><p>For an e-commerce system:</p>
      <p>
          <a href="https://journal.optivem.com/p/modern-hexagonal-architecture-testing-for-backend">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Hexagonal Architecture - External World (The "outside")]]></title><description><![CDATA[Our system interacts with the external world through adapters]]></description><link>https://journal.optivem.com/p/hexagonal-architecture-external-world-the-outside</link><guid isPermaLink="false">https://journal.optivem.com/p/hexagonal-architecture-external-world-the-outside</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 07 Nov 2025 07:00:42 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5fdc9501-b32c-4ec7-bc43-2fbea0f51bf8_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128640; Join the <strong><a href="https://atdd.optivem.com/">ATDD Accelerator waitlist</a></strong></p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>The Outside of the Hexagon</h2><p>The &#8220;external world&#8221; contains <em>all the technologies and systems that surround our core application</em>: databases, APIs, message brokers, user interfaces, etc.</p><p>The hexagon acts as a shield - it defines a clear boundary between what&#8217;s <strong>inside</strong> (domain and use cases) and what&#8217;s <strong>outside</strong> (infrastructure and delivery).</p><p>The outside world can change - you might switch frameworks, UIs, or databases - without changing the core logic inside the hexagon.</p><p>That&#8217;s because communication between the two happens only through <strong>ports and adapters</strong>.</p><h2>Driving Adapters (User-side API)</h2><p>On the left side of the hexagon, we have <strong>Driving Adapters</strong> &#8212; these are the entry points that <em>drive</em> the application from the outside.</p><p>They present the application in a way suitable for clients, and then call the <strong>driving ports</strong> (the use cases).</p><p>Driving adapters - examples:</p><ul><li><p>A REST API that receives HTTP requests and calls a use case</p></li><li><p>A Web UI or mobile app interacting with backend services</p></li><li><p>A command-line or batch process triggering operations</p></li></ul><p>Driving adapters don&#8217;t contain business logic. Instead they contain <strong>presentation logic:</strong></p><ul><li><p>Receive external input (like an HTTP request or button click)</p></li><li><p>Convert it into a suitable format (e.g. a DTO)</p></li><li><p>Call the appropriate use case inside the hexagon</p></li><li><p>Return the result back to the client</p></li></ul><p>Driving adapters make it possible to expose the same use case in multiple ways &#8212; e.g. through a web API, CLI, or message consumer &#8212; without duplicating logic.</p><p>You can swap out your web framework or migrate from REST to GraphQL without touching your business rules.</p><h2>Driven Adapters (Server-side API)</h2><p>On the opposite side of the hexagon, we have <strong>Driven Adapters</strong> &#8212; these implement the <strong>driven ports</strong> that the application depends on.</p><p>If a use case needs to:</p><ul><li><p>Save data to a database</p></li><li><p>Call an external API</p></li><li><p>Send an email</p></li><li><p>Publish an event</p></li></ul><p>&#8230;it doesn&#8217;t do those things directly. Instead, it talks to a <strong>driven port</strong> interface, and the driven adapter handles the infrastructure details.</p><p>Driven Adapters - examples:</p><ul><li><p><strong>Database Adapter</strong> implementing <code>OrderRepository</code> interface, e.g. by executing SQL queries on an SQL database, or perhaps by using an ORM, etc.</p></li><li><p><strong>Email Adapter</strong> implementing <code>NotificationGateway</code> interface, e.g. by using Gmail SDK to send emails</p></li><li><p><strong>Payment Adapter</strong> implementing <code>PaymentGateway</code>, e.g. by integrating with Stripe or PayPal API</p></li><li><p><strong>Event Adapter</strong> implementing <code>PublisherGateway</code>, e.g. by publishing messages to Kafka or RabbitMQ</p></li></ul><p>Driven adapters keep infrastructure concerns separate from business logic.</p><p>If your database, API provider, or message broker changes, you only update the adapter &#8212; your domain and use cases remain untouched.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KKwk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F065c3282-d132-4be9-94b5-f4993333abec_1840x1152.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KKwk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F065c3282-d132-4be9-94b5-f4993333abec_1840x1152.png 424w, https://substackcdn.com/image/fetch/$s_!KKwk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F065c3282-d132-4be9-94b5-f4993333abec_1840x1152.png 848w, https://substackcdn.com/image/fetch/$s_!KKwk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F065c3282-d132-4be9-94b5-f4993333abec_1840x1152.png 1272w, https://substackcdn.com/image/fetch/$s_!KKwk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F065c3282-d132-4be9-94b5-f4993333abec_1840x1152.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KKwk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F065c3282-d132-4be9-94b5-f4993333abec_1840x1152.png" width="1456" height="912" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/065c3282-d132-4be9-94b5-f4993333abec_1840x1152.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:912,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:127198,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/175532829?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F065c3282-d132-4be9-94b5-f4993333abec_1840x1152.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!KKwk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F065c3282-d132-4be9-94b5-f4993333abec_1840x1152.png 424w, https://substackcdn.com/image/fetch/$s_!KKwk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F065c3282-d132-4be9-94b5-f4993333abec_1840x1152.png 848w, https://substackcdn.com/image/fetch/$s_!KKwk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F065c3282-d132-4be9-94b5-f4993333abec_1840x1152.png 1272w, https://substackcdn.com/image/fetch/$s_!KKwk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F065c3282-d132-4be9-94b5-f4993333abec_1840x1152.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>Real-life Example: Ordering System</h2>
      <p>
          <a href="https://journal.optivem.com/p/hexagonal-architecture-external-world-the-outside">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Hexagonal Architecture - Internal World (The "inside")]]></title><description><![CDATA[Our system core logic shouldn&#8217;t be affected by anything external]]></description><link>https://journal.optivem.com/p/hexagonal-architecture-internal-world-the-inside</link><guid isPermaLink="false">https://journal.optivem.com/p/hexagonal-architecture-internal-world-the-inside</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Thu, 09 Oct 2025 06:00:16 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/29727075-f52b-4857-ab72-6fc8ac6c4983_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128640; Join the <strong><a href="https://atdd.optivem.com/">ATDD Accelerator waitlist</a></strong></p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>Hexagon</h2><p>The &#8220;internal world&#8221; is the <em>application itself</em> &#8211; its domain logic, the rules that define what the system does and how it behaves.</p><p>We want to keep the &#8220;internal world&#8221; uncontaminated, we don&#8217;t want it to be affected by the &#8220;external&#8221; world (external technologies).</p><p>We can think of this as a capsule, called a &#8220;Hexagon&#8221;. It that defines a clear boundary between the inside and the outside.</p><p>Keep your core independent from frameworks, databases, UIs, or any other external systems. This gives you flexibility to change your database, UI, or external APIs <em>without rewriting your business logic</em>.</p><h2>Driving Ports (User-side API)</h2><p>On one side of the hexagon, we have <strong>Driving Ports</strong> &#8212; the user-facing entry points into the system.</p><p>Our application exists so that it helps clients to run some use cases. Clients could be:</p><ul><li><p>Human users interacting via a UI</p></li><li><p>Other systems making API calls</p></li><li><p>Test harnesses simulating real-world behavior</p></li><li><p>etc&#8230;</p></li></ul><p>Driving Ports define <strong>what the system can do</strong>, not <strong>how</strong> it does it. Think of them like the &#8220;steering wheel&#8221; - they let you control the application, but not open the hood.</p><p>We want to expose ONLY the use cases to the clients (and NOT expose the internals of the application!).</p><p>The Hexagon provides some kind of user-facing API (&#8221;driving&#8221; ports), so that our clients can execute the use cases.</p><h2>Driven Ports (Server-side API)</h2><p>On the opposite side, we have <strong>Driven Ports</strong> &#8212; these are the interfaces that the <em>application needs the outside world to implement.</em></p><p>The application might need to:</p><ul><li><p>Save data to a database</p></li><li><p>Send an email notification</p></li><li><p>Publish an event to a message broker</p></li><li><p>Call an external payment service</p></li><li><p>etc&#8230;</p></li></ul><p>We don&#8217;t want to directly be aware of external tech nor directly talk to those external systems, so we need some kind of server-side API.</p><p>The internal world should never <em>know</em> which specific technology is used.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!E5Cc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc23a82f1-8bfc-4d05-88c9-c2b72d80b6b5_1568x1152.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!E5Cc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc23a82f1-8bfc-4d05-88c9-c2b72d80b6b5_1568x1152.png 424w, https://substackcdn.com/image/fetch/$s_!E5Cc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc23a82f1-8bfc-4d05-88c9-c2b72d80b6b5_1568x1152.png 848w, https://substackcdn.com/image/fetch/$s_!E5Cc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc23a82f1-8bfc-4d05-88c9-c2b72d80b6b5_1568x1152.png 1272w, https://substackcdn.com/image/fetch/$s_!E5Cc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc23a82f1-8bfc-4d05-88c9-c2b72d80b6b5_1568x1152.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!E5Cc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc23a82f1-8bfc-4d05-88c9-c2b72d80b6b5_1568x1152.png" width="1456" height="1070" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c23a82f1-8bfc-4d05-88c9-c2b72d80b6b5_1568x1152.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1070,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:377698,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/175035378?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc23a82f1-8bfc-4d05-88c9-c2b72d80b6b5_1568x1152.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!E5Cc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc23a82f1-8bfc-4d05-88c9-c2b72d80b6b5_1568x1152.png 424w, https://substackcdn.com/image/fetch/$s_!E5Cc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc23a82f1-8bfc-4d05-88c9-c2b72d80b6b5_1568x1152.png 848w, https://substackcdn.com/image/fetch/$s_!E5Cc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc23a82f1-8bfc-4d05-88c9-c2b72d80b6b5_1568x1152.png 1272w, https://substackcdn.com/image/fetch/$s_!E5Cc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc23a82f1-8bfc-4d05-88c9-c2b72d80b6b5_1568x1152.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>Real-life Example: Ordering System</h2>
      <p>
          <a href="https://journal.optivem.com/p/hexagonal-architecture-internal-world-the-inside">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Hexagonal Architecture - Adapter Testing]]></title><description><![CDATA[How should we test adapters? Should we use "integrated" or "isolated" tests?]]></description><link>https://journal.optivem.com/p/hexagonal-architecture-adapter-testing</link><guid isPermaLink="false">https://journal.optivem.com/p/hexagonal-architecture-adapter-testing</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 14 Mar 2025 07:01:32 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/6f202fa3-bcd1-4886-82da-fa8a8b67b1e8_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When I read Alistair&#8217;s original article <a href="https://alistair.cockburn.us/hexagonal-architecture/">Hexagonal Architecture</a>, I noticed only one type of test mentioned in the diagram - the &#8220;test harness adapter&#8220; which targets the user-side API, i.e. targeting the driving ports.</p><p>Testing driving ports (spanning the hexagon in isolation) had a clear value - providing regression bug protection in business logic. </p><div><hr></div><p>&#128226; My <strong>next Live Q&amp;A Session</strong> about <strong>Unit Tests </strong>is on Wed 30th April 17:00. If you&#8217;re an Optivem Journal paid member, <a href="https://lu.ma/hhmu38v1">get your 100% discount ticket</a>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://lu.ma/hhmu38v1&quot;,&quot;text&quot;:&quot;Register now&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://lu.ma/hhmu38v1"><span>Register now</span></a></p><p>Upgrade to join the Live Q&amp;A for free:</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>But what about testing adapters, e.g. testing database adapters, file adapters, external system adapters?</p><ol><li><p>Should adapters be tested, are they worthy of being tested?</p></li><li><p>If adapters should be tested, should we test them in an integrated way or in an isolated way?</p></li></ol>
      <p>
          <a href="https://journal.optivem.com/p/hexagonal-architecture-adapter-testing">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Hexagonal Architecture - Driven Adapter Integration Testing]]></title><description><![CDATA[I made mistakes when doing Integration Testing on Driven Adapters that were connecting to External Systems. I'll share the problems and solutions here.]]></description><link>https://journal.optivem.com/p/hexagonal-architecture-driven-adapter-integration-testing</link><guid isPermaLink="false">https://journal.optivem.com/p/hexagonal-architecture-driven-adapter-integration-testing</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Tue, 01 Oct 2024 06:01:29 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/35997314-8d55-4be4-9bef-bf90f828fc69_1224x816.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When reading <a href="https://alistair.cockburn.us/hexagonal-architecture">Hexagonal Architecture (Alistair Cockburn)</a>, we see how to test the Hexagon&#8230; but how can we test the Driven Adapters? This question troubled me for years. I went through a lot of trial-and-error to discover the effective approach. </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><h2>I thought ALL Integration Tests should include I/O</h2><p>When connecting to I/O sources, we use Driven Adapters, which integrate with the I/O source. The Driven Adapter does some translation work, calls the I/O source, and then does that translation again. </p><h4>Internal I/O Driven Adapters</h4><p>These Driven Adapters connect to the I/O that is owned by our microservice. For example, we have the <code>OrderRepository</code> (driven port), which is implemented by the <code>PostgresOrderRepository</code> (driven adapter). The Driven Adapter integrates with our Postgres database using ORM and/or SQL queries and/or stored procedures.</p><p>When doing Integration Tests for the <code>PostgresOrderRepository</code>, the test should span the Postgres Database. This means running the Postgres database locally, on our Test Server, or even better, running Postgres with Testcontainers.</p><h4>External I/O Driven Adapters</h4><p>These Driven Adapters connect to stuff that is not owned by our microservice. For example, we have the <code>PaymentGateway</code> (driven port), which is implemented by the <code>PayPalPaymentGateway</code> (driven adapter), which integrates with PayPal by making HTTP calls to the PayPal REST API.</p><p>When doing Integration Testing for the <code>PayPalPaymentGateway</code>, I (and many other developers) thought that we should integrate it with PayPal's REST API. This means running an External System Test Instance on our Test Server, or connecting to the Test Instance provided by the third-party vendor. In the case of PayPal, it means using the PayPal sandbox.</p><p>Thus, like many other developers, I thought that to test Driven Adapters, we should truly connect to the Test Instance of that I/O source.</p><h2>But I was wrong (in the case of External Systems)</h2><p>Fortunately, Integration Testing for Internal I/O Driven Adapters worked out well.</p><p>But Integration Testing for External I/O Driven Adapters was a nightmare! The mistake I made was including the External System Test Instance in the Integration Test itself, and that&#8217;s a mistake I&#8217;ve seen so many others developers make as well - due to fallacies in widespread notions of integration testing.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YUbT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5732109-4e0a-4353-b1d6-7a957c73e7e3_1008x991.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YUbT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5732109-4e0a-4353-b1d6-7a957c73e7e3_1008x991.png 424w, https://substackcdn.com/image/fetch/$s_!YUbT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5732109-4e0a-4353-b1d6-7a957c73e7e3_1008x991.png 848w, https://substackcdn.com/image/fetch/$s_!YUbT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5732109-4e0a-4353-b1d6-7a957c73e7e3_1008x991.png 1272w, https://substackcdn.com/image/fetch/$s_!YUbT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5732109-4e0a-4353-b1d6-7a957c73e7e3_1008x991.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YUbT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5732109-4e0a-4353-b1d6-7a957c73e7e3_1008x991.png" width="1008" height="991" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f5732109-4e0a-4353-b1d6-7a957c73e7e3_1008x991.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:991,&quot;width&quot;:1008,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:95745,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!YUbT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5732109-4e0a-4353-b1d6-7a957c73e7e3_1008x991.png 424w, https://substackcdn.com/image/fetch/$s_!YUbT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5732109-4e0a-4353-b1d6-7a957c73e7e3_1008x991.png 848w, https://substackcdn.com/image/fetch/$s_!YUbT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5732109-4e0a-4353-b1d6-7a957c73e7e3_1008x991.png 1272w, https://substackcdn.com/image/fetch/$s_!YUbT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff5732109-4e0a-4353-b1d6-7a957c73e7e3_1008x991.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I&#8217;m now going to reveal to you:</p><ul><li><p>The 5 problems of mainstream Integration Testing for Driven Adapters</p></li><li><p>The solution I discovered to overcome this problem &#11015;&#65039;&#11015;&#65039;&#11015;&#65039; </p></li></ul>
      <p>
          <a href="https://journal.optivem.com/p/hexagonal-architecture-driven-adapter-integration-testing">
              Read more
          </a>
      </p>
   ]]></content:encoded></item></channel></rss>