|
1 | | -<!doctype html><html lang=en><head><title>A Use Case for Port Boundaries in Frontend Development · cekrem.github.io</title><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=color-scheme content="light dark"><meta name=author content="Christian Ekrem"><meta name=description content="Learn how Elm's architectural discipline—and a few ideas from Clean Architecture—can reshape how we think about frontend boundaries."><meta name=keywords content="developer,personal"><meta name=fediverse:creator content><meta name=twitter:card content="summary_large_image"><meta name=twitter:image content="https://cekrem.github.io/images/banner.jpg"><meta name=twitter:title content="A Use Case for Port Boundaries in Frontend Development"><meta name=twitter:description content="Learn how Elm's architectural discipline—and a few ideas from Clean Architecture—can reshape how we think about frontend boundaries."><meta property="og:url" content="https://cekrem.github.io/posts/a-case-for-port-boundaries-in-frontend/"><meta property="og:site_name" content="cekrem.github.io"><meta property="og:title" content="A Use Case for Port Boundaries in Frontend Development"><meta property="og:description" content="Learn how Elm's architectural discipline—and a few ideas from Clean Architecture—can reshape how we think about frontend boundaries."><meta property="og:locale" content="en_us"><meta property="og:type" content="article"><meta property="article:section" content="posts"><meta property="article:published_time" content="2025-05-19T00:00:00+00:00"><meta property="article:modified_time" content="2025-05-19T00:00:00+00:00"><meta property="article:tag" content="Clean Architecture"><meta property="article:tag" content="Architecture"><meta property="article:tag" content="Elm"><meta property="article:tag" content="React"><meta property="article:tag" content="Frontend"><meta property="article:tag" content="Software Design"><meta property="og:image" content="https://cekrem.github.io/images/banner.jpg"><link rel=canonical href=https://cekrem.github.io/posts/a-case-for-port-boundaries-in-frontend/><link rel=preload href=/fonts/fa-brands-400.woff2 as=font type=font/woff2 crossorigin><link rel=preload href=/fonts/fa-regular-400.woff2 as=font type=font/woff2 crossorigin><link rel=preload href=/fonts/fa-solid-900.woff2 as=font type=font/woff2 crossorigin><link rel=stylesheet href=/css/coder.min.e8eb08b92eec0f2a19c8cbe4eda1f38c703416996d08f55900541ba798921e5b.css integrity="sha256-6OsIuS7sDyoZyMvk7aHzjHA0FpltCPVZAFQbp5iSHls=" crossorigin=anonymous media=screen><link rel=stylesheet href=/css/coder-dark.min.a93347c2833a476d1c25ae74bed3f80024fc1eaf1a33d084c2211065513e5c42.css integrity="sha256-qTNHwoM6R20cJa50vtP4ACT8Hq8aM9CEwiEQZVE+XEI=" crossorigin=anonymous media=screen><link rel=icon type=image/svg+xml href=/images/favicon.svg sizes=any><link rel=icon type=image/png href=/images/favicon-32x32.png sizes=32x32><link rel=icon type=image/png href=/images/favicon-16x16.png sizes=16x16><link rel=apple-touch-icon href=/images/apple-touch-icon.png><link rel=apple-touch-icon sizes=180x180 href=/images/apple-touch-icon.png><link rel=manifest href=/site.webmanifest><link rel=mask-icon href=/images/safari-pinned-tab.svg color=#5bbad5></head><body class="preload-transitions colorscheme-auto"><main class=wrapper><nav class=navigation><section class=container><a class=navigation-title href=https://cekrem.github.io/>cekrem.github.io |
| 1 | +<!doctype html><html lang=en><head><title>A Use Case for Port Boundaries in Frontend Development · cekrem.github.io</title><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=color-scheme content="light dark"><meta name=author content="Christian Ekrem"><meta name=description content="Learn how Elm's architectural discipline—and a few ideas from Clean Architecture—can reshape how we think about frontend boundaries."><meta name=keywords content="developer,personal"><meta name=fediverse:creator content><meta name=twitter:card content="summary_large_image"><meta name=twitter:image content="https://cekrem.github.io/images/banner.jpg"><meta name=twitter:title content="A Use Case for Port Boundaries in Frontend Development"><meta name=twitter:description content="Learn how Elm's architectural discipline—and a few ideas from Clean Architecture—can reshape how we think about frontend boundaries."><meta property="og:url" content="https://cekrem.github.io/posts/a-case-for-port-boundaries-in-frontend/"><meta property="og:site_name" content="cekrem.github.io"><meta property="og:title" content="A Use Case for Port Boundaries in Frontend Development"><meta property="og:description" content="Learn how Elm's architectural discipline—and a few ideas from Clean Architecture—can reshape how we think about frontend boundaries."><meta property="og:locale" content="en_us"><meta property="og:type" content="article"><meta property="article:section" content="posts"><meta property="article:published_time" content="2025-05-19T00:00:00+00:00"><meta property="article:modified_time" content="2025-05-19T00:00:00+00:00"><meta property="article:tag" content="Clean-Architecture"><meta property="article:tag" content="Architecture"><meta property="article:tag" content="Elm"><meta property="article:tag" content="React"><meta property="article:tag" content="Frontend"><meta property="article:tag" content="Software-Design"><meta property="og:image" content="https://cekrem.github.io/images/banner.jpg"><link rel=canonical href=https://cekrem.github.io/posts/a-case-for-port-boundaries-in-frontend/><link rel=preload href=/fonts/fa-brands-400.woff2 as=font type=font/woff2 crossorigin><link rel=preload href=/fonts/fa-regular-400.woff2 as=font type=font/woff2 crossorigin><link rel=preload href=/fonts/fa-solid-900.woff2 as=font type=font/woff2 crossorigin><link rel=stylesheet href=/css/coder.min.e8eb08b92eec0f2a19c8cbe4eda1f38c703416996d08f55900541ba798921e5b.css integrity="sha256-6OsIuS7sDyoZyMvk7aHzjHA0FpltCPVZAFQbp5iSHls=" crossorigin=anonymous media=screen><link rel=stylesheet href=/css/coder-dark.min.a93347c2833a476d1c25ae74bed3f80024fc1eaf1a33d084c2211065513e5c42.css integrity="sha256-qTNHwoM6R20cJa50vtP4ACT8Hq8aM9CEwiEQZVE+XEI=" crossorigin=anonymous media=screen><link rel=icon type=image/svg+xml href=/images/favicon.svg sizes=any><link rel=icon type=image/png href=/images/favicon-32x32.png sizes=32x32><link rel=icon type=image/png href=/images/favicon-16x16.png sizes=16x16><link rel=apple-touch-icon href=/images/apple-touch-icon.png><link rel=apple-touch-icon sizes=180x180 href=/images/apple-touch-icon.png><link rel=manifest href=/site.webmanifest><link rel=mask-icon href=/images/safari-pinned-tab.svg color=#5bbad5></head><body class="preload-transitions colorscheme-auto"><main class=wrapper><nav class=navigation><section class=container><a class=navigation-title href=https://cekrem.github.io/>cekrem.github.io |
2 | 2 | </a><input type=checkbox id=menu-toggle> |
3 | 3 | <label class="menu-button float-right" for=menu-toggle><i class="fa-solid fa-bars fa-fw" aria-hidden=true></i></label><ul class=navigation-list><li class=navigation-item><a class=navigation-link href=/posts/how-i-met-jesus/>Jesus!?</a></li><li class=navigation-item><a class=navigation-link href=/about/>About</a></li><li class=navigation-item><a class=navigation-link href=/posts/books-i-think-software-engineers-should-read>Recommended Books</a></li><li class=navigation-item><a class=navigation-link href=https://creators.spotify.com/pod/show/disippel>Podcast (🇳🇴)</a></li><li class=navigation-item><a class=navigation-link href=/posts/>Blog</a></li></ul></section></nav><div class=content><section class="container post"><article><header><div class=post-title><h1 class=title><a class=title-link href=https://cekrem.github.io/posts/a-case-for-port-boundaries-in-frontend/>A Use Case for Port Boundaries in Frontend Development</a></h1></div><div class=post-meta><div class=date><span class=posted-on><i class="fa-solid fa-calendar" aria-hidden=true></i> |
4 | 4 | <time datetime=2025-05-19T00:00:00Z>May 19, 2025 |
5 | 5 | </time></span><span class=reading-time><i class="fa-solid fa-clock" aria-hidden=true></i> |
6 | 6 | 5-minute read</span></div><div class=tags><i class="fa-solid fa-tag" aria-hidden=true></i> |
7 | | -<span class=tag><a href=/tags/clean-architecture/>Clean Architecture</a> |
| 7 | +<span class=tag><a href=/tags/clean-architecture/>Clean-Architecture</a> |
8 | 8 | </span><span class=separator>•</span> |
9 | 9 | <span class=tag><a href=/tags/architecture/>Architecture</a> |
10 | 10 | </span><span class=separator>•</span> |
|
14 | 14 | </span><span class=separator>•</span> |
15 | 15 | <span class=tag><a href=/tags/frontend/>Frontend</a> |
16 | 16 | </span><span class=separator>•</span> |
17 | | -<span class=tag><a href=/tags/software-design/>Software Design</a></span></div></div></header><div class=post-content><p>In the Elm ecosystem, the browser is treated like an I/O device. DOM events, JavaScript interop, and even network requests are kept outside the core logic. Rather than allowing side effects to permeate the codebase, Elm channels them through strictly typed boundaries known as <em>ports</em>.</p><p>This architectural stance is both radical and liberating. It allows you to build user interfaces where logic remains pure, testable, and robust, even as surrounding technologies evolve.</p><p>You might not be writing Elm, but the core idea is portable: <strong>treat your UI runtime as a detail</strong>. What if React—or your TypeScript frontend—adopted this philosophy? What if we stopped treating our framework as the foundation and started treating it as just another dependency?</p><hr><h2 id=your-app-is-not-your-framework>Your App Is Not Your Framework |
| 17 | +<span class=tag><a href=/tags/software-design/>Software-Design</a></span></div></div></header><div class=post-content><p>In the Elm ecosystem, the browser is treated like an I/O device. DOM events, JavaScript interop, and even network requests are kept outside the core logic. Rather than allowing side effects to permeate the codebase, Elm channels them through strictly typed boundaries known as <em>ports</em>.</p><p>This architectural stance is both radical and liberating. It allows you to build user interfaces where logic remains pure, testable, and robust, even as surrounding technologies evolve.</p><p>You might not be writing Elm, but the core idea is portable: <strong>treat your UI runtime as a detail</strong>. What if React—or your TypeScript frontend—adopted this philosophy? What if we stopped treating our framework as the foundation and started treating it as just another dependency?</p><hr><h2 id=your-app-is-not-your-framework>Your App Is Not Your Framework |
18 | 18 | <a class=heading-link href=#your-app-is-not-your-framework><i class="fa-solid fa-link" aria-hidden=true title="Link to heading"></i> |
19 | 19 | <span class=sr-only>Link to heading</span></a></h2><p>Too many frontend projects blur the lines between business logic and UI behavior. It’s common to model domain concepts directly inside React components and tie state management logic to hooks, lifecycle events, or global stores.</p><p>Over time, this creates tight coupling to the framework. Refactoring becomes risky. Testing core logic without a UI becomes difficult. And your <em>business model</em> becomes tangled with the frontend library of the month.</p><p>You might discover, months in, that half your app is unusable outside a browser—or that important state transitions are buried deep inside components.</p><p><img src=https://media0.giphy.com/media/v1.Y2lkPTc5MGI3NjExbWJocXQ3cnB5NG00OWV1aWZldmtvdjQxZmJxY3hiMndxcHJtZDNmYyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/SDGNkoOsb8alDk66ZN/giphy.gif alt=michael-scott-in-chains></p><p>According to Uncle Bob, you should <em>date</em> your framework, not <em>marry</em> it—and in theory, React isn’t even a framework, but a library. As Martin Fowler and others have pointed out, the key distinction lies in <em>inversion of control</em>: with a library, <em>your code</em> calls into it; with a framework, <em>it</em> calls into your code. This subtle but important difference affects how much architectural ownership you retain. As <a href=https://stackoverflow.com/questions/3057526/framework-vs-toolkit-vs-library class=external-link target=_blank rel=noopener>thoroughly explained and discussed here</a>, libraries are called by your code, while frameworks call your code.</p><p>By designing your application to live independently of its framework, you dramatically increase its adaptability and longevity.</p><h2 id=what-elm-gets-right>What Elm Gets Right |
20 | 20 | <a class=heading-link href=#what-elm-gets-right><i class="fa-solid fa-link" aria-hidden=true title="Link to heading"></i> |
|
0 commit comments