<?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"><channel><title><![CDATA[Scalable Backend]]></title><description><![CDATA[Welcome to blog.scalablebackend.com where you will find free and high quality learning materials that support more practical courses from scalablebackend.com.]]></description><link>https://blog.scalablebackend.com</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 17:20:23 GMT</lastBuildDate><atom:link href="https://blog.scalablebackend.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Performance Testing a Real-World Back-End: NodeJS vs Go]]></title><description><![CDATA[To be more precise, I'm about to compare two Back-End apps made with NestJS (configured with Fastify) and Gin.
Why this specific choice you may ask? NestJS and Gin are both used extensively and are great solutions for building production-ready Back-E...]]></description><link>https://blog.scalablebackend.com/performance-testing-a-real-world-back-end-nodejs-vs-go</link><guid isPermaLink="true">https://blog.scalablebackend.com/performance-testing-a-real-world-back-end-nodejs-vs-go</guid><category><![CDATA[Node.js]]></category><category><![CDATA[nestjs]]></category><category><![CDATA[gin-gonic]]></category><category><![CDATA[gorm]]></category><category><![CDATA[Go Language]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Sun, 07 Jul 2024 10:42:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/H30w37gpkro/upload/558d516eb8408197f956fde65ff84c69.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>To be more precise, I'm about to compare two Back-End apps made with <a target="_blank" href="https://nestjs.com">NestJS</a> (configured with <a target="_blank" href="https://fastify.dev">Fastify</a>) and <a target="_blank" href="https://github.com/gin-gonic/gin">Gin</a>.</p>
<p>Why this specific choice you may ask? NestJS and Gin are both used extensively and are great solutions for building production-ready Back-End applications.</p>
<p>Configuring NestJS with Fastify makes it one of the most performant setups for NodeJS. It could be a handicap for Go and Gin, unless Gin absolutely destroys one of the best solutions with NodeJS, performance-wise?</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://blog.scalablebackend.com/performance-testing-express-fastify-and-nestjs-with-expressfastify">https://blog.scalablebackend.com/performance-testing-express-fastify-and-nestjs-with-expressfastify</a></div>
<p> </p>
<p>This article is built on top of a previous <a target="_blank" href="https://blog.scalablebackend.com/performance-testing-express-fastify-and-nestjs-with-expressfastify">performance comparison</a> for NodeJS, feel free to have a look.</p>
<h3 id="heading-applications">Applications</h3>
<p>Both applications provide the same functionalities, a basic newsletter with authentication, user management, and article management.</p>
<p>The NodeJS application is made on top of TypeScript, Prisma, and does some validation with <a target="_blank" href="https://github.com/typestack/class-validator">class-validator</a>.</p>
<p>On the other side, the Gin application is made with <a target="_blank" href="https://gorm.io">GORM</a>, and uses the integrated <a target="_blank" href="https://github.com/go-playground/validator">validator</a>.</p>
<p>They both provide authentication capabilities with JWTs and are configured with a PostgreSQL database.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://aws.amazon.com">https://aws.amazon.com</a></div>
<p> </p>
<p>Again, the database is hosted on <a target="_blank" href="https://aws.amazon.com/rds/"><strong>AWS RDS</strong></a>, while the applications are on Beanstalk (on a <a target="_blank" href="https://aws.amazon.com/ec2/instance-types/t2/#Product_Detailshttps://aws.amazon.com/ec2/instance-types/t2/#Product_Details"><strong>t2.medium</strong></a>). If you read the previous article, you already know I'm about to use <a target="_blank" href="https://www.artillery.io/"><strong>Artillery</strong></a> to run the actual performance tests.</p>
<h3 id="heading-tests-configuration">Tests configuration</h3>
<p>I'll apply the same strategy to both environments (with Nest and Gin):</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="892f8d14d2d5b32c5fa78d086755b151"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/892f8d14d2d5b32c5fa78d086755b151" class="embed-card">https://gist.github.com/morintd/892f8d14d2d5b32c5fa78d086755b151</a></div><p> </p>
<p>Here, I'm creating a new user and logging in, before creating a new article and fetching different pages of articles (5 on each page). I still need a processor to generate users and articles with unique data:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="49324c4289ae55f88bb27edfc2ad4adb"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/49324c4289ae55f88bb27edfc2ad4adb" class="embed-card">https://gist.github.com/morintd/49324c4289ae55f88bb27edfc2ad4adb</a></div><p> </p>
<p>If you don't know yet, Artillery works with a virtual user system. The number of virtual users can be configured, and one will be created per second, to execute the entire scenario you can see above.</p>
<p>In our current scenario, we have 6 requests:</p>
<ul>
<li><p>Register</p>
</li>
<li><p>Login</p>
</li>
<li><p>Create an article</p>
</li>
<li><p>Fetch 3 pages</p>
</li>
</ul>
<p>We can multiply the number of virtual users by 6 to get the request count per second (or 360 to find the count per minute, etc.).</p>
<h3 id="heading-response-time">Response time</h3>
<p>To get started, we can run our performance test by simulating a medium load on our server. This way, we have exploitable results, without any of our Back-End actually failing.</p>
<p>To do so, 4 virtual users are enough (making 1.440 requests per minute if you don't feel like doing math). With Artillery, we can generate a nice visual report, with response time among other things:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720299804796/b622a906-4799-45cf-bca7-e4d669f6c500.jpeg" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720299819061/89b88879-7d95-48b0-9b8e-8773cfa7d39a.jpeg" alt class="image--center mx-auto" /></p>
<p>I'm not a monster, I'll provide you with the actual values:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>(In milliseconds)</td><td>NestJS + Fastify</td><td>Gin</td></tr>
</thead>
<tbody>
<tr>
<td>Min</td><td>31</td><td>29</td></tr>
<tr>
<td>Max</td><td>993</td><td>246</td></tr>
<tr>
<td>Median</td><td>153</td><td>40.9</td></tr>
<tr>
<td>p95</td><td>487.9</td><td>165.7</td></tr>
<tr>
<td>p99</td><td>550.1</td><td>190.6</td></tr>
</tbody>
</table>
</div><p>The first time I saw those values, I thought to myself:</p>
<blockquote>
<p>Wow, it's looking grim for NodeJS</p>
</blockquote>
<p>The <code>Min</code> and <code>Max</code> response time are interesting metrics, but that's not what I'm looking for here. You only need a single request to perform well or badly among thousands to get different results.</p>
<p>On the other hand, the median response time is an incredible metric, as well as <code>p95</code> and <code>p99</code>. They respectively show us the maximum response time among the 95% and 99% of the best requests.</p>
<p>There, the Gin-based Back-End is indeed destroying the NestJS one, performance-wise. The difference is almost threefold, even I didn't expect that!</p>
<h3 id="heading-making-our-servers-crash">Making our servers crash</h3>
<p>With the visual report from Artillery, we can also read the requests in <strong><em>timeout</em></strong>. It's a great indicator of when our APIs start to fail. I used the following configuration with my NestJS application:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="f4dd0e8feb1aa298c34865127303b205"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/f4dd0e8feb1aa298c34865127303b205" class="embed-card">https://gist.github.com/morintd/f4dd0e8feb1aa298c34865127303b205</a></div><p> </p>
<p>Here, we have 4 virtual users during the first minute. Then, we go up to 5 users for 2 minutes, and 6 users for 2 more minutes. We can see the requests in timeout for NestJS below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720301177765/b5dc1c48-dd7b-491d-9eaa-4c089511e307.png" alt class="image--center mx-auto" /></p>
<p>Here, we can see our server starts to fail around 4 minutes, when we use 6 virtual users (2.160 requests per minute).</p>
<p>We can observe a similar graph with the Go/Gin application, with... <strong><em>14 virtual users</em></strong>.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>I'm first and foremost a JavaScript developer, and have extensive experience on the NodeJS environment.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.prisma.io">https://www.prisma.io</a></div>
<p> </p>
<p>I worked with NestJS, Prisma, and everything around it for years now. I even have Prisma <strong><em>stickers</em></strong> and developed a <a target="_blank" href="https://github.com/morintd/prismock">library</a> dedicated to unit testing it.</p>
<p>If I made some configuration mistake or introduced bad practices, it might be on the Gin application, not NestJS.</p>
<p>If there is one thing to remember for this exercise, it's that <strong>Go performs two to three times better</strong> with a handicap, compared to NodeJS.</p>
]]></content:encoded></item><item><title><![CDATA[Performance Testing Express, Fastify, and NestJS (With Express/Fastify)]]></title><description><![CDATA[In the past few years, I used Express quite extensively, before moving to the amazing NestJS. I also used Fastify occasionally and, while it's not the framework I have the most experience with, I'm well aware of its performance capabilities and matur...]]></description><link>https://blog.scalablebackend.com/performance-testing-express-fastify-and-nestjs-with-expressfastify</link><guid isPermaLink="true">https://blog.scalablebackend.com/performance-testing-express-fastify-and-nestjs-with-expressfastify</guid><category><![CDATA[Express]]></category><category><![CDATA[nestjs]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[backend]]></category><category><![CDATA[fastify]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Sat, 06 Jul 2024 11:39:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/l87JKfXFcm4/upload/c423628330619640461aafd83a2c808e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the past few years, I used <a target="_blank" href="https://expressjs.com">Express</a> quite extensively, before moving to the amazing <a target="_blank" href="https://nestjs.com">NestJS</a>. I also used <a target="_blank" href="https://fastify.dev">Fastify</a> occasionally and, while it's not the framework I have the most experience with, I'm well aware of its performance capabilities and maturity.</p>
<h3 id="heading-nestjs-with-express-or-fastify">NestJS with Express or Fastify?</h3>
<p>If you aren't aware yet, NestJS is not an HTTP server framework by itself. It uses an actual server framework under the hood. It's configured by default with ExpressJS and provides easy integration with Fastify.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://docs.nestjs.com/techniques/performance">https://docs.nestjs.com/techniques/performance</a></div>
<p> </p>
<p>If you are just like me, you've read everywhere that higher-level frameworks tend to have lower performances. The features and ease of development often come with additional abstractions and processes, leading to a decline in performance.</p>
<p>But is that really true? Does Fastify indeed have better performance than Express? Also, what's the impact of using NestJS?</p>
<h3 id="heading-applications">Applications</h3>
<p>Luckily, I personally maintain four boilerplate projects with the same features, made with Express, Fastify, NestJS (with Express), and NestJS (with Fastify).</p>
<p>They're quite simple Back-End applications, a newsletter! To be more precise, they are made with:</p>
<ul>
<li><p>TypeScript</p>
</li>
<li><p>Prisma and PostgreSQL</p>
</li>
<li><p>Passport (and JWT) for authentication</p>
</li>
<li><p>Class-validator for data validation</p>
</li>
</ul>
<p>They provide a restricted set of functionalities:</p>
<ul>
<li><p>Authentication (Register, Login)</p>
</li>
<li><p>Manage users (Update, Delete)</p>
</li>
<li><p>Manage articles (Create, Delete, List, Detail)</p>
</li>
</ul>
<h3 id="heading-testing-context">Testing context</h3>
<p>Once I have access to those applications, it's possible to performance test them, and compare the results. Before doing so, let's define the context of our tests.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://aws.amazon.com">https://aws.amazon.com</a></div>
<p> </p>
<p>First of all, the database will be hosted on <a target="_blank" href="https://aws.amazon.com/rds/">AWS RDS</a>, and the applications themselves will be on Beanstalk (on a <a target="_blank" href="https://aws.amazon.com/ec2/instance-types/t2/#Product_Detailshttps://aws.amazon.com/ec2/instance-types/t2/#Product_Details">t2.medium</a>).</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.artillery.io">https://www.artillery.io</a></div>
<p> </p>
<p>Then, we need a solution to actually run the performance tests and generate results. I do so thanks to the amazing <a target="_blank" href="https://www.artillery.io">Artillery</a>.</p>
<h3 id="heading-artillery-setup">Artillery Setup</h3>
<blockquote>
<p>You can find the complete Artillery setup on <a target="_blank" href="https://github.com/morintd/artillery-test/">GitHub</a>.</p>
</blockquote>
<p>My strategy is quite straightforward. I'll set up four environments with the different applications, and apply the same scenario on each of them.</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="115b54c0c76b91a8c0050abc4ece10b4"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/115b54c0c76b91a8c0050abc4ece10b4" class="embed-card">https://gist.github.com/morintd/115b54c0c76b91a8c0050abc4ece10b4</a></div><p> </p>
<p>Here, you can see I:</p>
<ul>
<li><p>Create a new user</p>
</li>
<li><p>Login</p>
</li>
<li><p>Create a new article</p>
</li>
<li><p>Get a list of articles</p>
</li>
</ul>
<p>In order to run this script, I need to generate unique data. It's necessary to avoid creating a user with the same email, or articles with the same title. With Artillery, it can be done thanks to a processor:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="49324c4289ae55f88bb27edfc2ad4adb"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/49324c4289ae55f88bb27edfc2ad4adb" class="embed-card">https://gist.github.com/morintd/49324c4289ae55f88bb27edfc2ad4adb</a></div><p> </p>
<p>Finally, I have three external configurations:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="90008abb817ee061106dd188158d78f8"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/90008abb817ee061106dd188158d78f8" class="embed-card">https://gist.github.com/morintd/90008abb817ee061106dd188158d78f8</a></div><p> </p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="1975f0ec2300dc6abc40daa9bb73eb73"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/1975f0ec2300dc6abc40daa9bb73eb73" class="embed-card">https://gist.github.com/morintd/1975f0ec2300dc6abc40daa9bb73eb73</a></div><p> </p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="f4dd0e8feb1aa298c34865127303b205"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/f4dd0e8feb1aa298c34865127303b205" class="embed-card">https://gist.github.com/morintd/f4dd0e8feb1aa298c34865127303b205</a></div><p> </p>
<p>Those files define the number of requests done in different phases. Artillery works with virtual users, defined by <code>arrivalRate</code>. To be more precise, it defines the number of virtual users created per second.</p>
<p>The goal with those configurations is to simulate different levels of loads on our servers. This way, we can see the evolution of response time depending on usage. Also, with the <code>failure-usage</code> configuration, we can see at which point our server fails.</p>
<h3 id="heading-running-tests">Running tests</h3>
<p>With artillery, we can run our tests with the created configuration, for example with:</p>
<pre><code class="lang-bash">artillery run -o ./report-low-usage.json -c low-usage.yml -e fastify ./common-scenario.yml
</code></pre>
<p>Here, I'm running my main scenario, with the <code>fastify</code> environment, using the <code>low-usage</code> configuration. The results are then exported to <code>./report-low-usage.json</code>.</p>
<p>I'm not really interested in a JSON file. Thankfully, we can generate a visual report based on it:</p>
<pre><code class="lang-bash">artillery report ./report-low-usage.json
</code></pre>
<p>We now have access to an HTML report, with the following information at the top:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720193723361/b5fa31d2-c4da-4098-87f3-31f58af4d0cf.png" alt class="image--center mx-auto" /></p>
<p>I'm not really interested in this high-level overview, but we have access to more detailed results below. For example, we can have a look at the created virtual users:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720193861612/cd7d4775-bf06-426a-a593-1283f92c433d.png" alt class="image--center mx-auto" /></p>
<p>Here, we have a report every 10 seconds of the created users. On average we have 20 users, which makes sense as we used the configuration with 2 users per second.</p>
<p>Then, we have the most interesting part of the report under <code>http.response_time</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720194356252/b67bfead-3915-4726-8cac-7ab7c5dd5741.png" alt class="image--center mx-auto" /></p>
<p>Here, we can find the response that took the least time, the most time, the median time, as well as <code>p95</code> and <code>p99</code>. If you aren't sure:</p>
<ul>
<li><p><code>p95</code> defines the maximum response time among the best 95% requests.</p>
</li>
<li><p><code>p99</code> defines the maximum response time among the best 99% requests.</p>
</li>
</ul>
<h3 id="heading-comparing-performance">Comparing performance</h3>
<p>While the previous report is a great way to read a single result, it doesn't allow for easy comparison. Instead, I'll merge results from every application, and separate them by the simulated load.</p>
<p><strong>Low load</strong></p>
<p>First, we can have a look at the response time under a low load:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720196292889/9ec996ff-8063-4c38-a227-e5e6d156931b.png" alt class="image--center mx-auto" /></p>
<p>Here, we can find that the results are quite similar. The biggest difference can be found under the <code>max</code> response, but I don't think it's significant. A single <code>max</code> response is not a very good indicator as, all you need, is a single request taking longer.</p>
<p><strong>Medium load</strong></p>
<p>A more interesting report is for a medium load, closer to real-world usage. To be more precise, for the median, <code>p95</code>, and <code>p99</code> responses:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720196301061/b65c9660-b48a-4a74-af79-cadc56185a76.png" alt class="image--center mx-auto" /></p>
<p>This time, we have very interesting results. To be honest, they are even quite surprising! It seems like Fastify indeed has significantly better performance.</p>
<p>Not only has Fastify better performance, it seems like the NestJS counterpart for both Express and Fastify, have the same performance as the low-level http frameworks.</p>
<p><strong>Failure</strong></p>
<p>Because the third configuration overloads the applications until they cannot respond, the response time becomes unexploitable. Instead, on the default report, we have access to a <code>errors.ETIMEDOUT</code> section.</p>
<p>It tells us how many requests fail, but also when they start to fail. Below, you can find a comparison of the Fastify-based application (top) and Express-based application (bottom).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1720196326730/30f4debe-de5b-4541-8b1f-29ad93e0a380.png" alt class="image--center mx-auto" /></p>
<p>Here, we can see the Express-based application fails to answer requests earlier and more often than the Fastify application.</p>
<p>For Express, it starts to fail around 2 minutes, which corresponds to 5 virtual users on our configuration. For Fastify, it starts to fail 1 minute later, when we have 6 virtual users.</p>
<p>Interestingly enough, the results are exactly the same for NestJS with Express and NestJS with Fastify.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>From those results, I have two main conclusions:</p>
<p>Fastify indeed has significantly better performance, which transposes to NestJS with Fastify.</p>
<p>NestJS is even better than I thought, not only does it bring us higher-level tools, but it barely has any impact on performance.</p>
<hr />
<p>This article was originally a <a target="_blank" href="https://www.youtube.com/watch?v=5bJFptbw46I">video</a>. If you want to learn how to create production-ready Back-End application, have a look <a target="_blank" href="https://www.scalablebackend.com">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Build an ExpressJS Application With Clean Architecture]]></title><description><![CDATA[Motivation
Similarly to my Front-End implementation, I'm not satisfied with the available resources related to Clean Architecture. That's right, even on the Back-End!
What is Clean Architecture
If you stumbled upon this article, you probably know abo...]]></description><link>https://blog.scalablebackend.com/build-an-expressjs-application-with-clean-architecture</link><guid isPermaLink="true">https://blog.scalablebackend.com/build-an-expressjs-application-with-clean-architecture</guid><category><![CDATA[Clean Architecture]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Express]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Thu, 14 Mar 2024 19:21:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/aaaTRCfBC-M/upload/e8fdcb7e2426a4c23075d03e1dabbfe8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-motivation">Motivation</h2>
<p>Similarly to my <a target="_blank" href="https://morintd.hashnode.dev/build-a-react-application-with-clean-architecture">Front-End implementation</a>, I'm not satisfied with the available resources related to Clean Architecture. That's right, even on the Back-End!</p>
<h2 id="heading-what-is-clean-architecture">What is Clean Architecture</h2>
<p>If you stumbled upon this article, you probably know about Clean Architecture, but are looking for more information.</p>
<p>In the rare event of a beginner reading this article, I highly recommend getting started with the <a target="_blank" href="https://blog.scalablebackend.com/understand-the-theory-behind-clean-architecture">necessary theory</a>.</p>
<h2 id="heading-what-are-we-going-to-build">What are we going to build</h2>
<p>While I wrote this article, my goal was to test if Clean Architecture was as flexible as it promised. That's the reason I re-used the same entities and use cases as my Front-End implementation, and tried to move the infrastructure part to the Back-End with Express.</p>
<p>That means, the application is the same: a Tic-Tac-Toe game (in TypeScript). You will be able to send requests to a Back-End and play a complete game.</p>
<p>If you want to follow along with me, you can clone my <a target="_blank" href="https://github.com/morintd/expressjs-tic-tac-toe-clean-architecture/tree/original">starter project</a>.</p>
<h3 id="heading-behaviors">Behaviors</h3>
<p>You can find the Front-End Tic-Tac-Toe on <a target="_blank" href="https://codesandbox.io/p/sandbox/react-dev-q2z497">CodeSandbox</a> to understand what the application is doing in practice. There are two main functionalities:</p>
<ul>
<li><p>Obviously, you can play a game of Tic-Tac-Toe.</p>
</li>
<li><p>You can go back to a previous move (history) and play again.</p>
</li>
</ul>
<p>Instead of being a Front-End application, we will provide endpoints with the same behavior.</p>
<h3 id="heading-file-structure">File Structure</h3>
<p>The original application already follows the idea of Screaming Architecture, with three directories:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710438717844/fa7785f1-80ab-48c1-8207-d64b074b2b42.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><code>common/</code> includes all the shared source code.</p>
</li>
<li><p><code>core/</code> defines the entry point of our application:</p>
<ul>
<li><p><code>app.ts</code> contains the ExpressJS server.</p>
</li>
<li><p><code>bootstrap.ts</code> uses the code inside <code>app.ts</code> to build a server and listen to it.</p>
</li>
</ul>
</li>
<li><p><code>tic-tac-toe/</code> contains everything related to the game itself.</p>
</li>
</ul>
<h3 id="heading-files">Files</h3>
<p>While you can have a look at the entire project by yourself, please note there are three main files:</p>
<p><strong>App</strong></p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="a515410c627067ba659f74084fad9554"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/a515410c627067ba659f74084fad9554" class="embed-card">https://gist.github.com/morintd/a515410c627067ba659f74084fad9554</a></div><p> </p>
<p><code>createApp</code> is responsible for building the ExpressJS server and uses the Tic-Tac-Toe module (which is responsible for routing but is not the focus of this article).</p>
<p><strong>Tic-Tac-Toe Controller</strong></p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="e2e0ddc9d1ab7c17ace80ddf7178ae92"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/e2e0ddc9d1ab7c17ace80ddf7178ae92" class="embed-card">https://gist.github.com/morintd/e2e0ddc9d1ab7c17ace80ddf7178ae92</a></div><p> </p>
<p><code>TicTacToeController</code> is responsible for handling requests. It's the one that contains the business logic and the core part of the original implementation.</p>
<p><strong>Board Service</strong></p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="a3c7628c0cf8fa6a39ddda1a4c945dbc"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/a3c7628c0cf8fa6a39ddda1a4c945dbc" class="embed-card">https://gist.github.com/morintd/a3c7628c0cf8fa6a39ddda1a4c945dbc</a></div><p> </p>
<p><code>BoardService</code> takes the role of a data access object, which is an in-memory implementation for simplicity.</p>
<h3 id="heading-define-a-game-model">Define a Game Model</h3>
<p>Similarly to my Front-End implementation, I like to start by defining a clear <em>Domain Model</em>. Its goal is to define the elements that make up our system. Please, keep in mind this is not strictly a part of Clean Architecture but <a target="_blank" href="https://en.wikipedia.org/wiki/Domain-driven_design">Domain-Driven-Design</a>.</p>
<p>For our game of Tic-Tac-Toe, we need:</p>
<ul>
<li><p>A board composed of 9 squares.</p>
</li>
<li><p>Players and winners.</p>
</li>
<li><p>At one point in time, the state of our game:</p>
<ul>
<li><p>Is there a winner?</p>
</li>
<li><p>The history of moves (so we can navigate among them).</p>
</li>
<li><p>Who is playing next?</p>
</li>
<li><p>Potentially, the index of the last game played.</p>
</li>
</ul>
</li>
</ul>
<p>I'll create this file under <code>tic-tac-toe/</code>, using a namespace to make it clear:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="74deed0f78ccb88678e9b576ebeaa0ec"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/74deed0f78ccb88678e9b576ebeaa0ec" class="embed-card">https://gist.github.com/morintd/74deed0f78ccb88678e9b576ebeaa0ec</a></div><p> </p>
<h3 id="heading-create-the-base-structure-for-clean-architecture">Create the base structure for Clean Architecture</h3>
<p>We will create an <strong>entity</strong> and <strong>use case</strong> in a minute. We can create the base classes for them under <code>common/</code>:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="8f340c0c36f992e6d94e4602a813a2dd"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/8f340c0c36f992e6d94e4602a813a2dd" class="embed-card">https://gist.github.com/morintd/8f340c0c36f992e6d94e4602a813a2dd</a></div><p> </p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="5ca3d34ac252e845050fe835051b7058"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/5ca3d34ac252e845050fe835051b7058" class="embed-card">https://gist.github.com/morintd/5ca3d34ac252e845050fe835051b7058</a></div><p> </p>
<p>Keep in mind:</p>
<blockquote>
<p>There are multiple ways to define those.</p>
<p>For <code>Entity</code>, I like to store properties inside a <code>_data</code> object and make it protected. It prevents developers from mutating it directly, which is a common source of issues. Depending on our needs, this class will definitely change.</p>
<p>There are multiple practices when it comes to <strong>use cases</strong>, but a very common one is to provide an <code>execute</code> method, which returns a <code>Promise</code> most of the time.</p>
</blockquote>
<h3 id="heading-create-the-board-entity">Create the Board Entity</h3>
<p>Most of the logic of the Tic-Tac-Toe game seems related to the state of the <em>board</em>. Let's create an entity for it, inside a new <code>entities</code> subfolder, under <code>tic-tac-toe</code>:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="775f3fffb283278bc84efa33270c77bc"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/775f3fffb283278bc84efa33270c77bc" class="embed-card">https://gist.github.com/morintd/775f3fffb283278bc84efa33270c77bc</a></div><p> </p>
<p>Here:</p>
<ul>
<li><p>We store squares, based on the types from our <code>GameDomainModel</code>.</p>
<ul>
<li>We add a getter to easily access the property.</li>
</ul>
</li>
<li><p>We already have the formula to calculate if player <strong>X</strong> plays next.</p>
<ul>
<li><p>We copy it from the <code>TicTacToe</code> file inside the starter project.</p>
</li>
<li><p>We can calculate the current step based on the filled squares.</p>
</li>
</ul>
</li>
<li><p>The entity is also a great place to calculate if there is a winner.</p>
</li>
</ul>
<p>We can also copy the formula from the starter <code>TicTacToeController</code> file.</p>
<p>In "Clean Architecture", Uncle Bob says:</p>
<blockquote>
<p>An Entity is an object within our computer system that embodies a small set of critical business rules operating on Critical Business Data.</p>
</blockquote>
<p>In our context, our critical business rules are the rules of the game of Tic-Tac-Toe.</p>
<h3 id="heading-starting-with-our-first-use-case">Starting with our first use case</h3>
<p>Now, we have everything we need to create an empty <strong>use case</strong>. We can get started with an obvious one, playing a move.</p>
<p>Obviously, it will take the <em>square</em> we play on. On the other hand, we also need to define the <em>step</em> we play on, as we can play on a previous move. Our <strong>use case</strong> will probably return an object similar to the game state:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="f2ab8c5ea3fd876c1aac80daff9f1d9c"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/f2ab8c5ea3fd876c1aac80daff9f1d9c" class="embed-card">https://gist.github.com/morintd/f2ab8c5ea3fd876c1aac80daff9f1d9c</a></div><p> </p>
<blockquote>
<p>We could probably use <code>GameDomainModel.GameState</code> as <code>Output</code> here. On the other hand, those objects may only seem familiar, and change for different reasons.</p>
</blockquote>
<p>To be honest, the current code inside <code>TicTacToeController</code> is very similar to the future content of the <code>Play</code> use case. It might be because I created this project as another implementation, but the original project is still far from using Clean Architecture.</p>
<p>If you remember well, a use case shouldn't depend on a data access gateway from the Interface Adapters layer.</p>
<h3 id="heading-add-the-board-repository">Add the board repository</h3>
<p>Instead of the current <code>BoardService</code>, I'll define an interface as a <code>port</code> and its implementation will be an <code>adapter</code>. I'll also use the definition of <code>Repository</code> instead of <code>Service</code> for consistency.</p>
<p>Let's start with the interface by creating a sub-directory <code>ports/</code> under <code>tic-tac-toe/</code>, with a file called <code>board-repository.port.ts</code>:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="121381bcd270a0c7385b5affdf0d1367"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/121381bcd270a0c7385b5affdf0d1367" class="embed-card">https://gist.github.com/morintd/121381bcd270a0c7385b5affdf0d1367</a></div><p> </p>
<p>Then, we can create an <code>adapters/</code> sub-directory under <code>tic-tac-toe/</code>. Also, the previous <code>BoardService</code> shouldn't be called <code>BoardRepository</code>.</p>
<p>It's not <strong><em>the</em></strong> implementation of IBoardRepository, it's one among others. To be more precise, it's an in-memory implementation.</p>
<p>Let's create <code>in-memory-board.repository.ts</code>:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="2bafcff1026a6dac5dc4e11e3ff7880b"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/2bafcff1026a6dac5dc4e11e3ff7880b" class="embed-card">https://gist.github.com/morintd/2bafcff1026a6dac5dc4e11e3ff7880b</a></div><p> </p>
<p>Before we go back to our <strong>use case</strong>, we need one more thing: <strong>exceptions</strong>.</p>
<h3 id="heading-exception-management">Exception management</h3>
<p>If you look at the original <code>TicTacToeController</code>, you can see we throw exceptions when:</p>
<ul>
<li><p>The step we are supposed to play on doesn't exist</p>
</li>
<li><p>We play on a square that's already taken</p>
</li>
</ul>
<p>With Clean Architecture, we cannot throw exceptions that belong to the infrastructure layer (Interface Adapters &amp; Framework and Drivers), from the use case layer.</p>
<p>Instead, we need to throw a Domain Exception, that is handled by the controller.</p>
<p>Let's define two <strong>exceptions</strong>, stored inside an <code>exceptions/</code> sub-directory (itself inside <code>tic-tac-toe/</code>):</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="97df4bd51bb88108d6c113db54962e66"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/97df4bd51bb88108d6c113db54962e66" class="embed-card">https://gist.github.com/morintd/97df4bd51bb88108d6c113db54962e66</a></div><p> </p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="53a5069746b8a19026b105a2b780c6f9"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/53a5069746b8a19026b105a2b780c6f9" class="embed-card">https://gist.github.com/morintd/53a5069746b8a19026b105a2b780c6f9</a></div><p> </p>
<h3 id="heading-complete-our-use-case">Complete our use case</h3>
<p>Now that we have our <strong>entity</strong>, <strong>repository</strong>, and <strong>exceptions</strong>, we can complete our <strong>use case</strong>. Most of the logic can be transferred from <code>TicTacToeController</code>, in <code>handlePlay</code>.</p>
<p>There are a few changes necessary:</p>
<ul>
<li><p>Business rules are now inside the <code>Board</code> entity.</p>
</li>
<li><p>We rely on the <code>IBoardRepository</code>.</p>
</li>
<li><p>We throw a Domain Exception</p>
</li>
</ul>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="5ec5f5108ab60c8ff90773230d92330b"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/5ec5f5108ab60c8ff90773230d92330b" class="embed-card">https://gist.github.com/morintd/5ec5f5108ab60c8ff90773230d92330b</a></div><p> </p>
<p>This use case was added under <code>tic-tac-toe/</code> in its own sub-directory <code>use-cases/</code>.</p>
<h3 id="heading-adding-missing-use-cases">Adding missing use cases</h3>
<p>The process is similar for the two other use cases. We can retrieve most of the logic from the <code>TicTacToeController</code> but use our brand new <code>Board</code> entity and <code>IBoardRepository</code>.</p>
<p>Let's start with <code>jump-to.use-case.ts</code>:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="fee43fcd743fb35543729c3223667376"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/fee43fcd743fb35543729c3223667376" class="embed-card">https://gist.github.com/morintd/fee43fcd743fb35543729c3223667376</a></div><p> </p>
<p>Then, we can add <code>initialize.use-case.ts</code>, which is responsible for starting a new game:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="218074efa54f7f86552ec870fee1f581"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/218074efa54f7f86552ec870fee1f581" class="embed-card">https://gist.github.com/morintd/218074efa54f7f86552ec870fee1f581</a></div><p> </p>
<h3 id="heading-binding-use-cases-to-a-controller">Binding use cases to a controller</h3>
<p>The last part is definitely way easier in a Back-End environment. We simply need to:</p>
<ul>
<li><p>Execute the right use case</p>
</li>
<li><p>If there is a Domain (or unknown) Exception, throw the related Application Exception</p>
</li>
<li><p>Otherwise, return the result with the right status code.</p>
</li>
</ul>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="00c707c1d979a181adaa07a81e3236ff"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/00c707c1d979a181adaa07a81e3236ff" class="embed-card">https://gist.github.com/morintd/00c707c1d979a181adaa07a81e3236ff</a></div><p> </p>
<h3 id="heading-dependency-injection">Dependency Injection</h3>
<p>Same thing here, using dependency injection is definitely easy. Depending on the Back-End framework you're using, you will have different solutions (or you can setup your own with <a target="_blank" href="https://inversify.io">InversifyJS</a>).</p>
<p>In this example, I manually instantiate and inject dependencies from <code>app.ts</code>:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="334d908818d0d44e07aaa681c97df808"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/334d908818d0d44e07aaa681c97df808" class="embed-card">https://gist.github.com/morintd/334d908818d0d44e07aaa681c97df808</a></div><p> </p>
<h3 id="heading-using-a-presenter">Using a presenter</h3>
<p>If you look closely at our current controller, it's doing more than orchestration. It's also formatting data for <code>moves</code>, <code>status</code>, and <code>squares</code>, which is not really its role.</p>
<p>A better architecture would use a presenter instead. There are multiple ways to define a presenter. A common practice is to define a class with a <code>format</code> method.</p>
<p>Let's create <code>presenter.ts</code> inside <code>common/</code>:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="343b76ce8e7f2df2167840ef97642b82"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/343b76ce8e7f2df2167840ef97642b82" class="embed-card">https://gist.github.com/morintd/343b76ce8e7f2df2167840ef97642b82</a></div><p> </p>
<p>Now, we need to define our actual <strong>presenter</strong>. It's supposed to present our game and return the related <code>squares</code>, <code>moves</code>, and <code>status</code>. Based on our current code, it needs the current <code>history</code>, <code>winner</code>, <code>xIsNext</code>, and <code>step</code>.</p>
<blockquote>
<p>A controller shouldn't depend directly on a presenter but on its interface.</p>
</blockquote>
<p>Let's create a <code>game-presenter.port.ts</code> inside <code>tic-tac-toe/ports/</code>:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="7ad03c9f530a28f85b113efa0f240547"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/7ad03c9f530a28f85b113efa0f240547" class="embed-card">https://gist.github.com/morintd/7ad03c9f530a28f85b113efa0f240547</a></div><p> </p>
<p>Now, we can add the implementation, as <code>game.presenter.ts</code> inside <code>tic-tac-toe/adapters/</code>:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="23d5d88f269f4343557a25fcb081d80b"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/23d5d88f269f4343557a25fcb081d80b" class="embed-card">https://gist.github.com/morintd/23d5d88f269f4343557a25fcb081d80b</a></div><p> </p>
<h3 id="heading-completing-controller">Completing controller</h3>
<p>Now, we can expect our <code>IGamePresenter</code> inside the <code>TicTacController</code> constructor (which we instantiate and inject from <code>app.ts</code>). Then, we can remove the method related to formatting and instead use the presenter:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="2217cabc6a3244135a71fb8769a54956"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/2217cabc6a3244135a71fb8769a54956" class="embed-card">https://gist.github.com/morintd/2217cabc6a3244135a71fb8769a54956</a></div><p> </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Your Back-End Tic-Tac-Toe game is now fully functional, and adheres to Clean Architecture! The domain (logic) is completely isolated from the infrastructure.</p>
<p>Feel free to look at the <a target="_blank" href="https://github.com/morintd/expressjs-tic-tac-toe-clean-architecture">end result</a>.</p>
<p>You would have no problem moving away from ExpressJS and using another Back-End framework like Fastify or NestJS. If you did, you wouldn't need to modify any code that's part of the domain layer.</p>
<p>Not only could you use another Back-End framework, but you could even make this game a <a target="_blank" href="https://morintd.hashnode.dev/build-a-react-application-with-clean-architecture">Front-End application</a>!</p>
]]></content:encoded></item><item><title><![CDATA[Understand the Theory behind Clean Architecture]]></title><description><![CDATA[From a global perspective, Clean Architecture is a software design approach focused on structuring systems in a way that promotes clarity, maintainability, and flexibility.
It emphasizes separation of concerns and independence of implementation detai...]]></description><link>https://blog.scalablebackend.com/understand-the-theory-behind-clean-architecture</link><guid isPermaLink="true">https://blog.scalablebackend.com/understand-the-theory-behind-clean-architecture</guid><category><![CDATA[Clean Architecture]]></category><category><![CDATA[clean code]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[Hexagonal Architecture]]></category><category><![CDATA[DDD]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Thu, 14 Mar 2024 19:19:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/1_PyztbCmeQ/upload/5d90861ea054879cafaa4df87760833d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>From a global perspective, Clean Architecture is a software design approach focused on structuring systems in a way that promotes clarity, maintainability, and flexibility.</p>
<p>It emphasizes separation of concerns and independence of implementation details. Its goals include creating systems that are easy to modify over time.</p>
<p>To be more precise, it prioritizes high-level policies and business rules, keeping them independent of low-level details like frameworks and databases, enabling testability, and long-term sustainability of the software.</p>
<p>A great starting point for Clean Architecture is Robert C. Martin (Uncle Bob) <a target="_blank" href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">blog post</a>.</p>
<h2 id="heading-history-behind-clean-architecture">History behind Clean Architecture</h2>
<p>The concept of <em>Clean Architecture</em> was first formulated by Robert C. Martin in his blog post from 2012. It was also popularised when he published his book "<a target="_blank" href="https://www.amazon.com/Clean-Architecture-Craftsmans-Software-Structure/dp/0134494164">Clean Architecture: A Craftsman's Guide to Software Structure and Design</a>" in 2017.</p>
<p>While Robert C. Martin was the one to define Clean Architecture, he didn't invent all the concepts it's built upon.</p>
<h3 id="heading-hexagonal-architecture">Hexagonal Architecture</h3>
<p>One of the first references from the original blog post is <em>Hexagonal Architecture</em> (also called <em>Ports &amp; Adapters</em>). It was originally a <a target="_blank" href="https://alistair.cockburn.us/hexagonal-architecture/">blog post</a>, written by Alistair Cockburn, in 2005.</p>
<p>Essentially, it aims to decouple core business logic of an application from its external dependencies, such as databases, user interfaces, and external services.</p>
<p>The solution suggested by Alistair Cockburn is to define a clear boundary between the core logic and the external interactions, through a set of "ports" and "adapters".</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710267536768/3fe719fa-df9c-41c5-a245-8e68666dd16a.png" alt class="image--center mx-auto" /></p>
<p>Ports represent interfaces or contracts that define how the core logic communicates with the external world. On the other hand, adapters are implementations of these ports.</p>
<h3 id="heading-onion-architecture">Onion Architecture</h3>
<p>A few years later, in 2008, Jeffrey Palermo published about <a target="_blank" href="https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/">Onion Architecture</a> on his blog. It also aims to decouple business logic from external dependencies but in a different way.</p>
<p>It does so by organizing the application into concentric layers, with the innermost layer representing the domain model or core business logic. This is how Jeffrey manages to isolate the core from external concerns like databases, frameworks, or UI.</p>
<p>To be precise, he defines 4 layers:</p>
<ul>
<li><p><strong>Core (Domain Model)</strong>: This innermost layer contains the domain model and business logic of the application. It encapsulates the essential behavior and rules of the system.</p>
</li>
<li><p><strong>Infrastructure (Domain Services)</strong>: Surrounding the core, it provides implementations for interfaces defined in the core layer. It deals with external concerns like database access, framework integrations, and other infrastructure-related operations.</p>
</li>
<li><p><strong>Application Services</strong>: This layer orchestrates the interaction between the core domain and the infrastructure. It contains use cases or application-specific operations that coordinate the flow of data and actions within the system.</p>
</li>
<li><p><strong>Presentation/UI</strong>: It's the outermost layer, which handles user interface concerns. It includes components responsible for user interaction like controllers, views, or API endpoints.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710270264346/c3c9498d-c30e-48b2-977f-224ea8213d41.png" alt class="image--center mx-auto" /></p>
<p>In practice, Onion Architecture relies heavily on Dependency Injection. That's how the core can be independent from external implementations, and rely on abstractions defined by interfaces instead. This allows for loose coupling and easier substitution of components.</p>
<h3 id="heading-others">Others</h3>
<p>Robert C. Martin talks about two main books, <a target="_blank" href="https://www.amazon.com/Lean-Architecture-Agile-Software-Development/dp/0470684208/">Lean Architecture: for Agile Software Development</a> and <a target="_blank" href="https://www.amazon.com/Object-Oriented-Software-Engineering-Approach/dp/0201544350">Object-Oriented Software Engineering: A Use Case Driven Approach</a>.</p>
<p>Both of those books influenced Clean Architecture but for different reasons.</p>
<p><strong>Lean Architecture</strong></p>
<p>In the first one, you'll read about <a target="_blank" href="https://en.wikipedia.org/wiki/Data,_context_and_interaction">Data, context and interaction</a>. The main point is to separate the domain model (data) from use cases (context) and roles that objects play (interaction).</p>
<p><strong>Object-Oriented Software Engineering</strong></p>
<p>Then, in the second book, we focus on <a target="_blank" href="https://en.wikipedia.org/wiki/Use_case">use cases</a>. You can see them as a list of actions defining the interactions between an actor and a system, to achieve a goal.</p>
<p><strong>Screaming Architecture</strong></p>
<p>Another approach can be found as part of Clean Architecture: <a target="_blank" href="https://blog.cleancoder.com/uncle-bob/2011/09/30/Screaming-Architecture.html">Screaming Architecture</a>. It's a concept formulated by Robert C. Martin, who says:</p>
<blockquote>
<p>If the plans you are looking at are for a single family residence, then you’ll likely see a front entrance, a foyer leading to a living room and perhaps a dining room. [...] As you looked at those plans, there’d be no question that you were looking at a <em>house</em>. The architecture would <em>scream</em>: <strong>house</strong>.</p>
</blockquote>
<p>Then, he asks:</p>
<blockquote>
<p>So what does the architecture of your application scream? When you look at the top level directory structure, and the source files in the highest level package; do they scream: <strong>Health Care System</strong>, or <strong>Accounting System</strong>, or <strong>Inventory Management System</strong>? Or do they scream: <strong>Rails</strong>, or <strong>Spring/Hibernate</strong>, or <strong>ASP</strong>?</p>
</blockquote>
<p>He's not only asking rhetorical questions but has a great conclusion for us:</p>
<blockquote>
<p>Just as the plans for a house or a library scream about the use cases of those buildings, so should the architecture of a software application scream about the use cases of the application.</p>
</blockquote>
<p><strong>Humble Object</strong></p>
<p>If you read "Clean Architecture: A Craftsman's Guide to Software Structure and Design", you will learn about a new pattern: <a target="_blank" href="http://xunitpatterns.com/Humble%20Object.html">Humble Object</a>. It was originally popularised by Gerard Meszaros in <a target="_blank" href="https://www.amazon.com/xUnit-Test-Patterns-Refactoring-Code/dp/0131495054">xUnit Test Patterns: Refactoring Test Code</a>.</p>
<p>The idea is to separate the behavior that's hard to test from the behaviour that's easy to test. It's achieved by splitting a functionality into two parts:</p>
<ul>
<li><p>A humble part kept as simple as possible, that contains the hard-to-test code (usually dealing with problematic dependencies).</p>
</li>
<li><p>A part that contains the logic or behavior that can be easily tested.</p>
</li>
</ul>
<p><strong>Component Design Principles</strong></p>
<p>From a general perspective, large systems are built upon smaller components that are working together.</p>
<p>In his book on Clean Architecture, Uncle Bob gives us a set of principles that define how component should be composed together. While this is a broader subject, I definitely recommend you <a target="_blank" href="https://blog.scalablebackend.com/understand-component-design-principles">read about it</a>.</p>
<h2 id="heading-clean-architecture">Clean Architecture</h2>
<p>There are a few concepts and rules that make Clean Architecture what it is today, as well as its specific layer organization. To get started, you can have a look at the following diagram:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710343480050/57af3ed5-0205-4cab-b206-686fc9431c5d.png" alt class="image--center mx-auto" /></p>
<p>I highly recommend keeping this diagram saved somewhere (or the original one from Robert C. Martin <a target="_blank" href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">blog post</a>). Thankfully, you don't have to understand everything about this diagram yet.</p>
<p>Right now, you will discover what those layers are, but will understand them more in-depth through practice. With time, you will come back to look at this illustration and find how much it makes sense.</p>
<p>As you can see, there are four layers, from the innermost to the outermost:</p>
<ul>
<li><p><strong>Entities</strong>: They represent the essential business objects or concepts that are relevant to the application. In practice, an entity encapsulates the most general and high-level business rules and logic. They are completely independent of external concerns and are essentially the heart of the application's business logic.</p>
</li>
<li><p><strong>Use Cases (or Interactors)</strong>: Use cases represent the application's behavior and define the actions that can be performed within the system. They encapsulate the workflow or the specific steps required to achieve a particular goal or perform a task. To be more precise, use cases orchestrate the flow of data and operations between the entities and the outer layers. Each use case typically corresponds to a specific user action or system operation.</p>
</li>
<li><p><strong>Interface Adapters</strong>: They act as the intermediary between the inner layers (Entities and Use Cases) and the outer layers (Frameworks and Drivers). They are responsible for translating data from the format most convenient for the use cases and entities into the format most convenient for the external frameworks and tools. This layer usually includes controllers in a traditional MVC architecture, as well as data mappers and gateways for external services.</p>
</li>
<li><p><strong>Frameworks and Drivers</strong>: The outmost layer consists of the external frameworks, tools, and devices such as the database, web framework, UI, etc. It deals with the delivery mechanisms and frameworks specific to the platform. It includes components such as web frameworks (Front-End or Back-End), databases, and other external systems and devices.</p>
</li>
</ul>
<blockquote>
<p>It's a common practice to call the group with Entities &amp; Use Case "Business" or "Domain", while the group with Interface Adapters &amp; Frameworks is called "Infrastructure".</p>
</blockquote>
<h3 id="heading-the-dependency-rule">The dependency rule</h3>
<p>Probably the most important concept from Clean Architecture. In his blog post, Uncle Bob says:</p>
<blockquote>
<p>This rule says that <em>source code dependencies</em> can only point <em>inwards</em>. Nothing in an inner circle can know anything at all about something in an outer circle.</p>
<p>In particular, the name of something declared in an outer circle must not be mentioned by the code in the inner circle. That includes functions, classes. variables, or any other named software entity.</p>
<p>By the same token, data formats used in an outer circle should not be used by an inner circle, especially if those formats are generate by a framework in an outer circle. We don’t want anything in an outer circle to impact the inner circles.</p>
</blockquote>
<p>Now, you might ask: how is this possible? For example, when a use case needs to communicate through an adapter (defined inside or outside of its layer).</p>
<p>This issue is solved thanks to Dependency Injection. Usually, an interface is defined inside an inner layer, and implemented inside the outer layer. This structure is illustrated in the bottom right of the Clean Architecture diagram.</p>
<p>There, we can see the use case (or interactor) uses a presenter, which belongs to an outer layer. In order to conform to the Dependency Rule, the use case depends on an interface defined in its own layer. The actual presenter is implemented in the outer layer.</p>
<h3 id="heading-common-misconception">Common misconception</h3>
<p><strong>Data Access</strong></p>
<p>While it's tempting to define data access gateways inside the Framework &amp; Drivers layer, that's not where they belong! Simply because you use a framework or driver to request your database doesn't make your gateway part of the outermost layer.</p>
<p>The important characteristic is how data flows: it's a gateway for data access. It's pretty obvious in the Clean Architecture diagram: it belongs to the Interface Adapters layer.</p>
<p><strong>Controller</strong></p>
<p>Often, if you try to adhere strictly to Clean Architecture, you will have a problem with the definition of your controller. In most applications, controllers use some sort of functionality from a framework, making it incompatible with the interface adapter layer.</p>
<p>The truth is: controllers can have a role that spans multiple layers. They are usually a part of both the Framework &amp; Driver and Interface Adapters layer. In practice, you could make them independent of the Framework &amp; Driver layer by:</p>
<ul>
<li><p>Splitting every controller in two (one in each layer)</p>
</li>
<li><p>Converting the response format from one layer to the other.</p>
</li>
</ul>
<p>Again, in practice, this is a terrible idea that has multiple downsides without bringing any real value.</p>
<p><strong>Presenter</strong></p>
<p>The presenter is not a common concept and can be quite confusing at first. It's defined by Robert C. Martin in cooperation with the view (in the context of UI) and is introduced at the same time as the Humble Object pattern.</p>
<p>That's because the view should be treated as the hard-to-test, humble object, while the presenter is the testable object. To be more precise, the presenter is responsible for formatting data that is passed to the view.</p>
<p><em>That also means the view should be kept as simple as possible.</em></p>
<p>The presenter is sometimes confused with the controller in Front-End applications. They have a completely different role: while the controller orchestrates the actions taken by an actor with the use cases, the presenter formats data received by the view.</p>
<p><strong>Crossing boundaries</strong></p>
<p>Uncle Bob is very clear when it comes to data crossing boundaries. Not only should you use Dependency Injection to respect the Dependency Rule, but you should also be careful about what type of data goes from one layer to another.</p>
<p>In his blog post, we can read:</p>
<blockquote>
<p>Typically the data that crosses the boundaries is simple data structures. You can use basic structs or simple Data Transfer objects if you like.</p>
</blockquote>
<p>You don't want some data structure that comes from a framework or library to go around your different layers. Instead, you want to convert them to simple data structure.</p>
<p>In the same blog post, there is also:</p>
<blockquote>
<p>We don’t want to cheat and pass <em>Entities</em> or Database rows.</p>
</blockquote>
<p>Be careful about entities, this is not an absolute rule. For example, the default practice is to return entities from your data access gateway (usually <em>repositories</em> by the way).</p>
<p>On the other hand, 99% of the time, you don't want your entity to leave the use case layer. An acceptable exception could be if you have specific entities related to authentication, which are heavily used at the controller level.</p>
<p>Otherwise, most of the time your entities leave the use case layer, it's an architecture smell.</p>
<p><strong>Only 4 layers?</strong></p>
<p>While Clean Architecture is presented with 4 amazing layers, it can be changed depending on your application needs. While it's not common, removing a layer can simplify your architecture, even though it could lead to decreased modularity and flexibility.</p>
<p>Adding a layer is also entirely possible. Sometimes we can find an added Domain layer, which sits between the Entities and Use Cases layers.</p>
<p>The Domain layer typically contains domain-specific logic and rules that are fundamental to the problem domain being of the application. This layer helps to keep the business logic isolated and cohesive, making it easier to understand and maintain.</p>
<h3 id="heading-in-practice">In practice</h3>
<p>I highly recommend you have a look a two other articles that explain how to implement Clean Architecture. They are both a Tic-Tac-Toe game for:</p>
<ul>
<li><p><a target="_blank" href="https://morintd.hashnode.dev/build-a-react-application-with-clean-architecture">Front-End with ReactJS</a></p>
</li>
<li><p><a target="_blank" href="https://blog.scalablebackend.com/build-an-expressjs-application-with-clean-architecture">Back-End with ExpressJS</a></p>
</li>
</ul>
<h2 id="heading-what-clean-architecture-is-not">What Clean Architecture is not</h2>
<h3 id="heading-domain-driven-design-ddd">Domain-Driven-Design (DDD)</h3>
<p>Often, we find concepts that originate from DDD discussed in Clean Architecture spaces. For examples:</p>
<ul>
<li><p>Domain services</p>
</li>
<li><p>Domain events</p>
</li>
<li><p>Domain-specific value objects</p>
</li>
</ul>
<p>Are all rooted in Domain-Driven-Design. While they can be a great addition to Clean Architecture, they're not strictly a part of it.</p>
<h3 id="heading-cqscqrs">CQS/CQRS</h3>
<p>Often, instead of use cases, you will find commands and queries in applications made with Clean Architecture. They are principles that come from <a target="_blank" href="https://en.wikipedia.org/wiki/Command%E2%80%93query_separation">CQS</a> &amp; <a target="_blank" href="https://en.wikipedia.org/wiki/Command_Query_Responsibility_Segregation">CQRS</a>, which have a great synergy with Clean Architecture.</p>
<p>Similarly to concepts from DDD, they are homework not directly a part of Clean Architecture.</p>
<h2 id="heading-difference-with-onion-architecture">Difference with Onion Architecture</h2>
<p>Even though Onion Architecture looks similar to Clean Architecture, there are fundamental differences between both:</p>
<ul>
<li><p><strong>Layer Organization</strong>: Onion Architecture organizes layers based on their roles (core, infrastructure, application, presentation), while Clean Architecture organizes layers based on the flow of data and dependencies (entities, use cases, interface adapters, frameworks/drivers).</p>
</li>
<li><p><strong>Dependency Direction</strong>: In Onion Architecture, dependencies are typically inverted, with the core depending on abstractions defined in higher-level layers. In Clean Architecture, dependencies flow inward toward higher-level policies and business rules.</p>
</li>
<li><p><strong>Focus</strong>: Onion Architecture focuses on separating concerns through layered architecture, while Clean Architecture emphasizes dependency management and separation of concerns based on data flow.</p>
</li>
</ul>
<hr />
<p>Are looking for more in-depth resources about <strong><em>Clean Architecture</em></strong>? Let me know <a target="_blank" href="https://www.scalablebackend.com/courses/everything-about-clean-architecture-on-the-backend">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Understand Component Design Principles]]></title><description><![CDATA[The set of principles provided by Component Design gives great insights into how to organize components inside an application.
It's also one of the requirements to understand Clean Architecture. That's the reason multiple pieces of knowledge come fro...]]></description><link>https://blog.scalablebackend.com/understand-component-design-principles</link><guid isPermaLink="true">https://blog.scalablebackend.com/understand-component-design-principles</guid><category><![CDATA[software architecture]]></category><category><![CDATA[Clean Architecture]]></category><category><![CDATA[clean code]]></category><category><![CDATA[design patterns]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Wed, 01 Nov 2023 17:58:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/R2HtYWs5-QA/upload/2b3f85bbc2510e5cc5a67301fca65844.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The set of principles provided by <strong><em>Component Design</em></strong> gives great insights into how to organize components inside an application.</p>
<p>It's also one of the requirements to understand <strong><em>Clean Architecture</em></strong>. That's the reason multiple pieces of knowledge come from <a target="_blank" href="https://www.amazon.com/dp/0134494164">Clean Architecture</a>, which makes use of other references such as <a target="_blank" href="https://www.amazon.com/Object-Oriented-Software-Engineering-Approach/dp/0201544350">A Use Case Driven Approach</a> or <a target="_blank" href="https://www.amazon.com/Lean-Architecture-Agile-Software-Development/dp/0470684208/">Lean Architecture</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<h3 id="heading-solid">SOLID</h3>
<p>Inside this article, lots of principles you will learn are actually derived from <strong><em>SOLID</em></strong> (but applied to components). You definitely need to understand it first, before reading any further.</p>
<h3 id="heading-classes">Classes</h3>
<p>In the following part of this article, and probably most articles about architecture, you will read about organizing <strong><em>classes</em></strong>.</p>
<p>It definitely makes sense if your whole application is built on top of OOP, but might get you to second-guess information when your code is not using classes.</p>
<p>Feel free to substitute classes for the structure you are actually using (be it functions, or anything else).</p>
<h2 id="heading-what-are-components">What are components?</h2>
<p>A very simple definition of a component is a <strong><em>unit of deployment,</em></strong> which is the smallest <strong><em>entity</em></strong> that makes up a system. In practice, components take different forms depending on the language they are made with.</p>
<p>In interpreted languages, they are aggregated source files. On the other hand, they are closer to binary files with compiled languages (JAR files for Java or gem files for Ruby).</p>
<p>In the end, components can be used together to form a complete system. Potentially, they can be independently deployable, but more importantly, independently developable.</p>
<p>While those definitions are quite abstract, they include all the possible forms of components. In practice, we can think of components as part of this practical but non-comprehensive list:</p>
<ul>
<li><p>Multiple applications working together to create a system</p>
</li>
<li><p>Packages (or libraries)</p>
</li>
<li><p>Group of source files (sometimes called modules)</p>
</li>
</ul>
<h2 id="heading-cohesion">Cohesion</h2>
<p>When it comes to Component Design, you will often get asked "<strong><em>Which classes belong to which components?</em></strong> ". Usually, those decisions are made based on context, but three main <strong><em>Component Cohesion Principles</em></strong> help you give a better answer.</p>
<h3 id="heading-reuserelease-equivalence-principle-rep">Reuse/Release Equivalence Principle (REP)</h3>
<p>This principle tells us components should only be re-used when they are tracked through a release process (and given an identifier when released).</p>
<p>Obviously, it's necessary to ensure components work together when new versions get released. Developers must be told when a new version is released, as well as which changes are included. Only then can they decide whether to use a new version or not.</p>
<p>As a general rule of thumb, all the elements that make up a component should be releasable together. They must be grouped because it makes sense to users, but "making sense" is not precise enough advice, hence the following two principles.</p>
<h3 id="heading-common-closure-principle-ccp">Common Closure Principle (CCP)</h3>
<p>The <strong><em>Common Closure Principle</em></strong> can be seen as the <strong><em>Single Responsibility Principle</em></strong>, applied to components:</p>
<blockquote>
<p>Gather into components those classes that change for the same reasons and at the same times. Separate into different components those classes that change at different times.</p>
</blockquote>
<p>To be more straightforward, components should not have multiple reasons to change (just like a class). In the end, the same challenges apply:</p>
<ul>
<li><p>It's easier for maintainability to gather all the changes in a single component, instead of many components</p>
</li>
<li><p>When changes occur in a single component, you only need to (re)validate whoever uses this specific component.</p>
</li>
</ul>
<h3 id="heading-common-reuse-principle-crp">Common Reuse Principle (CRP)</h3>
<p>The <strong><em>Common Reuse Principle</em></strong> is closer to the <strong><em>Interface Segration Principle</em></strong>, albeit more generic:</p>
<blockquote>
<p>Don't force users of a component to depend on things they don't need.</p>
</blockquote>
<p>Like the previous principle, it helps decide which classes and modules should be placed into a component. Typically, you will find classes with multiple dependencies between each other.</p>
<p>On the other hand, this principle also tells us which classes should <strong>not</strong> be kept together. From a general standpoint, classes that are not tightly bound to each other shouldn't be in the same component.</p>
<blockquote>
<p>Don't depend on things you don't need.</p>
</blockquote>
<h3 id="heading-tension-diagram">Tension Diagram</h3>
<p>If you understood those principles correctly, you realized they have an incompatible relationship with each other: <strong><em>REP</em></strong> and <strong><em>CCP</em></strong> are inclusive, while <strong><em>CRP</em></strong> is exclusive.</p>
<p>Everything is about balance, which in this case, is called tension between the three principles:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698665626259/07115a0f-6ece-49a1-a791-4809e04ba721.png" alt="Cohesion Principles Tension Diagram" class="image--center mx-auto" /></p>
<p>The goal of any great architect is to find the right place on this tension diagram for its current system. Focusing too much on REP and CRP will cause too many components to be updated when a change is made. On the other hand, focusing solely on CCP and REP will increase the number of necessary releases</p>
<h2 id="heading-coupling">Coupling</h2>
<p>In the previous section, we talked multiple times about the relationship between components. But how exactly do we design those components, their relationship, and how coupled they are?</p>
<h3 id="heading-acyclic-dependencies-principle-adp">Acyclic Dependencies Principle (ADP)</h3>
<p>It's mandatory for components to have no cycles in their dependency tree. Take the following dependency graph for example (where components are nodes and dependencies are edges):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698680834141/80a5035f-c3f1-4287-94c0-e30ec9fd4334.png" alt class="image--center mx-auto" /></p>
<p>No matter which components you take and follow dependencies, you will always end up with <em>entities</em>. It's impossible to follow dependencies back to the same component.</p>
<p>In other words, this dependency graph has no cycles, it's a directed acyclic graph.</p>
<p>When a new version of a component is released, it's easy to find out which components are affected: you can follow the dependencies backwards.</p>
<p>Teams responsible for the affected components need to validate everything works fine with the new version. The unaffected components don't need to undergo the same process.</p>
<p>Now let's take another example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698680847662/847f1b43-44c4-487e-93e6-d5bb33ee08df.png" alt class="image--center mx-auto" /></p>
<p>Here, we can see a new dependency between <em>Entities</em> and <em>Authorizer</em>, adding a cyclic dependency to this graph. Immediately, we face new issues.</p>
<p>When a new version of a component is released, it has to be compatible with many more components. For example <em>Database</em> must ensure it works not only with <em>Entities</em>, but also <em>Authorizer</em>.</p>
<p>That's the case for every component that uses <em>Entities</em>, including <em>Interactors</em>. That also means Entities cannot be built in autonomy, but must integrate with <em>Authorizer</em> and <em>Interactors</em>. Generally, testing and releases are rendered bothersome.</p>
<p>To avoid cyclic dependencies there are two main solutions:</p>
<ul>
<li><p>Use <strong><em>Dependency Inversion Principle</em></strong> (DIP)</p>
</li>
<li><p>Instead of adding a new relationship between two components, create a new component, they both depend on.</p>
</li>
</ul>
<p>But don't forget:</p>
<blockquote>
<p>Allow no cycles in the component dependency graph.</p>
</blockquote>
<h3 id="heading-stable-dependencies-principle-sdp">Stable Dependencies Principle (SDP)</h3>
<p>Components and their relationship are volatile by design. Following the Common Closure Principle leads us to create components affected by some changes and immune to others.</p>
<p>As a general rule, a volatile component shouldn't be depended on by a component that is difficult to change. Otherwise, the volatile component will be difficult to change as well.</p>
<p>Be careful, a module that is easy to change can be made difficult to change when depended on by a volatile dependency.</p>
<p>That's the reason the <strong><em>Stable Dependencies Principle</em></strong> tells us:</p>
<blockquote>
<p>Modules that are intended to be easy to change should not be depended on by modules that are harder to change.</p>
</blockquote>
<p>Usually, a component will be harder to change (or <em>stable</em>), when it is depended on by multiple dependencies. It will also tend to be more volatile when it depends on more dependencies.</p>
<p>Another way to understand the <strong><em>Stable Dependencies Principle</em></strong> is the degree of volatility should decrease the deeper you go into the dependency graph.</p>
<p>Unfortunately, it will happen that one of your stable components depends on one that's volatile. Similar to the previous principle, it's possible to solve this issue by:</p>
<ul>
<li><p>Using <strong><em>Dependency Inversion Principle</em></strong></p>
</li>
<li><p>Creating another component depended on by the other two.</p>
</li>
</ul>
<p>To make it more simple:</p>
<blockquote>
<p>Depend in the direction of stability</p>
</blockquote>
<h3 id="heading-stable-abstraction-principle-sap">Stable Abstraction Principle (SAP)</h3>
<p>Usually, some part of a software is made to rarely change. That's what we call high-level policy. This type of business and architecture decision is intended to be stable, not volatile.</p>
<p>However, if the code for those policies is placed into a stable component, it will be made hard to change. Sometimes, you will need to have a stable high-level policy, which implementation should be easier to change, but how?</p>
<p>We can get some inspiration from the <strong><em>Open-Closed Principle</em></strong> (which is part of SOLID). It tells us it's possible (and desirable) to create classes flexible enough to be extended without requiring modification: <strong><em>Abstract</em></strong> classes.</p>
<p>This is where the <strong><em>Stable Abstraction Principle</em></strong> comes in. It defines a relationship between stability and abstractness:</p>
<ul>
<li><p>A stable component should also be abstract so that its stability does not prevent it from being extended.</p>
</li>
<li><p>An unstable component should be concrete (not abstract) since its instability allows the concrete code within it to be easily changed.</p>
</li>
</ul>
<p>In turn:</p>
<blockquote>
<p>If a component is to be stable, it should consist of interfaces and abstract classes so that it can be extended.</p>
</blockquote>
<p>If we combine the <strong><em>Stable Dependencies Principle</em></strong> and <strong><em>Stable Abstraction Principle</em></strong>, we are able to make a component partially abstract or stable. There is no perfect level of abstraction or stability, it depends on the policies you wish to express.</p>
<p>What you should aim for, is the right level of abstraction compared to the level of stability necessary for your components.</p>
<p>For example, a component that is both stable and concrete will be painful to maintain. It can hardly be extended because it's not abstract, and very difficult to change because it's stable.</p>
<p>On the other hand, a component that is both volatile and abstract will probably be useless. That means it expresses abstract policies, which nobody depends on.</p>
<hr />
<p>Do you want to learn more about <strong><em>Clean Architecture</em></strong>? Let me know <a target="_blank" href="https://www.scalablebackend.com/courses/everything-about-clean-architecture-on-the-backend">here</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Everything You Need To Get Started With Docker]]></title><description><![CDATA[Maybe you heard about Docker but don't fully understand its goal and inner functioning. On the other hand, having a clear understanding of Docker is necessary to not waste your time and resources.
In this article, I give you the necessary knowledge t...]]></description><link>https://blog.scalablebackend.com/everything-you-need-to-get-started-with-docker</link><guid isPermaLink="true">https://blog.scalablebackend.com/everything-you-need-to-get-started-with-docker</guid><category><![CDATA[Docker]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Express.js]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Fri, 29 Sep 2023 17:23:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/1cqIcrWFQBI/upload/fa827fc6e35cadcda2060022d1078c74.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Maybe you heard about Docker but don't fully understand its goal and inner functioning. On the other hand, having a clear understanding of Docker is necessary to not waste your time and resources.</p>
<p>In this article, I give you the necessary knowledge to get started efficiently with Docker, and resources to look at in the future, but you definitely need practice to get there.</p>
<h2 id="heading-what-is-docker">What is Docker?</h2>
<p>From the <a target="_blank" href="https://docs.docker.com/get-started/overview/">Docker documentation</a>, we learn:</p>
<blockquote>
<p><strong>Docker</strong> provides the ability to package and run an application in a loosely isolated environment called a container.</p>
</blockquote>
<p>That's a great definition of what Docker actually is, but if you're just getting started, it might not be easy to understand. Instead, I would like to describe Docker (and containers) in opposition to virtual machines.</p>
<h3 id="heading-virtual-machine">Virtual Machine</h3>
<p>Maybe you worked with virtual machines in the past, and they are definitely easier to approach.</p>
<p>They can be seen as software that simulates (or <a target="_blank" href="https://www.ibm.com/topics/virtualization">virtualize</a>s) a separate environment, on a host machine. For virtual machines, it's possible to interact with physical components (almost) directly and allocate dedicated processors or memory for example.</p>
<p>To be more precise, virtual machines use a piece of software called <a target="_blank" href="https://www.ibm.com/topics/hypervisors">hypervisor</a> to interact with the physical part of your computer.</p>
<p>In the end, it allows you to simulate a complete operating system, separated from everything else. On the other hand, it makes it heavy by nature, as it simulates a complete environment, using a hypervisor (even though lightweight).</p>
<h3 id="heading-container">Container</h3>
<p>A container (of which Docker is a provider, but there are <a target="_blank" href="https://jfrog.com/fr/devops-tools/article/alternatives-to-docker/">alternatives</a>) is slightly different. It achieves the same goal of virtualization but with another strategy.</p>
<p>Instead of building a complete virtual machine, a container can be seen as a package that contains everything needed for your system to work. That often includes a light operating system, runtime libraries, and your code.</p>
<p>Containers still work in separate processes but don't use an hypervisor. You can see the separation between containers as softer than virtual machines, which also makes it more performant.</p>
<p>To be more precise, instead of virtualizing physical components like virtual machines, containers only virtualize the operating system. They make use of the host operating system, its features, and its resources.</p>
<h2 id="heading-what-problems-does-docker-solve">What problems does Docker solve?</h2>
<p>Docker is a great solution to one modern challenge: automation. Because containers are lightweight, they can be easily built and deployed. It makes development, testing, and deployment faster hence cheaper.</p>
<p>Your applications often need a specific environment to run in. It includes an operating system (with a specific version), runtimes, libraries and frameworks, and sometimes more.</p>
<p>This is where the strength of containers, and Docker, is revealed. It makes it very easy to define the environment you need.</p>
<p>For example, setting up a new local environment becomes extremely fast and easy. Tests can run in the same environment, as well as deployment to minimize issues with your infrastructure.</p>
<h2 id="heading-theory-with-docker">Theory with Docker</h2>
<p>To get started, there are three main concepts to understand with Docker:</p>
<ul>
<li><p>Dockerfile</p>
</li>
<li><p>Image</p>
</li>
<li><p>Container</p>
</li>
</ul>
<h3 id="heading-container-1">Container</h3>
<p>Previously, we only talked about containers, which are the final building blocks. At this point, you should understand they are a running piece of software packaged with your application and all the necessary dependencies for it.</p>
<p>Usually, you give access to the application running on your container to the host machine so it becomes usable. But how do we get there?</p>
<h3 id="heading-image">Image</h3>
<p>If your container is actually running, based on a package with everything you need, the previous step is to build the necessary package.</p>
<p>Docker comes with the concept of <em>image</em>, which is exactly what you would expect from a <em>package</em>, a binary with all your dependencies.</p>
<p>Images are built on top of each other. For example, the most basic ones only provide a lightweight operating system like <a target="_blank" href="https://hub.docker.com/_/ubuntu">Ubuntu</a>, <a target="_blank" href="https://hub.docker.com/_/debian">Debian</a>, or <a target="_blank" href="https://hub.docker.com/_/alpine">Alpine</a>.</p>
<p>A more advanced docker image is <a target="_blank" href="https://hub.docker.com/_/node/">node</a>, which includes the NodeJS runtime on top of Debian (by default). Typically, you would build your own image on top of this one, and include your source code.</p>
<p>At this point, you might have one remaining question:</p>
<blockquote>
<p>How do we define the content of an image and build it?</p>
</blockquote>
<h3 id="heading-dockerfile">Dockerfile</h3>
<p>Docker works with a <a target="_blank" href="https://docs.docker.com/build/guide/intro/">configuration system</a> based on a text file called Dockerfile. The first example from the introduction is for a <a target="_blank" href="https://go.dev">go</a> application:</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># syntax=docker/dockerfile:1</span>
<span class="hljs-keyword">FROM</span> golang:<span class="hljs-number">1.20</span>-alpine
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /src</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .</span>
<span class="hljs-keyword">RUN</span><span class="bash"> go mod download</span>
<span class="hljs-keyword">RUN</span><span class="bash"> go build -o /bin/client ./cmd/client</span>
<span class="hljs-keyword">RUN</span><span class="bash"> go build -o /bin/server ./cmd/server</span>
<span class="hljs-keyword">ENTRYPOINT</span><span class="bash"> [ <span class="hljs-string">"/bin/server"</span> ]</span>
</code></pre>
<p>The first <strong>FROM</strong> instruction defines the base image we are building on top of. Those images usually come from <a target="_blank" href="https://hub.docker.com">DockerHub</a>, where you can upload your images.</p>
<p>Then, the <strong>WORKDIR</strong> instruction defines the working directory, where we will run commands by default.</p>
<p><strong>COPY</strong> is used to copy files from the host machine to the image, usually the source code.</p>
<p>As the name suggests, <strong>RUN</strong> is responsible for running commands on the image in the process of being built. In this example, go is used to install dependencies and build client and server applications, based on code copied previously.</p>
<p>Finally, <strong>ENTRYPOINT</strong> defines the default command used when starting a container based on this image. It's necessary, as you often need to start your application with the container, and a container will stop if the main process is not busy.</p>
<p>You can find more references in the <a target="_blank" href="https://docs.docker.com/engine/reference/builder/">Docker documentation</a>.</p>
<h2 id="heading-using-docker">Using Docker</h2>
<p>Now that you have a better understanding of Docker, you have to actually write Dockerfiles, build images, run containers, and more!</p>
<h3 id="heading-setup">Setup</h3>
<p>At this point, you will have to install the <a target="_blank" href="https://docs.docker.com/engine/install/">Docker engine</a> and use its <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/cli/">CLI</a>.</p>
<p>Please, clone <a target="_blank" href="https://github.com/morintd/hello-world">this project</a>, which we will use in the next part. It's a simple NodeJS backend application that answers with "hello world" on the root path (/).</p>
<h3 id="heading-create-a-dockerfile">Create a Dockerfile</h3>
<p>Inside the cloned repository, I want you to create a file called <strong><em>Dockerfile</em></strong> with the following content:</p>
<pre><code class="lang-dockerfile"><span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18.18</span>.<span class="hljs-number">0</span>

<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>

<span class="hljs-keyword">COPY</span><span class="bash"> . /app</span>

<span class="hljs-keyword">RUN</span><span class="bash"> yarn</span>

<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">8000</span>

<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"yarn"</span>, <span class="hljs-string">"start"</span>]</span>
</code></pre>
<p>In the previous section, we already explained most of the instructions we use in a Dockerfile. There are a few differences here:</p>
<ul>
<li><p>We are using an image for NodeJS with <a target="_blank" href="https://hub.docker.com/layers/library/node/18.18.0/images/sha256-159f7142e5dc759c6640b2a60a91a6ec15831e4bdcc1f5d9c1646d86e53df270?context=explore">version 18.18.0</a>.</p>
</li>
<li><p>We install dependencies with yarn (instead of using go).</p>
</li>
<li><p>We <strong>expose</strong> port 8000, which makes it accessible from outside the container.</p>
</li>
<li><p>We start the app with <strong>yarn start</strong>.</p>
</li>
</ul>
<h3 id="heading-create-an-image">Create an image</h3>
<p>Now, you can open a console inside the same directory and run the command:</p>
<pre><code class="lang-bash">docker image build . -t hello-world
</code></pre>
<p>Here, we start with the <strong><em>docker</em></strong> command to access its CLI, <strong><em>image</em></strong> to access actions for images, and <strong><em>build</em></strong> to create the actual image.</p>
<p>It takes one argument with the path to the directory with the Dockerfile to build into an image, <strong><em>"."</em></strong> for the actual directory.</p>
<p>While it's not necessary, the <strong><em>"-t"</em></strong> flag defines the <strong><em>name</em></strong> (and optionally <strong><em>:tag</em></strong>) for the generated image. You can have a look at the full documentation for the <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/image_build/">image build</a> as well as all the commands for <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/image/">image</a>.</p>
<h3 id="heading-list-images">List images</h3>
<p>Another useful command is to list images, thanks to the <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/image_ls/">image ls</a> command:</p>
<pre><code class="lang-bash">docker image ls
</code></pre>
<p>It's not the command you will use the most, but keep in mind it exists, mainly if you want to clean memory taken by docker.</p>
<h3 id="heading-introduction-to-containers">Introduction to containers</h3>
<p>While you can <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/container_create/">create</a> and <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/container_start/">start</a> a container separately, a more common practice is to use <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/container_run/">run</a> to do both at the same time.</p>
<p>We need to define which image to run our container with, but also define a port accessible on the host machine, and which port it corresponds to, on the container.</p>
<p>We <strong>exposed</strong> port 8000 from the Dockerfile, we can re-use it and link it to the same port 8000 on the host machine, thanks to the "-p" flag:</p>
<pre><code class="lang-bash">docker container run -p 8000:8000 hello-world
</code></pre>
<p>That's it, your container should be up and running. You can try to open <code>http://localhost:8000</code> on your browser and should see a message with "hello world".</p>
<h3 id="heading-list-containers">List containers</h3>
<p>From another console, you can <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/container_ls/">list</a> containers and have multiple commands to do it. In theory, the official command is:</p>
<pre><code class="lang-bash">docker container ls
</code></pre>
<p>But the truth is, you can also use:</p>
<pre><code class="lang-bash">docker container ps
</code></pre>
<p>Which can be shortened, and is the most used:</p>
<pre><code class="lang-bash">docker ps
</code></pre>
<p>No matter which exact command you are using, you can add the "-a" flag to list all containers, including the stopped ones:</p>
<pre><code class="lang-bash">docker ps -a
</code></pre>
<h3 id="heading-stop-a-container">Stop a container</h3>
<p>Among the information displayed when listing containers, you have access to the <strong>container ID</strong>.</p>
<p>After finding the ID of the currently running container, you can use the stop command:</p>
<pre><code class="lang-bash">docker container stop [CONTAINER ID]
</code></pre>
<h3 id="heading-start-an-existing-container">Start an existing container</h3>
<p>You can use the <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/container_start/">start</a> command:</p>
<pre><code class="lang-bash">docker container start [CONTAINER ID]
</code></pre>
<h3 id="heading-interact-with-a-container">Interact with a container</h3>
<p>During your day-to-day work with Docker, you often need to interact with your containers. It can be to see what's going on, move files around, etc.</p>
<p>You can use <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/container_exec/">exec</a> to run a single command on a container:</p>
<pre><code class="lang-bash">docker container <span class="hljs-built_in">exec</span> [CONTAINER ID] [COMMAND]
</code></pre>
<p>For a more complex use case, it's possible to keep a console attached and interactive with your container.</p>
<p>It's done thanks to the <strong><em>interactive</em></strong> and <strong><em>tty</em></strong> flags (found in the <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/container_exec/">exec</a> documentation). To be more precise, you can open an interactive console, using the basic <strong><em>sh</em></strong> command.</p>
<p>The <strong><em>interactive</em></strong> and <strong><em>tty</em></strong> flags can be shortened to <strong><em>"-it"</em></strong>:</p>
<pre><code class="lang-bash">docker container <span class="hljs-built_in">exec</span> -it [CONTAINER ID] sh
</code></pre>
<p>To easily leave the interactive console, you can use <strong>CMD + D</strong>.</p>
<h3 id="heading-free-disk-space">Free disk space</h3>
<p>If you are working heavily with Docker, but don't have a huge amount of available disk space, you might use it all.</p>
<p>Keep in mind you have access to a <strong><em>prune</em></strong> command for <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/image_prune/">images</a> and <a target="_blank" href="https://docs.docker.com/engine/reference/commandline/container_prune/">containers</a>, which can come in handy.</p>
<h2 id="heading-docker-compose">Docker Compose</h2>
<p>While using Docker by itself is a great start, I bet you will very quickly need <a target="_blank" href="https://docs.docker.com/compose/">Docker Compose</a>.</p>
<p>If Docker allows you to manipulate images and containers, docker-compose makes it possible to manage multiple containers and make them work together.</p>
<h3 id="heading-setup-1">Setup</h3>
<p>From a general perspective, Docker Compose works with a <a target="_blank" href="https://docs.docker.com/compose/compose-file/03-compose-file/">configuration file</a> that defines how your containers interact with each other. If you have Docker installed, you should also have Docker Compose and its CLI.</p>
<p>For this exercise, please clone a <a target="_blank" href="https://github.com/morintd/express-todo">todo backend</a> made with NodeJS. It needs a PostgreSQL database, which means we also need to <a target="_blank" href="https://www.prisma.io/docs/guides/deployment/deploy-database-changes-with-prisma-migrate">run migrations</a> with Prisma.</p>
<h3 id="heading-dockerfile-1">Dockerfile</h3>
<p>We will use a slightly different Dockerfile for this example:</p>
<pre><code class="lang-dockerfile"><span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18.18</span>.<span class="hljs-number">0</span>

<span class="hljs-keyword">RUN</span><span class="bash"> apt-get update &amp;&amp; apt-get install tini</span>

<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>

<span class="hljs-keyword">COPY</span><span class="bash"> . /app</span>

<span class="hljs-keyword">RUN</span><span class="bash"> yarn</span>

<span class="hljs-keyword">EXPOSE</span> <span class="hljs-number">8000</span>

<span class="hljs-keyword">ENTRYPOINT</span><span class="bash"> [<span class="hljs-string">"/usr/bin/tini"</span>, <span class="hljs-string">"--"</span>]</span>
</code></pre>
<p>Here, we are installing and using <a target="_blank" href="https://github.com/krallin/tini">tini</a>, which mainly keeps the container alive and improves signal forwarding.</p>
<p>We are not starting the app here, but that can be configured inside the docker-compose file. It's necessary, as starting the app requires access to a PostgreSQL database.</p>
<h3 id="heading-compose-configuration">Compose configuration</h3>
<p>While there are more advanced features inside Docker Compose (and Docker, such as <a target="_blank" href="https://docs.docker.com/compose/networking/">networks</a> and <a target="_blank" href="https://docs.docker.com/storage/volumes/">volumes</a>), we will focus on <a target="_blank" href="https://docs.docker.com/compose/compose-file/05-services/">services</a>.</p>
<p>While services are more complex in reality, you can see each of them as a definition for a container. Here, we need two containers:</p>
<ul>
<li><p>One with a PostgreSQL database</p>
</li>
<li><p>Another with our application, which depends on the first</p>
</li>
</ul>
<p>Good news, there is a <a target="_blank" href="https://hub.docker.com/_/postgres">postgres</a> image, which can be easily used. We can define our services with this image first:</p>
<pre><code class="lang-dockerfile">services:
  postgres:
    image: postgres:alpine
    restart: always
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    ports:
      - <span class="hljs-string">'5432:5432'</span>
</code></pre>
<p>Then, we can add a service for our <strong><em>hello-world</em></strong> application, and configure it with its Dockerfile. It can be done using the <strong><em>build</em></strong> property, which takes the path with the necessary Dockerfile:</p>
<pre><code class="lang-dockerfile">services:
  postgres:
    image: postgres:alpine
    restart: always
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    ports:
      - <span class="hljs-string">'5432:5432'</span>
  hello-world:
    build: .
</code></pre>
<p>Unfortunately, that's not good enough, we need to:</p>
<ul>
<li><p>Configure the necessary environment variables.</p>
</li>
<li><p>Configure the container port to the host machine port.</p>
</li>
<li><p>Start the application, which should access the container for postgres.</p>
</li>
</ul>
<pre><code class="lang-dockerfile">services:
  postgres:
    image: postgres:alpine
    restart: always
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    ports:
      - <span class="hljs-string">'5432:5432'</span>
  hello-world:
    build: .
    environment:
      - DATABASE_URL=postgres://postgres:postgres@postgres/express-todo
      - PORT=<span class="hljs-number">8000</span>
      - NODE_ENV=development
    depends_on:
      - postgres
</code></pre>
<p>Here, we configured the environment variable, using the username and password <strong><em>postgres</em></strong>, as configured in the first service.</p>
<p>We also use <strong><em>postgres</em></strong> as hostname for the database, which is provided by Docker Compose, as the service itself is called <strong><em>postgres</em></strong>.</p>
<p>We use <strong><em>PORT</em></strong> 8000 and <strong><em>NODE_ENV</em></strong> development as it's necessary for our application, but the most important part is not <strong><em>environment</em></strong>, but the <strong><em>depends_on</em></strong> property.</p>
<p>It's used to define the dependency relationship between services, hence which services are started when another one is, and in which order.</p>
<p>Finally, we can add the startup command:</p>
<pre><code class="lang-dockerfile">services:
  postgres:
    image: postgres:alpine
    restart: always
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    ports:
      - <span class="hljs-string">'5432:5432'</span>
  hello-world:
    build: .
    environment:
      - DATABASE_URL=postgres://postgres:postgres@postgres/express-todo
      - PORT=<span class="hljs-number">8000</span>
      - NODE_ENV=development
    ports:
      - <span class="hljs-string">'8000:8000'</span>
    command: &gt;
      sh -c <span class="hljs-string">"yarn prisma migrate deploy
      &amp;&amp; yarn build
      &amp;&amp; yarn start
    depends_on:
      - postgres</span>
</code></pre>
<p>With this syntax, we can define a command on multiple lines. Here, we are running migration with Prisma, building the app, and starting it.</p>
<h3 id="heading-starting-services">Starting services</h3>
<p>For Docker Compose, you can find the complete <a target="_blank" href="https://docs.docker.com/compose/reference/#command-options-overview-and-help">list of commands</a>. What we need right now is simply <strong><em>up</em></strong>:</p>
<pre><code class="lang-bash">docker-compose up
</code></pre>
<p>Once the application starts, you should be able to access <strong><em>http://localhost:8000/todo</em></strong>, which returns an empty list for now:</p>
<h2 id="heading-good-practices">Good practices</h2>
<p>This article was a good introduction, but I definitely recommend you have a look at an article on <a target="_blank" href="https://blog.scalablebackend.com/stop-using-insecure-and-inefficient-dockerfiles">performances</a>, and more generally <a target="_blank" href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/">good practices with Docker</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Stop Using Insecure and Inefficient Dockerfiles]]></title><description><![CDATA[Often, I see terrible Dockerfiles used at length. It has a negative impact on security, productivity, and overall cost.
Today, I want to show you how to improve from a basic to a very efficient Dockerfile, step by step.
Getting Started

It’s a very s...]]></description><link>https://blog.scalablebackend.com/stop-using-insecure-and-inefficient-dockerfiles</link><guid isPermaLink="true">https://blog.scalablebackend.com/stop-using-insecure-and-inefficient-dockerfiles</guid><category><![CDATA[Docker]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[backend]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Microservices]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Sun, 24 Sep 2023 14:39:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1692377174205/2e8928cb-2ef9-4070-8a93-aaca340f9b9a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Often, I see terrible Dockerfiles used at length. It has a negative impact on security, productivity, and overall cost.</p>
<p>Today, I want to show you how to improve from a basic to a very efficient Dockerfile, step by step.</p>
<h3 id="heading-getting-started">Getting Started</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692036754126/a64f12e5-84cf-4893-843d-a7ae9274cda4.png" alt class="image--center mx-auto" /></p>
<p>It’s a very short Dockerfile, which has the advantage of being simple while working perfectly fine. It does the job of:</p>
<ul>
<li><p>Using the latest version of the official NodeJS image as a base</p>
</li>
<li><p>Define a working directory (/app)</p>
</li>
<li><p>Copying source files</p>
</li>
<li><p>Installing dependencies</p>
</li>
<li><p>Exposing port (8000)</p>
</li>
<li><p>Running the app (yarn start)</p>
</li>
</ul>
<p>But is it efficient? Obviously not. If it was, there would be no point to the article you’re about to read.</p>
<h3 id="heading-parent-image">Parent Image</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692036773390/d8de4ae6-610f-488e-8f4e-68b0a71975aa.png" alt class="image--center mx-auto" /></p>
<p>Here, there are three important points.</p>
<h4 id="heading-using-a-fixed-version">Using a fixed version</h4>
<p>Maybe it seems like a good idea to use the latest version of an image as a base (to stay up-to-date), but it’s not. In practice, it’s often the source of issues.</p>
<p>If you use the last version of an image (latest), you don’t have control over when it gets updated. It’s possible (<a target="_blank" href="https://en.wikipedia.org/wiki/Murphy%27s_law">and will happen</a>) that a new version of an image is published while being incompatible with your app.</p>
<p>Between two builds, your application will suddenly go from working to failing, without any apparent reason.</p>
<p>That’s why we prefer using a fixed version instead of “latest”. But that also means you have the responsibility to update your base images from time to time.</p>
<h4 id="heading-long-term-support">Long-Term-Support</h4>
<p>Usually, a software is provided with specific LTS versions. That means creators provide better support for those versions, and should be preferred.</p>
<p>NodeJS provides a list of LTS versions on its <a target="_blank" href="https://nodejs.org/en">official website</a>.</p>
<h4 id="heading-alpine">Alpine</h4>
<p>Docker images are built on top of a given distribution, such as Ubuntu and Debian. Among those distributions is <a target="_blank" href="https://www.alpinelinux.org/">Alpine</a>.</p>
<p>It’s known for being very lightweight compared to others and is a huge help in keeping an efficient image.</p>
<h3 id="heading-lower-privileges">Lower Privileges</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692036797111/18f5e756-23be-4547-9b44-20b438766e9d.png" alt class="image--center mx-auto" /></p>
<p>The default user being ROOT, he has unlimited access. For security, it’s a better idea to provide a user with limited privileges.</p>
<p>Fortunately, with the node image comes a user called… node.</p>
<h3 id="heading-working-directory">Working Directory</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692036812278/fe4dfc72-6117-42ef-b359-e2a68e64d248.png" alt class="image--center mx-auto" /></p>
<p>The working directory is the one used by default. It’s a good idea to define a specific one for your app.</p>
<p>The most common practice is to use /usr/src/app.</p>
<h3 id="heading-caching">Caching</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692036826598/2da42a89-9a42-4a59-88f9-6c0d5a5a6b4e.png" alt class="image--center mx-auto" /></p>
<p>Docker works with a <a target="_blank" href="https://docs.docker.com/build/cache/#how-does-the-build-cache-work">caching system</a>, which is often ignored. You can think of each step in a Dockerfile as a layer, which is cached.</p>
<p>When one layer changes, all subsequent layers are invalidated. When the image is rebuilt, instead of retrieving a layer from the cache, the necessary command is simply restarted.</p>
<p>One layer changes frequently: the source code. So it’s best to copy the source code as late as possible.</p>
<p>The most common mistake is to copy the list of dependencies at the same time as the source code, then install the dependencies.</p>
<p>In this case, every time the source code changes and the image is rebuilt, the dependencies will be reinstalled. That’s why we prefer to copy the files defining the dependencies first, then install them, and finally copy the source code.</p>
<h3 id="heading-configure-the-working-directory">Configure the Working Directory</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692036841361/16f9aa72-14ca-4534-bfc6-d937b2fc8286.png" alt class="image--center mx-auto" /></p>
<p>Previously, we defined a new Working Directory and set up a user with limited privilege. That means we need to create the necessary directory first and give necessary access to the node user.</p>
<h3 id="heading-update-package-list">Update Package List</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692036860333/ca5b5f95-8e56-4ca1-82b7-afcc06fe0b81.png" alt class="image--center mx-auto" /></p>
<p>With Alpine, we aren’t using <a target="_blank" href="https://wiki.debian.org/Apt">APT</a>, but <a target="_blank" href="https://wiki.alpinelinux.org/wiki/Alpine_Package_Keeper">APK</a> to manage packages. No matter which tool you use to manage packages, you need to update its list from remote repositories.</p>
<p>That way, you ensure no out-of-date packages are installed. It’s mandatory to avoid packages with security and performance issues.</p>
<h3 id="heading-adding-packages">Adding Packages</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692362901910/92c10c7d-d2fb-45cd-947d-148b5b30be66.png" alt class="image--center mx-auto" /></p>
<p>I recommend using the — no-cache flag to avoid generating cache you won’t use, but will still make your image heavier.</p>
<h3 id="heading-multiple-run">Multiple RUN</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692348012937/4d530c81-66b5-45fc-a0de-06a3ce828add.png" alt class="image--center mx-auto" /></p>
<p>Inside <a target="_blank" href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run">Best practices for writing Dockerfiles</a>, we learn to:</p>
<blockquote>
<p>always combine <code>RUN apt-get update</code> with <code>apt-get install</code> in the same <code>RUN</code> statement.</p>
<p>Using <code>apt-get update</code> alone in a <code>RUN</code> statement causes caching issues and subsequent <code>apt-get install</code> instructions to fail.</p>
</blockquote>
<p>For more information, I highly recommend you give <a target="_blank" href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices">best practices</a> a read.</p>
<h3 id="heading-entrypoint">Entrypoint</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692036882624/186a9415-12ae-4bbf-857c-cfd4c7d58415.png" alt class="image--center mx-auto" /></p>
<p>I like to keep a basic Dockerfile that doesn’t start my application directly. Instead, I use <a target="_blank" href="https://github.com/krallin/tini">Tini</a>, which keeps the container alive and improve how processes are managed.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://docs.docker.com/build/building/multi-stage/">https://docs.docker.com/build/building/multi-stage/</a></div>
<p> </p>
<p>Then, you can either use multi-stage builds to augment your basic stage, or start and configure your application from outside the Dockerfile.</p>
<p>Locally you can use the Docker CLI. Also, when you deploy your app, any container management system allows you to configure startup scripts.</p>
<p>That way, you can have a single Dockerfile that is used in different configurations, instead of multiple configurations with barely any difference.</p>
<p>It becomes the source of truth about which environment your app run on.</p>
<h3 id="heading-final-result">Final Result</h3>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="f3a6bb08228bf30d705888b57e8dd1d4"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/morintd/f3a6bb08228bf30d705888b57e8dd1d4" class="embed-card">https://gist.github.com/morintd/f3a6bb08228bf30d705888b57e8dd1d4</a></div><p> </p>
<p>Now, you might be wondering: what’s the difference?</p>
<p>Apart from better security and faster re-build, one of my real-world project is 1GB lighter with the improved image:</p>
<p><img src="https://cdn-images-1.medium.com/max/1000/1*W0p0twwebC7srWoO7SmJVw.png" alt class="image--center mx-auto" /></p>
<hr />
<p>Do you want to learn more <strong>backend</strong> skills, you can effectively use in a <strong>professional</strong> environment?</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.scalablebackend.com/courses/bundle-scalable-backend-from-zero-to-hero">https://www.scalablebackend.com/courses/bundle-scalable-backend-from-zero-to-hero</a></div>
<p> </p>
<hr />
<p>Cover photo by <a target="_blank" href="https://unsplash.com/@tata_morais?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Thais Morais</a> on <a target="_blank" href="https://unsplash.com/photos/9c6j-akotJQ?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[How To Fail at Micro-Services]]></title><description><![CDATA[Doing micro-services the wrong way can be a death sentence for your project. If your architecture resonates with some elements of today’s list, it might be a good time to stop what you’re doing and reflect on it.
First, we need to define the basic pr...]]></description><link>https://blog.scalablebackend.com/how-to-fail-at-micro-services</link><guid isPermaLink="true">https://blog.scalablebackend.com/how-to-fail-at-micro-services</guid><category><![CDATA[Microservices]]></category><category><![CDATA[backend]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Devops]]></category><category><![CDATA[scalability]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Fri, 18 Aug 2023 17:18:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/-Cmz06-0btw/upload/82a885dfedbbd0cf82b8c9d40cb2d3bb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Doing micro-services the wrong way can be a death sentence for your project. If your architecture resonates with some elements of today’s list, it might be a good time to stop what you’re doing and reflect on it.</p>
<p>First, we need to define the basic properties of micro-services. Amazing resources like <a target="_blank" href="https://samnewman.io/books/building_microservices_2nd_edition/">Building Microservices</a> and <a target="_blank" href="https://www.oreilly.com/library/view/microservice-architecture/9781491956328/">Microservice Architecture</a> tell us they are supposed to be:</p>
<ul>
<li><p>Small in size</p>
</li>
<li><p>Messaging enabled</p>
</li>
<li><p>Bounded by contexts</p>
</li>
<li><p>Autonomously developed</p>
</li>
<li><p>Independently deployable</p>
</li>
<li><p>Decentralized</p>
</li>
<li><p>Built and released with automated processes</p>
</li>
<li><p>Loosely coupled</p>
</li>
<li><p>Focused on one thing</p>
</li>
</ul>
<p>If you built your back-end with micro-services in mind, but don’t respect some of those properties, it’s an obvious sign you made a mistake.</p>
<p>But sometimes, wrong architecture choices sneak up on you. You end up with something close to micro-services but don’t respect some of their rules.</p>
<h2 id="heading-distributed-monolith">Distributed monolith</h2>
<p>The most common is to build a distributed monolith instead of micro-services. This is a complete subject in itself, I highly recommend you read more about it.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://blog.scalablebackend.com/understand-the-difference-between-monolith-microservices-and-distributed-monolith">https://blog.scalablebackend.com/understand-the-difference-between-monolith-microservices-and-distributed-monolith</a></div>
<p> </p>
<h2 id="heading-shared-database">Shared database</h2>
<p>Let’s get this straight: it’s impossible to create independent and loosely coupled services with a shared database.</p>
<p>You will quickly realize how shared ownership of a database is a nightmare and has a negative impact on development and maintenance. It doesn’t integrate too well with ORMs, and I’m not even talking about deployment.</p>
<p>Yes, in a monolith you can have a single database, which makes things easy. With micro-services, you don’t have a choice but to make it more complex by separating databases for each service.</p>
<p>It’s one of the trade-offs with micro-services, it’s an added complexity that allows you to take advantage of micro-services.</p>
<h2 id="heading-lack-of-automation">Lack of automation</h2>
<p>Micro-services definitely require more effort to develop, deploy, and maintain. That’s why one of the cornerstones of your system should be automation.</p>
<p>I guess you could try to create a few micro-services without automation, but on a real project that’s simply unrealistic. Mainly, testing and deployment quickly become impossible without a completely automated CI/CD system.</p>
<h2 id="heading-shared-business-logic">Shared business logic</h2>
<p>Sharing business logic goes against the properties expected from micro-services, as it makes your services highly coupled.</p>
<p>And, in practice, you will also realize it has fewer benefits than expected. Using a shared package for business logic forces you to update the package itself, before updating your services.</p>
<p>It makes development longer and causes context-switching for developers, without bringing any real value.</p>
<h2 id="heading-starting-with-micro-services">Starting with micro-services</h2>
<p>In recent times, micro-services became trendy. While they solve several specific challenges, they’re not a silver bullet.</p>
<p>In practice, they should only be used when you already have a monolith, but have challenges micro-services solve.</p>
<p>Let’s say you started your back-end application, day one, as micro-services. There is a higher probability you did so because you wanted to use micro-services than to solve a real challenge.</p>
<h2 id="heading-lack-of-tests">Lack of tests</h2>
<p>Micro-services should be tested as much, if not more, compared to a monolith.</p>
<p>They should be tested in isolation, but that’s not all. If your services communicate (usually through events), it means they agree on a contract (implicitly or explicitly).</p>
<p>This contract should definitely be a part of the scope of your tests.</p>
<h2 id="heading-not-independently-deployable">Not independently deployable</h2>
<p>If, for some reason, you are unable to deploy a new version of a service in autonomy, it’s an obvious sign your services are highly coupled.</p>
<p>You shouldn’t need the other services at any point in the development and deployment process of a service. In <a target="_blank" href="https://www.oreilly.com/library/view/hands-on-microservices-with/9781788471459/">Hands-on Microservices with Kotlin</a>, we learn:</p>
<blockquote>
<p>Having the ability to deliver constantly is one of the advantages of the microservices architecture; any constraints should be removed, as much as we remove bugs from our applications.</p>
<p>We should take care of deployments from the beginning of the design of our microservices and architecture; finding a constraint on this area at late stages could have a big impact on the overall application.</p>
</blockquote>
<h2 id="heading-synchronous-communication-between-services">Synchronous communication between services</h2>
<p>Using synchronous (request-response) communication between services breaks the independent &amp; loosely coupled properties.</p>
<p>If you use synchronous communication between services, you are actually building a distributed monolith. After reading about distributed monolith, you should know it’s the exact opposite of what we want.</p>
<p>It doesn’t bring the added value of micro-services but also loses the simplicity that comes with a monolith.</p>
<p>Moreover, with micro-services, we need to guarantee messages get delivered. With request-response communication, when a process extends to more than one service, it’s simply impossible.</p>
<h2 id="heading-need-for-other-services-to-work">Need for other services to work</h2>
<p>Maybe the <strong>most important</strong>, but still the most often <strong>ignored</strong>.</p>
<p>One of the downsides of synchronous communication between services is the need for multiple services to work at the same time.</p>
<p>In theory, one service should be able to work perfectly fine even if all other services are down. Obviously, the system itself won’t work as expected, but services in isolation should.</p>
<p>This is generally made possible by:</p>
<ul>
<li><p>Limiting relationships between services</p>
</li>
<li><p>Using deduplication</p>
</li>
<li><p>Communicating through events.</p>
</li>
</ul>
<hr />
<p>If you are looking for an example projects with micro-services, you might be interested in a <a target="_blank" href="https://github.com/scalablebackend/tic-tac-toe-service?tab=readme-ov-file">tic-tac-toe</a> game and its <a target="_blank" href="https://github.com/scalablebackend/score-service">score</a>.</p>
<p>Otherwise, if you are looking for a more <strong>in-depth</strong>, and <strong>practical</strong> course on micro-services: Good news, I teach micro-services in a <a target="_blank" href="https://www.scalablebackend.com">complete guide</a>.</p>
]]></content:encoded></item><item><title><![CDATA[How To Handle Authentication with Micro-Services]]></title><description><![CDATA[With micro-services, and more generally distributed systems, come a lot of new challenges. One of them is how to handle authentication.
If you are unsure about the exact properties of micro-services and the challenges that come along, feel free to re...]]></description><link>https://blog.scalablebackend.com/how-to-handle-authentication-with-micro-services</link><guid isPermaLink="true">https://blog.scalablebackend.com/how-to-handle-authentication-with-micro-services</guid><category><![CDATA[Microservices]]></category><category><![CDATA[backend]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Devops]]></category><category><![CDATA[authentication]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Wed, 16 Aug 2023 16:58:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1691951545551/6f0ec794-d222-4bff-8b6d-9858f2f025c2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>With micro-services, and more generally distributed systems, come a lot of new challenges. One of them is how to handle <strong>authentication</strong>.</p>
<p>If you are unsure about the exact properties of micro-services and the challenges that come along, feel free to read about it more in-depth:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://blog.scalablebackend.com/understand-the-difference-between-monolith-microservices-and-distributed-monolith">https://blog.scalablebackend.com/understand-the-difference-between-monolith-microservices-and-distributed-monolith</a></div>
<p> </p>
<h2 id="heading-authentication-amp-authorization">Authentication &amp; Authorization</h2>
<p>Actually, <strong>authentication</strong> is not the only step we have to manage, but also <strong>authorization</strong>.</p>
<p>If you’re not sure about the difference, <a target="_blank" href="https://auth0.com/docs/get-started/identity-fundamentals/authentication-and-authorization#what-are-authentication-and-authorization-">Auth0</a> reminds us that:</p>
<blockquote>
<p>In simple terms, authentication is the process of verifying who a user is, while authorization is the process of verifying what they have access to.</p>
</blockquote>
<p>In practice, you can see <strong>authentication</strong> as the process where you send your email and password. You usually retrieve something that identifies you as the user, which you send along with other requests.</p>
<p>That way, on every other request, the server knows who you are and what resources you have access to. He can then decides if you can access certain endpoints, this is <strong>authorization</strong>.</p>
<p>Now, I would like to differentiate authentication systems into two groups with different challenges. They are <strong>stateful</strong> and <strong>stateless</strong> authentication systems.</p>
<h3 id="heading-stateful-authentication">Stateful authentication</h3>
<p>In a stateful authentication system, you need to retrieve the user’s information every time he’s making a request.</p>
<p>For example, after authentication, you can retrieve a unique id that ties you to a user in database. That way, the server can find who you are, without sending your e-mail and password every time.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691951776709/3cd5ebaa-afa9-4d60-88db-5638cd025833.png" alt="Stateful authentication" class="image--center mx-auto" /></p>
<p>But the server still needs to verify the user’s information that is tied to the unique id you’re sending. Only then can he decides if you can access a given resource.</p>
<p>That means, even if you’re sending a unique ID that identifies you, the server has to retrieve the user information before handling a request.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691951810171/a1f8acc9-ddd5-4f21-b725-a7986b17c19d.png" alt="Stateful authorization" class="image--center mx-auto" /></p>
<p>This authorization step becomes problematic when it comes to micro-services. If you’re doing micro-services well, you want to avoid high coupling, which implies two things (among others):</p>
<ul>
<li><p>Separate database</p>
</li>
<li><p>No synchronous communication (request/response) between services.</p>
</li>
</ul>
<p>That means, with the current state of things, we cannot authorize a user on a service outside of the one dedicated to authentication.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691951841554/4022f2ee-b0c4-4db6-b0d4-cd8153bd8db6.png" alt="Stateful authorization - cannot access" class="image--center mx-auto" /></p>
<p>Now, we have to think about a way to make authorization possible but keep micro-services independent and lowly coupled.</p>
<p>There is one main solution: managing authentication through an API gateway.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691951929791/e87a3b80-e698-47ac-a25b-869a544ebc5d.png" alt="Authentication through API gateway" class="image--center mx-auto" /></p>
<p>Here, we use an API gateway whose role is to proxy requests to the right service. But, before proxying requests, it checks if the user sent an ID he wants to authenticate with.</p>
<p>If he did, the API gateway retrieves the data related to the user and adds it to the request before it gets sent to the right service.</p>
<p><em>For example, it encodes the user information inside a string, that is passed with a header.</em></p>
<p>This way, any service can decode the user information, without relying on the user service.</p>
<blockquote>
<p>In my opinion, stateful authentication is simply not a great authentication system when it comes to micro-services.</p>
<p>It makes development and maintenance harder, has a bad impact on performance, and opens the door to security exploits.</p>
<p>While the previous solution is possible, I would prefer using a stateless authentication system.</p>
</blockquote>
<h3 id="heading-stateless-authentication">Stateless authentication</h3>
<p>In a stateless authentication system, the user’s information is stored by the client. That means, once you are authenticated, there is no need to retrieve the user’s information.</p>
<p>For example, a stateless authentication system can be set up using JSON Web Token.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://blog.scalablebackend.com/vulnerabilities-in-authentication-with-jwt">https://blog.scalablebackend.com/vulnerabilities-in-authentication-with-jwt</a></div>
<p> </p>
<p>Here, the process becomes much more straightforward. We generate a JWT by authenticating to the user service and sending it with requests to other services.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691952128480/6b2b84d4-7233-4d70-aedc-8c72298aedf8.png" alt="Stateless authentication &amp; authorization" class="image--center mx-auto" /></p>
<p>Other services don’t need to reach the user service as the user information is self-contained.</p>
<p>It makes everything simple by design and helps a lot in keeping our services completely independent.</p>
<blockquote>
<p>While this is my opinion, it’s definitely my favorite way to handle authentication with micro-services.</p>
</blockquote>
<hr />
<p>If you are looking for an example projects with micro-services, you might be interested in a <a target="_blank" href="https://github.com/scalablebackend/tic-tac-toe-service?tab=readme-ov-file">tic-tac-toe</a> game and its <a target="_blank" href="https://github.com/scalablebackend/score-service">score</a>.</p>
<p>Otherwise, if you are looking for a more <strong>in-depth</strong>, and <strong>practical</strong> course on micro-services: Good news, I teach micro-services in a <a target="_blank" href="https://www.scalablebackend.com">complete guide</a>.</p>
<hr />
<p>Cover photo by <a target="_blank" href="https://unsplash.com/@harlynkingm?utm_source=medium&amp;utm_medium=referral">Max Harlynking</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[Vulnerabilities in Authentication with JWT]]></title><description><![CDATA[After working with JWT more in-depth for the past few months, I realized most of the learning materials are of poor quality.
Today, I want to make it clear how JWT should be used in your authentication flow, what are its security vulnerabilities, and...]]></description><link>https://blog.scalablebackend.com/vulnerabilities-in-authentication-with-jwt</link><guid isPermaLink="true">https://blog.scalablebackend.com/vulnerabilities-in-authentication-with-jwt</guid><category><![CDATA[JWT]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Security]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Mon, 14 Aug 2023 16:49:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1691952475843/323cb805-433f-42c1-a478-32b60806ee26.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>After working with JWT more in-depth for the past few months, I realized most of the learning materials are of poor quality.</p>
<p>Today, I want to make it clear how JWT should be used in your authentication flow, what are its security vulnerabilities, and how to avoid them.</p>
<h2 id="heading-what-is-a-jwt">What is a JWT</h2>
<p>From its <a target="_blank" href="https://jwt.io/introduction">introduction page</a>, we learn the following:</p>
<blockquote>
<p>JSON Web Token (JWT) is an open standard (<a target="_blank" href="https://tools.ietf.org/html/rfc7519">RFC 7519</a>) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object</p>
</blockquote>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jwt.io/">https://jwt.io/</a></div>
<p> </p>
<h3 id="heading-content">Content</h3>
<p>In practice, a JWT is a string that looks like <code>xxxxx.yyyyy.zzzzz</code> , where the sections are respectively the header (<code>xxxxx</code> ), payload (<code>yyyyy</code> ), and signature (<code>zzzzz</code> ).</p>
<p><strong>Header</strong></p>
<p>The header is a JSON object, which typically defines the algorithm used, and the type of the token JWT. It’s encoded in base64 to be used as a string.</p>
<p><strong>Payload</strong></p>
<p>The payload is also a JSON object, where you define the user information. it’s also encoded in base64.</p>
<p><strong>Signature</strong></p>
<p>The signature is generated based on the header and payload, using an algorithm (such as HMAC SHA256) and your secret.</p>
<h3 id="heading-result">Result</h3>
<p>In the end, you generate a JWT, which anyone can read the information of. But you are the only one able to verify a JWT has not been tampered with. Nobody can change a JWT payload and sign it with your own secret.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691953360897/90f585ac-1987-41f0-8400-6e8c0ff39c90.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-processes">Processes</h2>
<h3 id="heading-authentication">Authentication</h3>
<p>In the authentication process, a user typically sends his credentials to an API, which tries to find the corresponding account in a database.</p>
<p>If an account is found, a JWT is generated with the user information, such as id, name, or even his roles (admin? user?).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691953472010/508bca2e-98a5-4cda-98af-92c80163cbed.webp" alt class="image--center mx-auto" /></p>
<h3 id="heading-authorization">Authorization</h3>
<p>Then, a user is able to request a private endpoint, where he needs to be authenticated. This is known as the authorization process:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691953546396/526c1230-3a98-48f6-9c33-aedbfc684104.webp" alt class="image--center mx-auto" /></p>
<p>Because a JWT contains user information (it’s stateless/self-contained), the API doesn’t need to request a database. This is amazing in terms of performance, and even better on distributed architecture.</p>
<p>This is the normal use case of a JWT, if you’re making requests to your database during authorization, you defeat the purpose of JWT.</p>
<h3 id="heading-limitations">Limitations</h3>
<p>On one side, the self-contained aspect of JWT makes it amazing. On the other, because you’re not requesting to your database, you cannot invalidate a JWT.</p>
<p>This is an issue for both functionality &amp; security. If a user gets his token stolen, or if you have a role system and someone gets a role removed, he can still use a previous token when he shouldn’t be allowed to (while the token is not expired).</p>
<p>This problem is solved by limiting the lifetime of tokens to a short duration, such as 5 minutes. But you don’t want to ask a user credentials every five minutes.</p>
<p>That’s why you need to implement <strong>refresh tokens.</strong> It’s often treated as beyond the scope of basic learning materials, but it’s mandatory.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/">https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/</a></div>
<p> </p>
<h3 id="heading-refresh-tokens">Refresh tokens</h3>
<p>Refresh tokens are completely different from the regular JWT you use for authorization (which are called identity tokens). They are long-lived tokens (~7 days) and can be used a single time to generate a new identity token.</p>
<p>If you respect those properties, you can implement identity tokens in different ways. You can have a table that stores refresh tokens and the corresponding user, which is updated every time it’s used.</p>
<p>For refresh tokens, I usually generate a JWT where the payload contains two properties, a <code>sub</code>, and <code>userId</code>. The sub contains a UUID, which is stored in a database, and map to its corresponding user.</p>
<p>When a user tries to log in based on a refresh token, I find the corresponding <code>sub</code> in my database and verify the <code>userId</code> it maps to is the right one (it avoids a potential situation where a user connects with a previous user refresh token UUID).</p>
<p>In the end, you should have two endpoints for login, one with user credentials, and one with a refresh token. Now, there are multiple ways to generate and store those tokens, which leads us to the next section: vulnerabilities.</p>
<h2 id="heading-security-vulnerabilities">Security vulnerabilities</h2>
<h3 id="heading-xss-attack">XSS attack</h3>
<p>There is one implementation issue I’ve seen too many times, how to store a JWT. In most online examples, you can see a JWT being returned inside a request response (<em>body</em>), and stored in localStorage or simply in memory.</p>
<p>This is the source of a huge security vulnerability, <strong>XSS attack</strong>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://owasp.org/www-community/attacks/xss/">https://owasp.org/www-community/attacks/xss/</a></div>
<p> </p>
<p>An XSS attack is possible when a malicious third party manage to inject code inside your application.</p>
<p>From here, anything allowed by your runtime is possible. A third party could secretly read the content of <code>localStorage</code> and send it to their own server, stealing JWTs for example.</p>
<p>Storing your JWT in-memory isn’t enough. A third party can easily intercept a request response, and read users JWTs from there. There is a single solution: storing them inside secured <code>cookies</code>.</p>
<p>A secure cookie is configured with, at least, the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies">Secure and HttpOnly</a> attributes. It’s also a good practice to use <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#samesite_attribute">SameSite</a> to avoid CSRF.</p>
<p>There is also some arguments in favor of storing refresh tokens inside localStorage instead of cookies. The impact of a refresh token being stolen <a target="_blank" href="https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/#You-Can-Store-Refresh-Token-In-Local-Storage">is reduced</a> by its one-time only validity.</p>
<h3 id="heading-csrf">CSRF</h3>
<p>Cookies are much better than <code>localStorage</code> for our use case, but they’re not perfect. With cookies, you don’t control when they are sent, your browser sends them with every request.</p>
<p>It’s the source of CSRF, short for Cross Site Request Forgery.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://owasp.org/www-community/attacks/csrf">https://owasp.org/www-community/attacks/csrf</a></div>
<p> </p>
<p>From a general perspective, CSRF can happen when a third party trick a user into making a malicious request. The CSRF page from OWASP gives an <a target="_blank" href="https://owasp.org/www-community/attacks/csrf#get-scenario">amazing scenario</a> with a bank transfer endpoint.</p>
<p>Using cookies with <code>SameSite</code> mitigates CSRF, but only a <a target="_blank" href="https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#token-based-mitigation">CSRF token</a> can completely get rid of it.</p>
<h3 id="heading-signing">Signing</h3>
<p>There are two potential vulnerabilities when you sign a JWT token, bad algorithm and secret key.</p>
<p>JWT can be signed with different algorithms. The list can differ depending on <a target="_blank" href="https://jwt.io/libraries">which library</a> you are using. Library authors are responsible to implement those.</p>
<p>The default algorithm is usually <em>HS256</em>, but using a bad implementation or wrong configuration might end up with you using the <strong>none</strong> algorithm.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/#Meet-the--None--Algorithm">https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/#Meet-the--None--Algorithm</a></div>
<p> </p>
<p>What this algorithm does is… nothing! It generates an empty signature, which allows any third party to modify the JWT payload, and your server will still believe the modified JWT is valid.</p>
<p>My advice is to use well-known/maintained libraries and not try to use the <strong>none</strong> algorithm. You can also verify the content of tokens you generate using <a target="_blank" href="http://jwt.io">jwt.io</a>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://auth0.com/blog/brute-forcing-hs256-is-possible-the-importance-of-using-strong-keys-to-sign-jwts/">https://auth0.com/blog/brute-forcing-hs256-is-possible-the-importance-of-using-strong-keys-to-sign-jwts/</a></div>
<p> </p>
<p>Previously, we talked about refresh tokens, but there is a vulnerability introduced by using different types of tokens.</p>
<p>If you configure your refresh tokens to be signed with the same secret as the identity tokens, a malicious user could send an identity token where you expect a refresh token and vice-versa.</p>
<p>Depending on your implementation, you might grant access to a malicious user where you shouldn’t. There is a single solution: use a different secret for your identity &amp; refresh tokens.</p>
<h3 id="heading-validation">Validation</h3>
<p>During the validation phase, bad implementation could introduce security vulnerabilities.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.npmjs.com/package/jsonwebtoken">https://www.npmjs.com/package/jsonwebtoken</a></div>
<p> </p>
<p>There is an example with the NodeJS <a target="_blank" href="https://www.npmjs.com/package/jsonwebtoken">jsonwebtoken</a> library. The right implementation is to use the <a target="_blank" href="https://www.npmjs.com/package/jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback">verify method</a> to ensure the token is valid and decode it.</p>
<p>On the other hand, it provides a <a target="_blank" href="https://www.npmjs.com/package/jsonwebtoken#jwtdecodetoken--options">decod method</a> that doesn’t check if the token is valid. Some developers not used to working with JWT might use the second method instead, virtually accepting any JWT token.</p>
<p>Ensure you are using well-known libraries and learn them properly before implementing anything sensitive.</p>
<h2 id="heading-authors-note">Author’s note</h2>
<p>I advocate for the use of cookies over localStorage to mitigate XSS attacks, but that’s not a reason to ignore potential XSS attacks altogether. I definitely recommend following good practices regarding XSS attacks.</p>
<p>For example, using JS eval <a target="_blank" href="https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#dangerous-contexts">should be avoided</a>. You can also verify your dependencies using tools like <a target="_blank" href="http://snyk.io">snyk.io</a>, and only use trusted CDN.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://auth0.com/">https://auth0.com/</a></div>
<p> </p>
<p>Building your own authentication system is an arduous task, I would recommend anyone to either use an authentication provider such as <a target="_blank" href="https://auth0.com/">Auth0</a>, or have a dedicated team working full time on authentication.</p>
<hr />
<p>Do you want to learn how to create a <strong>backend</strong> application, add a <strong>secure authentication system</strong>, and much more?</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.scalablebackend.com/">https://www.scalablebackend.com/</a></div>
<p> </p>
<hr />
<p>Cover photo by <a target="_blank" href="https://unsplash.com/fr/@edwinhooper?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Edwin Hooper</a> on <a target="_blank" href="https://unsplash.com/fr/photos/TJ9rBJAAguQ?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[Understand the Difference Between Monolith, MicroServices, and Distributed Monolith]]></title><description><![CDATA[Often, inexperienced teams want to create micro-services but end up with a distributed monolith. What is it and why should you avoid it?
First, we need to understand the difference between a simple monolith and micro-services.
Monolith
Impressive onl...]]></description><link>https://blog.scalablebackend.com/understand-the-difference-between-monolith-microservices-and-distributed-monolith</link><guid isPermaLink="true">https://blog.scalablebackend.com/understand-the-difference-between-monolith-microservices-and-distributed-monolith</guid><category><![CDATA[Microservices]]></category><category><![CDATA[backend]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[scalability]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Sun, 13 Aug 2023 17:58:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1691948528040/c006a7ba-ce2d-46ec-8232-3384ef3639e1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Often, inexperienced teams want to create micro-services but end up with a distributed monolith. What is it and why should you avoid it?</p>
<p>First, we need to understand the difference between a simple monolith and micro-services.</p>
<h2 id="heading-monolith">Monolith</h2>
<p>Impressive only by name, monolithic is the most common architectural style. It’s when everything is contained in a single piece, or in other words, self-contained.</p>
<h3 id="heading-architecture">Architecture</h3>
<p>In practice, a monolithic backend looks like a single and large codebase that provides all the APIs you need</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691948607682/7b6c9387-433c-4f54-a6fe-fe7e37c80c57.png" alt /></p>
<p>Now, the questions are, why is monolithic the traditional architecture, and why would you want to create micro-services instead?</p>
<p>A monolithic architecture comes with much more simplicity at the beginning of a project and is sufficient for most projects.</p>
<p>When your whole codebase is in the same place, development, testing, monitoring, and deployment are much more straightforward, faster, and cheaper.</p>
<h3 id="heading-challenges">Challenges</h3>
<p>But when your application grows, with more functionality and users, new challenges appear. They could be gathered under two main categories, codebase maintainability and scalability.</p>
<p>In a monolith, it's a common practice to organize code by creating abstractions, such as modules.</p>
<p>But those abstractions and their boundaries usually break down with time, and similar code starts to become spread all over.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691948648880/b4f51056-d4c2-4acf-97bf-eb7eada55cfd.png" alt class="image--center mx-auto" /></p>
<p>With a large codebase, it becomes difficult to know where a change needs to be made, making it harder to fix bugs and implement new features.</p>
<h3 id="heading-scaling">Scaling</h3>
<p>Then, come the issues with scalability. When a system needs more resources, there are two ways to scale it: horizontally, and vertically.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691948718853/914d8d4e-1fa5-40c4-9105-9c0f60c9c948.png" alt class="image--center mx-auto" /></p>
<p>Vertical scaling refers to adding more resources to an existing machine, such as GPU or RAM. Horizontal scaling refers to spreading out your application on multiple machines and adding more machines to your pool of resources.</p>
<p>Vertical scaling can be enough but will be limited at some point, as there is a limit to how much resources a single machine can have.</p>
<p>Horizontal scaling is in theory infinite. On the other hand, it requires your application to be able to work when spread out on multiple machines.</p>
<p>Even a monolithic application could work if it's spread out on multiple machines, as long as it's stateless. But monoliths have a weakness that micro-services don't have when it comes to horizontal scaling.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691948758383/f1d48588-7e0d-48a5-9c85-8d2bbf2fc590.png" alt class="image--center mx-auto" /></p>
<p>As a monolith contains the whole system in a single codebase, everything must be scaled together. If you only need to scale a subset of endpoints, you have no choice but to deploy the whole system.</p>
<h2 id="heading-microservices">Microservices</h2>
<p>Unfortunately, microservices are described in a lot of different ways.</p>
<h3 id="heading-architecture-1">Architecture</h3>
<p>A succinct definition I particularly like comes from "<a target="_blank" href="https://samnewman.io/books/building_microservices_2nd_edition/">Building Microservices</a>" by Sam Newman:</p>
<blockquote>
<p>Microservices are small, autonomous services that work together.</p>
</blockquote>
<p>That's enough for a first approach but lacks details to understand microservices at a deeper level. From "<a target="_blank" href="https://www.oreilly.com/library/view/microservice-architecture/9781491956328/">Microservice Architecture</a>", we learn microservices should share the following traits:</p>
<ul>
<li><p>Small in size</p>
</li>
<li><p>Messaging enabled</p>
</li>
<li><p>Bounded by contexts</p>
</li>
<li><p>Autonomously developed</p>
</li>
<li><p>Independently deployable</p>
</li>
<li><p>Decentralized</p>
</li>
<li><p>Built and released with automated processes</p>
</li>
</ul>
<p>Now, what does that mean, in practice? In the real world, your app is split into multiple services with its own set of related functionalities (while ensuring low coupling between them).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691948976281/3d4d7701-8a94-4424-9ae2-e56d7eae8e3b.png" alt class="image--center mx-auto" /></p>
<p>There are, at the same time, multiple instances of the same service depending on the load to handle. They communicate asynchronously to ensure messages get delivered and avoid coupling.</p>
<h3 id="heading-challenges-1">Challenges</h3>
<p>Opposite to monoliths, why is development, testing, monitoring, and deployment slower and more expensive with microservices?</p>
<p>New challenges appear with multiple applications. For example, managing data in separate but related databases comes with more challenges than a single, unified database.</p>
<p>Then, you need some sort of communication between services, usually through an event bus. Making your micro-services work together, testing them correctly, as well as deployment will be a serious headache.</p>
<p>In the end, you should expect development to take more time with micro-services. The increased complexity also means you need a bigger team to manage it, setting up micro-services with a single and small team is a bad idea.</p>
<h3 id="heading-scaling-1">Scaling</h3>
<p>There is one primary benefit with micro-services. They can be easily deployed as you need them, and scale more efficiently.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691949013592/49ad1521-9328-49e0-9b97-a76a7ee0fb2a.png" alt class="image--center mx-auto" /></p>
<p>With micro-services, you can just scale the service that needs scaling, making it possible to run it on smaller, less powerful hardware. It makes it faster and more cost-effective.</p>
<p>Micro-services provide other benefits compared to monoliths, such as resilience, ease of deployment, and replaceability. As a monolith contains your whole app, if one of its components fails, everything else becomes unavailable.</p>
<p>Also, you might have experienced how hard it can be to refactor a huge codebase, whereas the size of a micro-service is usually limited. The cost to replace a service is then much more manageable.</p>
<p>On large projects, with numerous teams, micro-services might even have a positive impact on productivity</p>
<h2 id="heading-distributed-monolith">Distributed Monolith</h2>
<p>A distributed monolith is the result of splitting a monolith into multiple services, that are heavily dependent on each other, without adopting the patterns needed for distributed systems.</p>
<p>In practice, it's the result of splitting a monolith into separate services, but keeping them tightly coupled.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1691949043740/ed1cb630-cf38-425f-b031-043f92099ff0.png" alt class="image--center mx-auto" /></p>
<p>That means, they still rely heavily on each other. In this context, you lose the simplicity that comes with a monolithic architecture, but don't enjoy the benefits of independent microservices.</p>
<blockquote>
<p>Don't do it!</p>
</blockquote>
<p>Being lowly coupled doesn't mean you have absolutely no relationship either. For example, you might send and listen to events.</p>
<p>Also, you should write some integration tests, or to be more precise contract tests (if you're doing microservices well).</p>
<p>That means, at some point, you need information on the contracts from other services. There is some relationship going on, but it doesn't make your services tightly coupled.</p>
<hr />
<p>If you are looking for an example projects with micro-services, you might be interested in a <a target="_blank" href="https://github.com/scalablebackend/tic-tac-toe-service?tab=readme-ov-file">tic-tac-toe</a> game and its <a target="_blank" href="https://github.com/scalablebackend/score-service">score</a>.</p>
<p>Otherwise, if you are looking for a more <strong>in-depth</strong>, and <strong>practical</strong> course on micro-services: Good news, I teach micro-services in a <a target="_blank" href="https://www.scalablebackend.com">complete guide</a>:</p>
<hr />
<p>Cover photo by <a target="_blank" href="https://unsplash.com/@crawford?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Crawford Jolly</a> on <a target="_blank" href="https://unsplash.com/photos/DV1WYAu8fHQ?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[How To Write Efficient Unit Tests with Prisma ORM]]></title><description><![CDATA[During my life as a full-stack developer, I tried out lots of ORM, ODM, and query builders. Some were fantastic, while others still give me nightmares (we see you Sequelize).
Current state
Among the fantastic tools, there is Prisma. It works perfectl...]]></description><link>https://blog.scalablebackend.com/how-to-write-efficient-unit-tests-with-prisma-orm-e9d8fdf43f5f</link><guid isPermaLink="true">https://blog.scalablebackend.com/how-to-write-efficient-unit-tests-with-prisma-orm-e9d8fdf43f5f</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Testing]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[nestjs]]></category><category><![CDATA[Express]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Fri, 09 Dec 2022 09:55:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1692035710779/414408ff-98e2-4967-b5db-3740ee9ef8db.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>During my life as a full-stack developer, I tried out lots of ORM, ODM, and query builders. Some were fantastic, while others still give me nightmares (we see you <a target="_blank" href="https://sequelize.org/"><em>Sequelize</em></a>).</p>
<h3 id="heading-current-state">Current state</h3>
<p>Among the fantastic tools, there is <a target="_blank" href="https://www.prisma.io/">Prisma</a>. It works perfectly, has a great DX, documentation, and much more. I encountered a single issue when working with it, unit testing.</p>
<p>While they have a documentation page on unit testing, with a great introduction, their method of mocking is unsatisfactory at best.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.prisma.io/docs/guides/testing/unit-testing">https://www.prisma.io/docs/guides/testing/unit-testing</a></div>
<p> </p>
<p>At best, that allows you to mock, one by one, the responses for the requests you believe will be called. I think this is inefficient for a few reasons:</p>
<ul>
<li><p>It requires too much setup, on every single test.</p>
</li>
<li><p>There is a very low level of trust in the mocks you define, as you are not sure Prisma will return the same data.</p>
</li>
<li><p>By defining these mocks yourself, you get further away from testing your app behavior, and closer to testing implementation details.</p>
</li>
<li><p>With new versions of Prisma, you are at risk of seeing your tests being obsolete.</p>
</li>
</ul>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://kentcdodds.com/blog/testing-implementation-details">https://kentcdodds.com/blog/testing-implementation-details</a></div>
<p> </p>
<p>Let’s look at what application we want to test, which tests to write, and then define how to write unit tests.</p>
<h3 id="heading-application-structure">Application structure</h3>
<p>My backend applications are usually split into a few layers. If we take an example with a basic ExpressJS app, it has at least 3 layers: module, controller, and service.</p>
<p>Each set of functionality is separated into a dedicated <strong>module</strong>, which handles routing, validation, and passes the request to the controller.</p>
<p>The <strong>controller</strong> is where the business logic is found, and often makes use of services.</p>
<p>A <strong>service</strong> provides high-level functions that make requests to the database. This is where I use ORM such as <strong>Prisma</strong>.</p>
<h3 id="heading-what-should-be-tested">What should be tested</h3>
<p>I usually have two primary <a target="_blank" href="https://www.atlassian.com/continuous-delivery/software-testing/types-of-software-testing">types of tests</a> when working on a backend application (and many more depending on the context!). They are <strong>unit</strong> and <strong>end-to-end</strong> tests.</p>
<p>I try to avoid writing <strong>e2e</strong> tests when possible, as they have limitations like being slow, expensive, and giving late feedback. I usually write <strong>unit</strong> tests for low-level functions such as middlewares.</p>
<p>On the other hand, I don’t test my services or controllers in complete isolation. In those cases, I feel like it’s not giving me enough value for the time it takes to write tests. Instead, I want to test my endpoint behavior as a whole.</p>
<p>To do it, I write my tests using a <strong>test version</strong> of my backend app. Outside dependencies, like Prisma, are mocked (in an efficient way) which allows me to simulate queries in isolation using tools like <a target="_blank" href="https://www.npmjs.com/package/supertest">SuperTest</a>.</p>
<p>In this context, I write unit tests for most use-cases. It includes validation (such as parameters), authorization (ensure you’re connected with the right access), but I also verify I receive the right response, using the mocked version of Prisma. Depending on my app, there is even more use-case.</p>
<p>I also end up writing e2e tests, to ensure my successful response and database-dependent errors, are the expected ones, in a real environment.</p>
<p>Those tests may partially overlap with my unit tests, but this time using a real database. It gives me the quick and early feedback of unit tests while having the high confidence of e2e tests.</p>
<h3 id="heading-how-to-write-those-tests">How to write those tests</h3>
<p>In the following examples, I expect you to understand the basics of testing. That includes Jest, which is used as part of the examples.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://blog.scalablebackend.com/testing-with-node-js-understand-and-choose-the-right-tools-98c0a33fb59a">https://blog.scalablebackend.com/testing-with-node-js-understand-and-choose-the-right-tools-98c0a33fb59a</a></div>
<p> </p>
<h4 id="heading-middleware">Middleware</h4>
<p>Middlewares don’t need a special environment to be tested in, they can be considered just like any other function.</p>
<p>Only thing is, they have a defined format. For Express, they must return a function that takes a <strong>request</strong>, <strong>response</strong>, and <strong>next function</strong>. Let’s have a look at the following snippet:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Request, Response, NextFunction } <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;

<span class="hljs-keyword">type</span> Validator = <span class="hljs-function">(<span class="hljs-params">req: Request</span>) =&gt;</span> <span class="hljs-built_in">boolean</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">validator</span>(<span class="hljs-params">validate: Validator</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-function">(<span class="hljs-params">req: Request, _res: Response, next: NextFunction</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> valid = validate(req);
    <span class="hljs-keyword">if</span> (!valid) next(<span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>());
    <span class="hljs-keyword">else</span> next();
  };
}
</code></pre>
<p>Validator takes a function that determines if a request is considered valid, by returning a boolean based on the received request. It returns a middleware, which will throw an error if the received request is determined as invalid.</p>
<p>We don't want to test implementation details for this middleware, but a real scenario. With Jest, it can be tested easily by creating a temporary server (app) object using this middleware.</p>
<p>Then, we can send requests in different use-case:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> supertest <span class="hljs-keyword">from</span> <span class="hljs-string">'supertest'</span>;
<span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;

<span class="hljs-keyword">import</span> { validator } <span class="hljs-keyword">from</span> <span class="hljs-string">'../validator.middleware'</span>;

describe(<span class="hljs-string">'validator'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> app = express();

  beforeAll(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> validate = validator(<span class="hljs-function">(<span class="hljs-params">req</span>) =&gt;</span> req.query.mock === <span class="hljs-string">'false'</span>);

    app.get(<span class="hljs-string">'/'</span>, validate, <span class="hljs-function">(<span class="hljs-params">_req, res</span>) =&gt;</span> {
      res.status(<span class="hljs-number">201</span>).send();
    });
  });

  it(<span class="hljs-string">'Should return an error if request is invalid'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> supertest(app).get(<span class="hljs-string">'/?mock=true'</span>).send().expect(<span class="hljs-number">500</span>);
  });

  it(<span class="hljs-string">'Should return a success otherwise'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> supertest(app).get(<span class="hljs-string">'/?mock=false'</span>).send().expect(<span class="hljs-number">201</span>);
  });
});
</code></pre>
<h4 id="heading-endpoints-unit">Endpoints (Unit)</h4>
<p>I already explained I use <a target="_blank" href="https://www.npmjs.com/package/supertest">SuperTest</a> for my endpoints. I also talked about using a <strong>test version</strong> of my backend app. To be more precise, I have a few helper functions, dedicated to bootstraping my backend during tests and managing mocked data.</p>
<p>The following snippet is a good example of a unit test. We demonstrate how we can test an endpoint dedicated to creating an article.</p>
<p>We use helpers to bootstrap our app, generate the tokens needed for our use case, and send the request with SuperTest.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Express } <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-keyword">import</span> supertest <span class="hljs-keyword">from</span> <span class="hljs-string">'supertest'</span>;

<span class="hljs-keyword">import</span> { ArticleFixture, ServerMock, UserMock } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../../../testing'</span>;

describe(<span class="hljs-string">'POST /article'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">let</span> app: Express;
  <span class="hljs-keyword">const</span> tokens = UserMock.generateTokens();

  beforeAll(<span class="hljs-keyword">async</span> () =&gt; {
    app = <span class="hljs-keyword">await</span> ServerMock.createApp();
  });

  test(<span class="hljs-string">"Should return error if user isn't authenticated"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> supertest(app).post(<span class="hljs-string">'/article'</span>).send(ArticleFixture.articles.newArticle).expect(<span class="hljs-number">401</span>);
  });

  test(<span class="hljs-string">"Should return error if user doesn't have ADMIN role"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> supertest(app)
      .post(<span class="hljs-string">'/article'</span>)
      .set(<span class="hljs-string">'Authorization'</span>, <span class="hljs-string">`Bearer <span class="hljs-subst">${tokens.user}</span>`</span>)
      .send(ArticleFixture.articles.newArticle)
      .expect(<span class="hljs-number">401</span>);
  });

  it(<span class="hljs-string">'Should create article'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> supertest(app)
      .post(<span class="hljs-string">'/article'</span>)
      .set(<span class="hljs-string">'Authorization'</span>, <span class="hljs-string">`Bearer <span class="hljs-subst">${tokens.admin}</span>`</span>)
      .send(ArticleFixture.articles.newArticle)
      .expect(<span class="hljs-number">201</span>);
  });
});
</code></pre>
<h4 id="heading-mocking-with-prisma">Mocking with Prisma</h4>
<p>But we still didn’t solve the issue that comes with Prisma. In the above snippet, it looks like nothing is mocked.</p>
<p>There is a single solution if we want to write tests with no dependence to a database or <a target="_blank" href="https://www.prisma.io/docs/guides/testing/unit-testing">heavy mocking</a>: using an in-memory implementation of Prisma.</p>
<p>Introducing <a target="_blank" href="https://www.npmjs.com/package/prismock">prismock</a>. <em>Disclaimer: I am indeed its creator</em>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.npmjs.com/package/prismock">https://www.npmjs.com/package/prismock</a></div>
<p> </p>
<p>As there was no satisfying solution to efficiently write unit tests with Prisma, I decided to write my own solution.</p>
<p>It actually reads your <code>schema.prisma</code> and generates models based on it. It perfectly simulates Prisma’s API and store everything in-memory for fast, isolated, and retry-able unit tests.</p>
<p>Remember how I use a helper to build a test version of my backend app?</p>
<p>In production I build my app, using a genuine <strong>PrismaClient</strong>, which is then bootstrapped. During my test, I replace <strong>PrismaClient</strong> using <a target="_blank" href="https://www.npmjs.com/package/prismock#dependency-injection">dependency injection</a>.</p>
<p>In the above snippet, it’s done as part of <code>ServerMock.createApp()</code>, which makes it virtually invisible when I write my tests.</p>
<h4 id="heading-endpoints-e2e">Endpoints (E2E)</h4>
<p>In a context where our article endpoint and authorization process are already tested, we could argue that it’s not mandatory to test authentication on every single endpoint during e2e tests.</p>
<p>For example, we could end up with the following test:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> supertest <span class="hljs-keyword">from</span> <span class="hljs-string">'supertest'</span>;

<span class="hljs-keyword">import</span> { ArticleFixture } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../fixtures'</span>;
<span class="hljs-keyword">import</span> { ArticleMock } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../mocks'</span>;
<span class="hljs-keyword">import</span> E2EUtils <span class="hljs-keyword">from</span> <span class="hljs-string">'../EndToEndUtils'</span>;

describe(<span class="hljs-string">'POST /article'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">let</span> tokens: { admin: <span class="hljs-built_in">string</span> };

  beforeAll(<span class="hljs-keyword">async</span> () =&gt; {
    tokens = <span class="hljs-keyword">await</span> E2EUtils.generateTokens();
  });

  it(<span class="hljs-string">'Should return created article'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">return</span> supertest(<span class="hljs-string">'http://localhost'</span>)
      .post(<span class="hljs-string">'/article'</span>)
      .set(<span class="hljs-string">'Authorization'</span>, <span class="hljs-string">`Bearer <span class="hljs-subst">${tokens.admin}</span>`</span>)
      .send(ArticleFixture.articles.newArticle)
      .expect(<span class="hljs-number">201</span>)
      .then(<span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> {
        expect(response.body).toEqual({
          title: ArticleMock.articles.newArticle.title,
          content: ArticleMock.articles.newArticle.content,
          slug: ArticleMock.articles.newArticle.slug,
        });
      });
  });
});
</code></pre>
<p>This test must be written in a different environment, where we have access to a seeded database, and our endpoint has been built and runs in a near-production environment.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>In this context, we should cover the entirety of our codebase with unit tests, giving us fast and early feedback, with a strong trust in our test results.</p>
<p>Using an in-memory implementation of Prisma instead of <a target="_blank" href="https://www.prisma.io/docs/guides/testing/unit-testing">manual mocks</a> increases our confidence even more. Together with our testing strategy (defined in <strong>what should be tested</strong>), we also end up with amazing productivity.</p>
<p>Finally, we write E2E tests exclusively for use-cases that make requests to our database. It does overlap with some unit tests, is slower, and more expensive with late feedback, but it eliminates the remaining grey areas.</p>
<p>We are then able to answer:</p>
<blockquote>
<p>Are you confident in shipping your app to production?</p>
</blockquote>
<hr />
<p><strong>Looking for a complete course on creating backend apps with Node.JS?</strong></p>
<p>Learn everything you need on <a target="_blank" href="https://www.scalablebackend.com">scalablebackend</a>.</p>
<hr />
<p>Photo by <a target="_blank" href="https://unsplash.com/fr/@roman_lazygeek">Roman Mager</a> on <a target="_blank" href="https://unsplash.com/fr/s/photos/test">Unsplash</a></p>
]]></content:encoded></item><item><title><![CDATA[Testing with Node.js: Understand and Choose the Right Tools]]></title><description><![CDATA[If you’re here today, there is a 99% chance you want to get started with testing in a higher-level environment, but lack the low-level knowledge. You might be working on Frontend or Backend with Node.js, but don’t know where to start. Congratulations...]]></description><link>https://blog.scalablebackend.com/testing-with-node-js-understand-and-choose-the-right-tools-98c0a33fb59a</link><guid isPermaLink="true">https://blog.scalablebackend.com/testing-with-node-js-understand-and-choose-the-right-tools-98c0a33fb59a</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Testing]]></category><category><![CDATA[software development]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Teddy Morin]]></dc:creator><pubDate>Mon, 27 Jun 2022 05:17:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1692035737286/57638ca0-2cb3-47a2-b60e-96a22955e450.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you’re here today, there is a 99% chance you want to get started with testing in a higher-level environment, but lack the low-level knowledge. You might be working on Frontend or Backend with Node.js, but don’t know where to start. Congratulations, you’re in the right place.</p>
<h3 id="heading-test-types">Test types</h3>
<p>If you are not familiar with testing, you might get a bit lost. There are <a target="_blank" href="https://www.guru99.com/types-of-software-testing.html">a lot of test types</a>, but some are more common.</p>
<p>The <a target="_blank" href="https://www.atlassian.com/continuous-delivery/software-testing/types-of-software-testing">following article</a> from Atlassian provides a good starting point.</p>
<p>As we will take our first step in the Node.js test environment, our scope will revolve around unit and integration tests.</p>
<h3 id="heading-different-tools">Different tools</h3>
<p>Now, you might be wondering about <strong><em>where</em></strong> to start. You’ve seen a Node.js project that uses Jest, heard about Mocha or Chai but you are not sure what those are about.</p>
<p>Great, that’s exactly what we will be discussing today. To have a clearer picture, we can separate the different kinds of tools we need.</p>
<h4 id="heading-test-runners">Test runners</h4>
<p>You have this vague idea of writing tests but how do you organize and run them? This is the role of test runners. Some of their functionalities are:</p>
<ul>
<li><p>Finding test files and running tests</p>
</li>
<li><p>Reporting a test success or failure</p>
</li>
<li><p>Configuring which tests to run or re-run when updated</p>
</li>
<li><p>Setting up an environment</p>
</li>
</ul>
<p>And a lot more.</p>
<p>Here is a graph comparing the most popular Node.js test runners by download:</p>
<p><a target="_blank" href="https://www.npmtrends.com/jest-vs-mocha-vs-jasmine-vs-ava-vs-qunit"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692035729296/6ba3af3e-ae25-45be-9f68-0af02b391159.png" alt /></a></p>
<h4 id="heading-assertions">Assertions</h4>
<p>Now that you have an environment where you can write your tests, we come to the most important question: <strong>How do I write my first test?</strong></p>
<p>From our previously cited article, Atlassian says the following about unit tests:</p>
<p><em>They consist in testing individual methods and functions of the classes, components, or modules used by your software.</em></p>
<p>From a practical perspective you can view it as the following:</p>
<ul>
<li><p>Get the result from your individual unit (function for example) in a situation (with defined parameters).</p>
</li>
<li><p>Ensure the given result match the expected one.</p>
</li>
</ul>
<p>That’s what <strong>assertions</strong> are about: comparing the received result with the expected one to report the test as a success or failure.</p>
<p>Like tests runners, there are several libraries helping you with assertions:</p>
<p><a target="_blank" href="https://www.npmtrends.com/jest-vs-chai-vs-assert-vs-should-vs-better-assert-vs-unexpected"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692035730923/94be6b53-27b6-4cbb-99e4-1f7173c6798f.png" alt /></a></p>
<h4 id="heading-spies">Spies</h4>
<p>With unit and integration testing comes a difficulty:</p>
<ul>
<li><p>You might want to unit test a function that relies on an external dependency.</p>
</li>
<li><p>For unit as well as integration tests, your function can have a native dependency you cannot use in a JS environment.</p>
</li>
<li><p>Your function is making an API call but your server is not set up in your testing environment.</p>
</li>
</ul>
<p>That’s where spies are used. They help you create fakes (or <a target="_blank" href="https://en.wikipedia.org/wiki/Mock_object">mock objects</a>) and replace dependencies with an object you defined.</p>
<p>In the same way, several spy libraries can help you:</p>
<p><a target="_blank" href="https://www.npmtrends.com/jest-vs-sinon-vs-jack-vs-testdouble"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692035732481/474b1fe4-fa7e-4ed7-9a7c-3320dce36593.png" alt /></a></p>
<h3 id="heading-making-a-choice">Making a choice</h3>
<p>Great, you now have a general idea of what tools you need and a list of the most popular ones. Now comes the hardest part: which tools to choose?</p>
<h4 id="heading-test-runner">Test runner</h4>
<p>To be honest, I never really considered Jasmine, Ava, and QUnit.</p>
<p>I first started testing with Backend projects. At the time, Mocha was the more widely used, that’s the only reason why I started using it (with Chai).</p>
<p>Mocha worked great for a few years and then two changes happened:</p>
<ul>
<li><p>I started to specialize in React</p>
</li>
<li><p>Jest started to overthrow Mocha</p>
</li>
</ul>
<p><a target="_blank" href="https://www.npmtrends.com/jest-vs-mocha"><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692035733957/0ec08f92-6e16-47fa-8dd9-fb5a217021e2.png" alt /></a></p>
<p>At this point, I had to do a serious comparison.</p>
<p><a target="_blank" href="https://mochajs.org/#getting-started">Mocha</a> is a test runner that lets you choose an external assertion and spy library. It doesn’t restrict you but gives you more flexibility.</p>
<p>Jest is a whole testing framework, shipped with React and maintained by Facebook. From the previous sections and graphs, you might have noticed that more than a test runner, Jest includes its own assertion and mock libraries.</p>
<p>It’s interesting to note that Jest was built on top of Jasmine.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jestjs.io/">https://jestjs.io/</a></div>
<p> </p>
<p>Both are very similar. Of course, their APIs change, and you need to get used to it when going from one to the other.</p>
<p>I’m a big fan of having a single library/framework/utility that fulfills a given role. After trying out Jest and experimenting with React, I definitely adopted it.</p>
<h3 id="heading-afterward">Afterward</h3>
<p>Once you understand how to write tests, your best move would be to write <strong><em>great</em></strong> tests.</p>
<p><a target="_blank" href="https://en.wikipedia.org/wiki/Test-driven_development">Test-Driven Development</a> is a software development process that will definitely help you in writing great tests. Another useful tool is <a target="_blank" href="https://www.atlassian.com/continuous-delivery/software-testing/code-coverage">Code Coverage</a>.</p>
<p>I would like to complete the article from Atlassian on Code Coverage by saying the following:</p>
<p><em>If your Code Coverage is below 100% you can be sure you didn’t test everything. If it’s 100% … you don’t know.</em></p>
<p>Code Coverage can tell you if your code has been executed during your tests. Sometimes, you’ll end up executing code without really testing it. To be sure that you really tested everything, you must resort to <a target="_blank" href="https://en.wikipedia.org/wiki/Mutation_testing">Mutation Testing</a>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://stryker-mutator.io/">https://stryker-mutator.io/</a></div>
<p> </p>
<p>Mutation Testing is great but really expensive, and cannot be used as much as unit/integration testing.</p>
<p>Once you get into testing, you often see the following pyramid:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1692035735285/ee08cab4-5c37-4090-b7a1-82a83e798b53.png" alt /></p>
<p>What this represents is how many tests you should have. As unit tests are cheap to write and run, you should have plenty of them.</p>
<p>Still cheap but less than unit tests are: integration tests. It’s expected for an application to have a lot of integration tests but less than unit tests.</p>
<p>Finally, you’ll have e2e tests but in fewer numbers, as those are expensive to set up, write, and run.</p>
<p>Good luck on your testing journey.</p>
<hr />
<p>Cover photo by <a target="_blank" href="https://unsplash.com/@flowforfrank">flowforfrank</a> on <a target="_blank" href="https://unsplash.com/s/photos/code-test">Unsplash</a></p>
]]></content:encoded></item></channel></rss>