DEV Community: Chiranjeev Thomas The latest articles on DEV Community by Chiranjeev Thomas (@code0monkey1). https://dev.to/code0monkey1 https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F342609%2F3bd3ab4e-2aa8-4df8-853f-a9d4f74fad24.png DEV Community: Chiranjeev Thomas https://dev.to/code0monkey1 en LLD : N X N Tic Tac Toe with O(1) checks Chiranjeev Thomas Sun, 21 Jul 2024 10:38:58 +0000 https://dev.to/code0monkey1/lld-n-x-n-tic-tac-toe-with-o1-checks-2ih0 https://dev.to/code0monkey1/lld-n-x-n-tic-tac-toe-with-o1-checks-2ih0 <p>๐Ÿš€ *<em>The Importance of Low-Level Design (LLD) in Interviews *</em> ๐Ÿš€</p> <p>In today's tech interviews, mastering Low-Level Design (LLD) is crucial.</p> <p>It demonstrates your ability to think through the detailed aspects of system design, ensuring robustness, scalability, and maintainability. </p> <p>Let's explore how to design a software system for such interviews with an advanced <strong>N x N Tic Tac Toe game</strong>! ๐ŸŽ‰</p> <p>๐Ÿ” <strong>Overview:</strong></p> <p>This project brings a modern twist to the classic Tic Tac Toe game, allowing players to compete on a flexible N x N board. </p> <p>The first player to align N marks in a row, column, or diagonal wins the game. If the board is filled without any player achieving this, the game ends in a tie.</p> <p>๐Ÿ“ <strong>Game Rules:</strong></p> <ol> <li><strong>Players:</strong></li> </ol> <ul> <li><p>Player 1 ('Bob')</p></li> <li><p>Player 2 ('Alice')</p></li> </ul> <ol> <li><strong>GamePlay:</strong></li> </ol> <ul> <li><p>Player 1 starts the game by placing a 'Bob' on the board.</p></li> <li><p>Player 2 follows by placing an 'Alice'.</p></li> <li><p>Players take turns to place their marks in an empty cell of the board.</p></li> <li><p>The game continues until a player wins or the board is full.</p></li> </ul> <ol> <li><strong>Winning Conditions:</strong></li> </ol> <ul> <li><p>A player wins by placing N marks consecutively in a row, column, or diagonal.</p></li> <li><p>If all cells are filled and no player has N consecutive marks, the game is a tie.</p></li> </ul> <p>๐Ÿ’ก <strong>Classes:</strong></p> <ol> <li><strong>Player:</strong></li> </ol> <ul> <li><p>Represents a player in the game.</p></li> <li><p>Can be extended to include additional player attributes and methods.</p></li> </ul> <ol> <li><strong>Board:</strong></li> </ol> <ul> <li><p>Manages the state of the game board.</p></li> <li><p>Contains the business logic to check for valid moves and determine the game status (win/tie).</p></li> </ul> <ol> <li><strong>Game:</strong></li> </ol> <ul> <li><p>Orchestrates the game flow.</p></li> <li><p>Interacts with the Player and Board classes to manage turns and check for game completion.</p></li> </ul> <p>โš™๏ธ <strong>Algorithm Design:</strong></p> <ul> <li>The time complexity for determining whether a player has won, tied, or has more moves left is constant time, <strong>O(1)</strong>.</li> </ul> <p>๐ŸŽฎ Ready to dive in? Check out the GitHub repository and start playing! ๐Ÿ•น๏ธ</p> <p>GitHub Link : <a href="proxy.php?url=https://lnkd.in/gGShtCTF" rel="noopener noreferrer">https://lnkd.in/gGShtCTF</a></p> <p>Reference Video : <a href="proxy.php?url=https://lnkd.in/gr6D5uBG" rel="noopener noreferrer">https://lnkd.in/gr6D5uBG</a></p> <h1> LowLevelDesign #TicTacToe #Python #GameDevelopment #OOP #DesignPatterns #Coding #SoftwareEngineering #GitHub #Tech #Interviews </h1> lld python systemdesign ๐Ÿš€ The Importance of Understanding Social Networking Basics ๐Ÿš€ Chiranjeev Thomas Tue, 28 May 2024 11:05:52 +0000 https://dev.to/code0monkey1/the-importance-of-understanding-social-networking-basics-58l7 https://dev.to/code0monkey1/the-importance-of-understanding-social-networking-basics-58l7 <p><a href="proxy.php?url=https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftctbra5kxee13sgdah1r.jpeg" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftctbra5kxee13sgdah1r.jpeg" alt="Image description" width="800" height="800"></a></p> <p>In today's digital era, almost every website incorporates social media components. Whether you're browsing through reviews on Amazon, checking out product ratings on Flipkart, or engaging in discussions on Reddit, social networking features are ubiquitous. They drive user interaction and engagement, making it crucial for tech enthusiasts and professionals to grasp the fundamentals of social networking websites.</p> <p>๐Ÿ” <strong>Why Should You Care?</strong></p> <p>Understanding these basics is not just about building traditional social media platforms like Facebook or Twitter</p> <ul> <li> <strong>E-commerce Sites</strong>: User reviews and ratings (like on Amazon) play a vital role in purchasing decisions.</li> <li> <strong>Content Platforms</strong>: Comments, likes, and shares (as seen on YouTube and blogs) increase user interaction.</li> <li> <strong>Professional Networks</strong>: LinkedIn recommendations and endorsements help build professional credibility.</li> </ul> <p>๐Ÿ› ๏ธ <strong>Exciting Backend Project Opportunity!</strong></p> <p>This backend project that encapsulates these social networking principles, designed with industry best practices like Test-Driven Development (TDD). <br> Here's a glimpse into the project features:</p> <h3> ๐ŸŒ Application Routes </h3> <h4> ๐Ÿ”‘ Auth Routes </h4> <ol> <li> <strong>/auth/login</strong>: Log in a user using their credentials.</li> <li> <strong>/auth/register</strong>: Register a new user to the system.</li> <li> <strong>/auth/logout</strong>: Log out a user from the system.</li> <li> <strong>/auth/refresh</strong>: Refresh the user's authentication token.</li> </ol> <h4> ๐Ÿ‘ค User Routes </h4> <ol> <li> <strong>/users</strong>: Fetch a list of all users.</li> <li> <strong>/users/:userId</strong>: Fetch details of a specific user.</li> <li> <strong>/users/:userId/avatar</strong>: Fetch the avatar of a specific user.</li> <li> <strong>/users/:userId/follow</strong>: Follow a specific user.</li> <li> <strong>/users/:userId/unfollow</strong>: Unfollow a specific user.</li> <li> <strong>/users/:userId/recommendations</strong>: Fetch recommendations for a specific user</li> </ol> <h4> ๐Ÿ“ Post Routes </h4> <ol> <li> <strong>/posts</strong>: Fetch a list of all posts.</li> <li> <strong>/posts/:postId</strong>: Fetch details of a specific post.</li> <li> <strong>/posts/:postId/photo</strong>: Fetch the photo of a specific post.</li> <li> <strong>/posts/:postId/comment</strong>: Comment on a specific post.</li> <li> <strong>/posts/:postId/uncomment</strong>: Delete a comment on a specific post.</li> <li> <strong>/posts/:postId/like</strong>: Like a specific post.</li> <li> <strong>/posts/:postId/unlike</strong>: Unlike a specific post.</li> <li> <strong>/posts/by/user/:userId</strong>: Fetch all posts by a specific user.</li> <li> <strong>/posts/feed/user/:userId</strong>: Get the feed for a specific user.</li> </ol> <h4> ๐Ÿ“‹ Self Routes </h4> <ol> <li> <strong>/self</strong>: Fetch information about the logged-in user.</li> </ol> <p>๐Ÿ”ง <strong>Built with Precision</strong><br> This project adheres to best practices, including TDD, to ensure robust and maintainable code. In a job market that increasingly demands senior-level expertise, writing clean and efficient code is non-negotiable.</p> <p>๐Ÿ“Ž <strong>Check it Out on GitHub!</strong><br> Explore the project in detail on my GitHub profile: <a href="proxy.php?url=https://lnkd.in/gCiFTSfF">https://lnkd.in/gCiFTSfF</a></p> <p>Stay tuned! The frontend for this backend application will be released soon, providing a complete full-stack solution.</p> <p>Happy coding! ๐ŸŒŸ</p> social node tdd mern My First Tech Startup ... An Accident? Chiranjeev Thomas Thu, 19 Oct 2023 08:29:12 +0000 https://dev.to/code0monkey1/my-first-tech-startup-an-accident-4hg https://dev.to/code0monkey1/my-first-tech-startup-an-accident-4hg <p>How solving a problem for my college mates gave me a glimpse into the world of tech startups </p> <h2> <strong>Prelude :</strong> </h2> <p>Let me begin by stating that it would be a <u>long stretch</u> for me to even call the humbe website I built for a few hundred people while still at the university, a <u>legitimate startup</u> .</p> <p><br><br> Heck , I didn't even code it ... just went on <u><a href="proxy.php?url=https://www.sites.google.com">Google Sites</a></u> and used predefined templates to put together a functional static website .</p> <p>At that moment , it was more about solving a problem I had myself, so that others like me would not have to struggle with it as much as I did.</p> <p>However , it did use <u>digital technology</u> to address a <u>pain point</u> , so I'll stick to calling it a <u>tech - startup</u> !</p> <blockquote> <p><strong><u>Scenario </u></strong> : </p> <p>Every year students have to appear for <em>2 main exams</em> , <strong>a unit test</strong> and submit a couple of <em>assignments</em> that are taken by <u>180</u> people of a specific Course ( MCA )<br> <br><br> As per exam paper trends , About 60 - 70 % of the Topics / Questions are repeated from the last 5 years papers in every subject<br> <br><br> Theoretically, if you come prepared with the answers to the past 5 years' exam questions, then you will be able to pass the exam without buying any books, making notes, or attending classes (of course, you should keep attendance in mind)<br> <br><br> <u><strong>The Problem</strong></u> : Digital Copies of Question papers are <u>non-existant</u> , and the only way to get ones hands on the previous years papers is to go to the 2nd floor of the University's Library and take a zerox of each and every question paper . </p> <p>And this needs to be done by everyone who wants to benefit from the question papers .</p> <p>So , theoretically , 180 people will have to go through this arduous process <u><strong>EVERY YEAR</strong></u></p> <p><strong>What I <u>did not</u> consider at the start :</strong></p> <ol> <li><p>How would I scale it ? ( will it even scale )</p></li> <li><p>What's the monetisation stategy going to be ?</p></li> <li><p>How will I market it ?</p></li> </ol> </blockquote> <blockquote> <p><strong>What I <u>did</u> cosider at the start :</strong></p> <ol> <li><p>Will people actually benefit from it ? ( does solving the problem have value )</p></li> <li><p>Will it make the life of even 1 person a bit better ?</p></li> <li><p>Will it solve a recurring problem ? ( i.e Will solving this problem have a long term impact , albeit , for a small group of people )</p></li> </ol> </blockquote> <blockquote> <p><u><strong>Some metrics :</strong> </u></p> <p><br><br> <br><br> <em>Starting Assumtion</em> : Maximum Number of people estimated to be helped at any point in time : 60 * 3 = <strong>180</strong> ( 3 batches of classes ) <br> <br><br> <em>Actual Number of page visits per month on average</em> : <strong><u>33.52</u></strong> ( Google Analytics )<br> <br><br> <em>Actual Number of unique visitors throughout the lifetime of the website</em> : <strong><u>1106</u></strong> ( Google Analytics )<br> <br><br> <em>Maximum number of people to ever visit the website in a given month</em> : <strong><u>120</u></strong> ( Google Analytics )<br> <br><br> <em>Money spent on marketing the product</em> : <strong>0</strong></p> </blockquote> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--VO1qq8h5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1693197970/Screenshot_2023_08_28_at_10_14_21_AM_91e769a412.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--VO1qq8h5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1693197970/Screenshot_2023_08_28_at_10_14_21_AM_91e769a412.png" alt="google_analytics" width="800" height="1575"></a></p> <h2> Simply put , the problem that I was trying to solve was the lack of digital resources for students to study for upcoming exams. </h2> <h2> To address this problem, I decided to create a website that would host digitally scanned exam papers. </h2> <h2> I used my own Google Drive to store the scanned papers, and I made it simple for students to access them through the website. </h2> <h2> The website was designed to be user-friendly and easy to navigate, mobile responsive , with a clean and modern design. </h2> <h2> The website gained a lot of traction since its launch. </h2> <h2> It has received positive reviews from students , who have found it to be a helpful resource for their studies and their careers. </h2> <h2> The website has also received favorable reviews from users on Quora and Facebook. </h2> <blockquote> <p><strong>Quora</strong> : <strong>"</strong><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--eevAUGCB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1693202993/Screenshot_2023_08_28_at_11_36_53_AM_75894d0887.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--eevAUGCB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1693202993/Screenshot_2023_08_28_at_11_36_53_AM_75894d0887.png" alt="Screenshot 2023-08-28 at 11.36.53 AM.png" width="800" height="153"></a> <strong>"</strong></p> </blockquote> <h2> However, the website did not generate any sponsorships / income for me. ( but then , that was never the objective ) </h2> <h2> In conclusion, my startup project was a success ( or at least how i defined success for this particuar project ) . </h2> <h2> I was able to solve a real-world problem by creating a simple website that hosted digitally scanned question papers that catered to the needs of a few hundred students . </h2> <blockquote> <p><u>Concluding Thoughts</u> : <br> <br><br> How would I have scaled the startup, had I chosen to !</p> <p>I could have made the website usefull for all the students at the university ( <u>More than 1,000,000 students</u> ) by making it easy for individuals from all courses to submit scanned papers to the website<br> <br><br> Which feature would have made the website <u>10X more usefull</u> ?</p> <p><strong>M.L algorithms</strong> that would have scanned question papers and predited the questions to be asked with high accuracy for all courses .<br> <br><br> How could I have monetise the startup ?</p> <p>Google Ads </p> <p>Ed-tech sponsorships </p> <p>Mentoring Framework ( 1 to 1 ) from industry exprets </p> <p>Premium Question paper generation with highest % of accuracy</p> <p><br><br> How would I have marketed it ?</p> <p>Posting about the website on Facebook groups of all the courses , along with the accuracy of the website to predict upcoming exam questions would have generated suffiient buzz and impetus for people to start using and contributing to the website </p> </blockquote> <blockquote> <p>( p.s : I did not purchase even a single text book during the 3 years of my masters and solely relied on Google Search+ Digital Question papers to prepare for my exame )</p> <p>( p.p.s : I spent <u><strong>no more than 6 hours of prep time / subject</strong></u> using the exam papers and scored above 60% in aggregate) </p> </blockquote> <p><u><em><a href="proxy.php?url=https://sites.google.com/view/usictmca">Click to View the Website: In case you wanted to have a look</a></em></u></p> startup webdev beginners programming Embracing Clean Architecture: A Journey to Better Code Chiranjeev Thomas Thu, 19 Oct 2023 08:27:02 +0000 https://dev.to/code0monkey1/embracing-clean-architecture-a-journey-to-better-code-4lde https://dev.to/code0monkey1/embracing-clean-architecture-a-journey-to-better-code-4lde <p>"DevReferral's journey from MVC to Clean Architecture: Discover how adopting Clean Architecture improved code quality, enabled Test-Driven Development (TDD), and paved the way for scalability, all while reducing technical debt. Explore the benefits of this transformation for startups seeking resilient and adaptable software solutions."</p> <blockquote> <p>In the fast-paced world of startups, it's crucial to build a strong foundation for your software from the very beginning. One of the most common challenges startups face is maintaining code that can adapt to evolving requirements while remaining maintainable and testable. At DevReferral, we embarked on a journey to transform our codebase by adopting Clean Architecture, and in this article, we'll share our experiences and insights along the way.</p> </blockquote> <h2> <u>The Road to Clean Architecture</u> </h2> <h3> DevReferral began as a small startup with big ambitions. Like many startups, we initially developed our application using the traditional MVC (Model-View-Controller) architecture with JavaScript in an Express.js Node.js app. This choice allowed us to quickly prototype and develop our application, but it came with its set of challenges. </h3> <h3> Our MVC architecture led to a tightly coupled system, where dependencies were intertwined, making any change a complex, error-prone process. To exacerbate the issue, our testing strategy primarily focused on router-level tests using tools like Supertest, leaving individual modules of code untested. This practice left us vulnerable to regressions and made the development process slower and riskier. </h3> <h3> As we realized the limitations of our initial architecture, we began exploring alternative approaches. Domain-Driven Design (DDD) and Clean Architecture stood out as promising options, each offering a unique set of benefits. Ultimately, we decided to adopt Clean Architecture because of its clear separation of concerns and its potential to facilitate the transition to microservices in the future. </h3> <h2> <br> </h2> <p><br><br> <strong><u>Understanding Clean Architecture</u></strong></p> <h3> Clean Architecture, introduced by Robert C. Martin, is a software architectural pattern that emphasizes modularity, maintainability, and testability. At its core, Clean Architecture advocates for a strict separation of concerns by dividing the codebase into concentric circles or layers, each with a specific responsibility. Let's explore these layers: </h3> <ol> <li> <strong>Entities</strong>: At the center of the architecture are the entities, which represent the core business domain. These entities are independent of any external frameworks or technologies and encapsulate the business rules. </li> <li> <strong>Use Cases</strong>: Surrounding the entities are use cases or interactors. These use cases define the application's business logic. They serve as an intermediary layer between the entities and the external world. </li> <li> <strong>Interfaces</strong>: Moving outward, we have the interfaces layer. This layer defines the boundaries through which data flows in and out of the application. It includes interfaces for communication with external systems, such as APIs and databases. </li> <li> <strong>Frameworks and Drivers</strong>: The outermost layer consists of frameworks and drivers. This layer is responsible for handling external concerns like the user interface, web frameworks (e.g., Express.js), and database access. It's essential to note that this layer depends on the inner layers, but the inner layers remain independent of the outer layers.</li> </ol> <h2> <br> </h2> <p><strong><u>Benefits of Clean Architecture</u></strong><br> <br></p> <ol> <li> <strong>Separation of Concerns</strong>: Clean Architecture enforces a strict separation of concerns, making it easier to reason about different parts of your application. This separation improves code organization and maintainability. </li> <li> <strong>Test-Driven Development (TDD)</strong>: Clean Architecture facilitates the practice of TDD by allowing you to write isolated unit tests for each component. With well-defined boundaries, it becomes simpler to mock dependencies and ensure that your code behaves as expected. </li> <li> <strong>Scalability and Adaptability</strong>: By decoupling the core business logic from external frameworks and technologies, Clean Architecture makes it easier to adapt to changing requirements and even transition to microservices when your startup grows. </li> <li> <strong>Reduced Technical Debt</strong>: With cleaner code and well-defined boundaries, you'll accumulate less technical debt over time. This leads to a more efficient development process and fewer bugs. --- <strong><u>Our Journey with Clean Architecture</u></strong> Adopting Clean Architecture at DevReferral was not without its challenges. Here are some key steps we took on our journey: </li> <li> <strong>Education</strong>: We invested time in understanding the principles of Clean Architecture thoroughly. This involved reading books, articles, and attending workshops to ensure our team had a shared understanding of the approach. </li> <li> <strong>Refactoring</strong>: We started refactoring our existing codebase incrementally. This process involved identifying the core entities and use cases and gradually migrating them to fit the Clean Architecture structure. </li> <li> <strong>Dependency Injection</strong>: We adopted dependency injection to manage dependencies between layers. This allowed us to replace real implementations with mocks during testing, making our unit tests more isolated. </li> <li> <strong>Testing Culture</strong>: We cultivated a testing culture within our team. We began writing unit tests for all our use cases and entities to ensure they behaved correctly. This approach helped us identify and fix issues early in the development process. </li> <li> <strong>Code Reviews and Knowledge Sharing</strong>: We encouraged code reviews and knowledge sharing sessions among team members. This facilitated learning and ensured that the Clean Architecture principles were consistently applied. --- <strong><u>Conclusion</u></strong> In the journey of adopting Clean Architecture at DevReferral, we witnessed a significant transformation in our codebase. The separation of concerns, emphasis on testability, and scalability provided by Clean Architecture have empowered our team to build more robust and adaptable software. While transitioning to Clean Architecture required effort and commitment, the benefits it brought to our startup far outweighed the initial challenges. Clean Architecture has not only improved our code quality but also set the stage for future growth and the potential transition to microservices. As a startup, investing in Clean Architecture early on has proven to be a wise decision. It's a testament to the importance of building a solid foundation for your software, one that can withstand the evolving demands of your business. In conclusion, if you're a startup founder or developer looking to build maintainable, scalable, and testable software, consider embarking on your own journey to Clean Architecture. It's a path that can lead to code that's not only clean but also resilient in the face of change, ultimately setting your startup on a path to success.</li> </ol> cleancode architecture typescript express GPT Prompts: A Next.js 13 App ( App Router ) Chiranjeev Thomas Thu, 19 Oct 2023 08:24:24 +0000 https://dev.to/code0monkey1/gpt-prompts-a-nextjs-13-app-app-router--4g1f https://dev.to/code0monkey1/gpt-prompts-a-nextjs-13-app-app-router--4g1f <p>A Next.js 13 + Tailwind app, that lets users submit prompts that can be used to perform various tasks using CHAT GPT</p> <p><a href="proxy.php?url=https://nexjs-13-fundamentals.vercel.app/"> GPT Prompts: Live Project </a></p> <p><u><a href="proxy.php?url=https://github.com/code0monkey1/nexjs-13-fundamentals">GitHub Repo </a></u></p> nextjs tailwindcss crud typescript System Design (Comprehensive List) Chiranjeev Thomas Thu, 19 Oct 2023 08:22:38 +0000 https://dev.to/code0monkey1/system-design-comprehensive-list-1oek https://dev.to/code0monkey1/system-design-comprehensive-list-1oek <p>System Design Concepts List and related links to comprehensive reviews</p> <h1> Objectives : </h1> <blockquote> <p>๐Ÿ’Ž To have a comprehensive understanding of different software architectures ( SOA , Monoliths , Micro-Services )</p> <p>๐Ÿ’Ž To confidently design complex systems such as Stripe , Amazon , Instagram ... While discussing the tradeoffs of a selected architectural style </p> <p>๐Ÿ’Ž Identify and select various auxiliary services for special use cases in a system , while discussing their benefits and trade offs</p> </blockquote> <h2> <strong><u>Key Concepts ( Pre - Req's )</u></strong> </h2> <p>1&gt; <code>Scalability</code></p> <p>2&gt; <code>Availability</code></p> <p>3&gt; <code>Reliability</code></p> <p>4&gt; <code>Replication</code></p> <p><br><br> 5&gt; <code>Load Balancing</code></p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> &gt; a. What problems do load balancers solve ? &gt; &gt; b. Types of Load Balancer &gt; &gt; c. Different Algorithms used in Load balancers &gt; &gt; d. Pros &amp; Cons (discussing the trade-offs when to use LBs are when to avoid) </code></pre> </div> <p>6&gt; <code>Caching</code></p> <blockquote> <pre class="highlight plaintext"><code> - What is caching? And what problems caching systems are trying to solve? - Cache Invalidation &amp; Cache eviction policies </code></pre> </blockquote> <p>7&gt; <code>Sharding Or Data Partitioning</code></p> <blockquote> <pre class="highlight plaintext"><code> - Partitioning Techniques - Leader Selection </code></pre> </blockquote> <p>8&gt; <code>Challenges that Sharding brings into the system</code></p> <blockquote> <ul> <li>Joins &amp; Denormalization</li> <li>Enforcing Integrity Checks &amp; Validations</li> <li>Rebalancing</li> </ul> </blockquote> <p>9&gt; <code>Indexing</code></p> <blockquote> <ul> <li>Need for Indexing and the problem Indexing is trying to solve</li> <li>MySQL Indexes</li> <li>NoSQL Indexes Challenges that Indexes bring into the systems</li> </ul> </blockquote> <p>10&gt; Proxies</p> <p>11&gt; Proxy Servers</p> <blockquote> <ul> <li>What problem Proxy servers are trying to solve?</li> <li>Types of Proxy Servers</li> </ul> </blockquote> <p>12&gt; Reverse Proxy Servers</p> <p>13&gt; Importance of Replication and Redundancy</p> <p>14&gt; NoSQL vs SQL<br> &gt; - Differences between SQL &amp; NoSQL (Storage, Schema, ACID properties, Scalability)</p> <p>15&gt; CAP (Consistency, Availability &amp; Fault Tolerance) theorem</p> <p>16&gt; Distributed Hash Tables (DHTS) &amp; Consistent Hashing</p> <blockquote> <ul> <li>What Problem Consistent Hashing is trying to solve?</li> <li>Examples when to use consistent hashing</li> </ul> <p>17&gt; HTTP Long Polling, Server Sent Events &amp; Web-sockets</p> </blockquote> <p>18&gt; APIs (Application Programming Interfaces)</p> <p>19&gt; Client-Server Architecture and Peer-to-peer</p> <p>20&gt; Microservice Patterns</p> <blockquote> <ul> <li>Decomposition Pattern</li> <li>Strangler Pattern</li> <li>SAGA Pattern</li> <li>CQRS Pattern</li> </ul> </blockquote> <p>20&gt;Different Types of Storage<br> &gt; - Object<br> &gt; - File<br> &gt; - Block</p> <p>21&gt; Message Queues</p> <p>22&gt; Gossip Protocols</p> <p>23&gt; Rate Limiting</p> <h2> Framework To Design Systems in System Design Interviews </h2> <blockquote> <p>Gathering Requirements<br> &gt; - Functional<br> &gt; - Non-Functional<br> &gt; </p> <ul> <li><p>High Level APIs provided by the system</p></li> <li><p>Capacity Planning</p></li> <li><p>Data Models</p></li> <li><p>High Level Design</p></li> <li><p>Focus On 1-2 individual components in the system</p></li> <li><p>Network Protocols</p></li> <li><p>Bottlenecks &amp; Single Point Of Failures in the system</p></li> <li><p>Caching</p></li> <li><p>Security</p></li> <li><p>Data Compression</p></li> </ul> </blockquote> <h2> Designing Distributed Systems (From Scratch) </h2> <blockquote> <p>Designing Uber, Rapido &amp; Ola</p> <p>Designing Swiggy, Zepto &amp; Blinkit</p> <p>Design Amazon Retail</p> <p>Designing Facebook/Instagram</p> <p>Design File sharing platforms (DropBox, Google Drive)</p> <p>Design A Key-Value Store (Like Amazon DynamoDB)</p> </blockquote> webdev designpatterns systemdesign interview REST API USING TYPESCRIPT ( CRUD ) Chiranjeev Thomas Thu, 19 Oct 2023 08:20:08 +0000 https://dev.to/code0monkey1/rest-api-using-typescript-crud--1mc0 https://dev.to/code0monkey1/rest-api-using-typescript-crud--1mc0 <p>An MVC Express Typescript App with CRUD operations, including File Upload and proper Authentication and Authorization using JWT </p> <h2> In this Article, we'll delve into the process of creating a RESTful API that implements CRUD operations using JWT for authorization </h2> <blockquote> <p>A basic CRUD api has options to CREATE / READ / UPDATE and DELETE items , along with Authentication and Authorisation in place , so as to ensure that the app transactions are secure</p> <p>๐Ÿ‘€ NOTE : This APP has not been created using the best testing practices ( i.e TDD or BDD ) , but we'll mitigate this issue later by creating a similar app , where WE'LL BE FOLLOWING TDD / BDD practices , and buiding it using the principles of <u><a href="proxy.php?url=https://www.chiranjeevthomas.com/article/clean-architecture" rel="noopener noreferrer">CEAN ARCHITECTURE</a></u></p> </blockquote> <h2> An Overview Of The Project : </h2> <blockquote> <ol> <li> โœ… The project is an e-commerce app that lets the ADMIN add products to the backend (The product has some text details and a picture )</li> <li> โœ… Sign up: Users can register by creating a new account using an <code>email address</code> and <code>password</code>.</li> <li> โœ… Authentication: Registered users can <code>login</code> and <code>logout</code>.</li> <li> โœ… Only <code>admins</code> are allowed to create products</li> <li> โœ… Protected user profile: Only registered users can view individual user details after signing in.</li> </ol> <h2> 18. โœ… We'll be using JWT to provide Authorization / Authentication in the app </h2> </blockquote> <h2> The API we'll be creating : </h2> <p><br><br> <a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdde6rnocj%2Fimage%2Fupload%2Fv1691905643%2Fapi_routes_4cfd29f90e.png" class="article-body-image-wrapper"><img alt="api_structure" src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdde6rnocj%2Fimage%2Fupload%2Fv1691905643%2Fapi_routes_4cfd29f90e.png"></a></p> <h2> <strong>Let's first talk about the USER MODEL</strong> </h2> <ul> <li>A person who registers on the app can either have the <u>role </u> of a <u><code>user</code></u> or an <u><code>admin</code></u> . </li> <li>An admin has all permissions but a user is limited to only <u><code>viewing products</code></u> and their <u><code>own information</code></u> </li> </ul> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import { Document, Schema, model } from 'mongoose'; // Define the interface for the User document const enum Role { ADMIN = 'admin', USER = 'user', } export interface IUser extends Document { username: string; email: string; password: string; role: Role; } // Declare the Schema of the Mongo model const userSchema = new Schema&lt;IUser&gt;( { username: { type: String, required: true, index: true, }, email: { type: String, required: true, unique: true, }, password: { type: String, required: true, }, role: { type: String, enum: [Role.ADMIN, Role.USER], default: Role.USER, }, }, { timestamps: true, } ); // Export the model export default model&lt;IUser&gt;('User', userSchema); </code></pre> </div> <blockquote> <p>So in the above <u><code>userSchema</code></u> , we define the<u> <code>username</code></u> , <u><em><code>email</code></em></u> and <u><code>password</code></u> for a user to register , along with a <u><code>role</code></u> that's set to <u> <code>user</code> </u> by default </p> <p>The roles are set as <code>Enums</code> deliberately , so as to avoid illegal assignment .<br> </p> </blockquote> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="kd">const</span> <span class="kr">enum</span> <span class="nx">Role</span> <span class="p">{</span> <span class="nx">ADMIN</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">admin</span><span class="dl">'</span><span class="p">,</span> <span class="nx">USER</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">user</span><span class="dl">'</span><span class="p">,</span> <span class="p">}</span> </code></pre> </div> <h2> Now , let's look at the <code>registerUser</code> controller </h2> <h3> Steps taken to register a user : </h3> <blockquote> <p>Step 1 : [+] validate the request and extract info</p> <p>Step 2 : [+] check if user is in database already</p> <p>Step 3: [+] hash password to encrypt</p> <p>Step 4: [+] create user object</p> <p>Setp 5: [+] save user to database</p> <p>Setp 6 : [+] create access_token ( JWT )</p> <p>Step 7 : [+] create refresh_token ( JWT )</p> <p>Step 8 : [+] save refresh_token to db</p> <p>Step 9 : [+] send json response to frontend ( with the newly created refresh_token and the access_token )</p> <p>Step 10 :[+] send async exception to <code>next(err)</code><br> </p> </blockquote> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>//registerUser.ts import { NextFunction, Request, Response } from 'express'; import { NextFunction, Request, Response } from 'express'; import { REFRESH_TOKEN_SECRET } from '../../config'; import { RefreshToken, User } from '../../models'; import { IUser } from '../../models/UserModel'; import CustomErrorHandler from '../../services/CustomErrorHandler'; import EncryptionService from '../../services/EncryptionService'; import JwtService from '../../services/JwtService'; import { RegisterUserRequest, RegisterUserResponse, Role } from '../../types'; import { registerSchema } from '../../validation'; type Tokens = { access_token: string; refresh_token: string; }; const registerUser = async ( req: Request&lt;RegisterUserRequest&gt;, res: Response&lt;RegisterUserResponse, Tokens&gt;, next: NextFunction ): Promise&lt;void&gt; =&gt; { try { // [+] validate the request and extract info const { username, email, password } = await registerSchema.parseAsync( req.body ); //[+] check if user is in database already const userExists = await User.exists({ email }); if (userExists) return next( CustomErrorHandler.alreadyExists('This email is already taken') ); //[+] hash password to encrypt const hashedPassword = await EncryptionService.getHashedToken(password); //[+] create user object const userInfo = { username, email, password: hashedPassword, role: Role.ADMIN, }; //[+] save user to database const user: IUser = await User.create(userInfo); //[+] create access_token const access_token = await JwtService.sign({ _id: user._id as string, role: user.role, }); //[+] create refresh_token const refresh_token = await JwtService.sign( { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment _id: user.id, role: user.role, }, '1y', REFRESH_TOKEN_SECRET ); //[+] save refresh_token to db await RefreshToken.create({ token: refresh_token }); // [+] send response const tokens: Tokens = { access_token, refresh_token }; res.json(tokens); } catch (err) { next(err); } }; export default { registerUser, }; </code></pre> </div> <h2> Let's turn our attention to the multiple imports we have at the top of the <code>resgisterUser</code> code ( p.s : this is a bad sign , as the number of imports shows how coupled our code is with other modules ) </h2> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code> <span class="k">import</span> <span class="p">{</span> <span class="nx">NextFunction</span><span class="p">,</span> <span class="nx">Request</span><span class="p">,</span> <span class="nx">Response</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">express</span><span class="dl">'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">REFRESH_TOKEN_SECRET</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../../config</span><span class="dl">'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">RefreshToken</span><span class="p">,</span> <span class="nx">User</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../../models</span><span class="dl">'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">IUser</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../../models/UserModel</span><span class="dl">'</span><span class="p">;</span> <span class="k">import</span> <span class="nx">CustomErrorHandler</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../../services/CustomErrorHandler</span><span class="dl">'</span><span class="p">;</span> <span class="k">import</span> <span class="nx">EncryptionService</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../../services/EncryptionService</span><span class="dl">'</span><span class="p">;</span> <span class="k">import</span> <span class="nx">JwtService</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../../services/JwtService</span><span class="dl">'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">RegisterUserRequest</span><span class="p">,</span> <span class="nx">RegisterUserResponse</span><span class="p">,</span> <span class="nx">Role</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../../types</span><span class="dl">'</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">registerSchema</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../../validation</span><span class="dl">'</span><span class="p">;</span> </code></pre> </div> <h2> Validation using Zod : </h2> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code> <span class="c1">// validationSchemas.ts</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">z</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">zod</span><span class="dl">'</span><span class="p">;</span> <span class="cm">/* The `.refine()` method is used to add additional validation rules to the validation schema. In this case, it is checking if the `password` field is equal to the `repeat_password` field. If they are not equal, it will throw an error with the specified message ("Passwords don't match") and the path of the error will be set to `['repeat_password']`. This allows for more specific error messages and error paths when validating the data. */</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">registerSchema</span> <span class="o">=</span> <span class="nx">z</span> <span class="p">.</span><span class="nf">object</span><span class="p">({</span> <span class="na">username</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="nf">string</span><span class="p">().</span><span class="nf">min</span><span class="p">(</span><span class="mi">3</span><span class="p">).</span><span class="nf">max</span><span class="p">(</span><span class="mi">20</span><span class="p">).</span><span class="nf">trim</span><span class="p">(),</span> <span class="na">email</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="nf">string</span><span class="p">().</span><span class="nf">email</span><span class="p">().</span><span class="nf">trim</span><span class="p">(),</span> <span class="na">password</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="nf">string</span><span class="p">().</span><span class="nf">min</span><span class="p">(</span><span class="mi">6</span><span class="p">),</span> <span class="na">repeat_password</span><span class="p">:</span> <span class="nx">z</span><span class="p">.</span><span class="nf">string</span><span class="p">().</span><span class="nf">min</span><span class="p">(</span><span class="mi">6</span><span class="p">),</span> <span class="p">})</span> <span class="p">.</span><span class="nf">refine</span><span class="p">((</span><span class="nx">data</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">data</span><span class="p">.</span><span class="nx">password</span> <span class="o">===</span> <span class="nx">data</span><span class="p">.</span><span class="nx">repeat_password</span><span class="p">,</span> <span class="p">{</span> <span class="na">message</span><span class="p">:</span> <span class="dl">"</span><span class="s2"> : Passwords don't match</span><span class="dl">"</span><span class="p">,</span> <span class="na">path</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">repeat_password</span><span class="dl">'</span><span class="p">],</span> <span class="c1">// path of error</span> <span class="p">});</span> </code></pre> </div> <h2> Custom Error Handler : </h2> <blockquote> <p>*A Convenient and Elegant way to handle errors is to create a <u>custom error handler </u> and define all errors over there</p> <p>*We will throw our custom errors if and when the need arises </p> <p>*By following this pattern, we are in complete control of the declariation and summoning of Errors from a single source of truth<br> </p> </blockquote> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="kd">class</span> <span class="nc">CustomErrorHandler</span> <span class="kd">extends</span> <span class="nc">Error</span> <span class="p">{</span> <span class="nf">constructor</span><span class="p">(</span><span class="k">public</span> <span class="nx">status</span><span class="p">:</span> <span class="kr">number</span><span class="p">,</span> <span class="k">public</span> <span class="nx">message</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="p">{</span> <span class="k">super</span><span class="p">();</span> <span class="k">this</span><span class="p">.</span><span class="nx">status</span> <span class="o">=</span> <span class="nx">status</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">message</span> <span class="o">=</span> <span class="nx">message</span><span class="p">;</span> <span class="p">}</span> <span class="cm">/** * The function returns a new instance of a custom error handler with a status code of 409 and a * given error message. * @param {string} message - The `message` parameter is a string that represents the error message to * be displayed. * @returns A new instance of the CustomErrorHandler class with a status code of 409 and the provided * message. */</span> <span class="k">static</span> <span class="nf">alreadyExists</span><span class="p">(</span><span class="nx">message</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="k">new</span> <span class="nc">CustomErrorHandler</span><span class="p">(</span><span class="mi">409</span><span class="p">,</span> <span class="nx">message</span><span class="p">);</span> <span class="p">}</span> <span class="cm">/** * The function returns a custom error handler with a 401 status code and a message indicating that * the user authentication failed due to an invalid password or email. * @param [message=password or email invalid] - The message parameter is a string that represents the * error message to be displayed when the user authentication fails. By default, the message is set * to 'password or email invalid'. * @returns A new instance of the CustomErrorHandler class with a status code of 401 and a message of * "password or email invalid". */</span> <span class="k">static</span> <span class="nf">userAuthFailed</span><span class="p">(</span><span class="nx">message</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">missing or invalid jwt</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* The line `return new CustomErrorHandler(401, message);` is creating a new instance of the `CustomErrorHandler` class with a status code of 401 and the provided error message. This instance is then returned by the `wrongUserCredentials` static method. */</span> <span class="k">return</span> <span class="k">new</span> <span class="nc">CustomErrorHandler</span><span class="p">(</span><span class="mi">401</span><span class="p">,</span> <span class="nx">message</span><span class="p">);</span> <span class="p">}</span> <span class="cm">/** * The function returns a custom error handler with a 401 status code and a message indicating * invalid password or email. * @param [message=password or email invalid] - The message parameter is a string that represents the * error message to be displayed when the user credentials are invalid. The default value is * 'password or email invalid'. * @returns a new instance of the CustomErrorHandler class with a status code of 401 and a message of * "password or email invalid". */</span> <span class="k">static</span> <span class="nf">wrongCredentials</span><span class="p">(</span><span class="nx">message</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">password or email invalid</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="k">new</span> <span class="nc">CustomErrorHandler</span><span class="p">(</span><span class="mi">401</span><span class="p">,</span> <span class="nx">message</span><span class="p">);</span> <span class="p">}</span> <span class="k">static</span> <span class="nf">notFound</span><span class="p">(</span><span class="nx">message</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">Not Found</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="k">new</span> <span class="nc">CustomErrorHandler</span><span class="p">(</span><span class="mi">404</span><span class="p">,</span> <span class="nx">message</span><span class="p">);</span> <span class="p">}</span> <span class="k">static</span> <span class="nf">unAuthorized</span><span class="p">(</span><span class="nx">message</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">auth failed</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="k">new</span> <span class="nc">CustomErrorHandler</span><span class="p">(</span><span class="mi">401</span><span class="p">,</span> <span class="nx">message</span><span class="p">);</span> <span class="p">}</span> <span class="k">static</span> <span class="nf">multerError</span><span class="p">(</span><span class="nx">message</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">Multer File Upload Error</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="k">new</span> <span class="nc">CustomErrorHandler</span><span class="p">(</span><span class="mi">401</span><span class="p">,</span> <span class="nx">message</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">CustomErrorHandler</span><span class="p">;</span> </code></pre> </div> <h3> <strong>You can View the Complete Project on my GitHub profile</strong> : <u> <a href="proxy.php?url=https://github.com/code0monkey1/rest-api-structure" rel="noopener noreferrer">here</a></u> </h3> webdev restapi typescript node Test Driven Development ( TDD ) Chiranjeev Thomas Thu, 19 Oct 2023 08:18:24 +0000 https://dev.to/code0monkey1/test-driven-development-tdd--570g https://dev.to/code0monkey1/test-driven-development-tdd--570g <p>The Benefits of Test-Driven Development in a TypeScript Express Project</p> <p><strong>Test-driven development (TDD) is a software development practice that involves writing tests before writing the code. This approach helps ensure that the code is thoroughly tested and meets the requirements before release. In the context of a TypeScript Express project, TDD can help improve the quality of the code and reduce the number of bugs.</strong><br> <br></p> <p><em>By following TDD, developers can write tests first and then write the code to make those tests pass. This approach can help catch bugs early in the development process and ensure the code is well-structured and easy to maintain. Additionally, TDD can help developers focus on the project's requirements and avoid getting bogged down in the implementation details.</em></p> <blockquote> <p>To start learning about TDD in a TypeScript Express project, wait for the upcoming exercises. In the exercises, you'll learn how to write tests using Jest and Typescript, and how to implement TDD in a simple Express project. By the end of the exercises, you'll have a better understanding of the benefits of TDD in a TypeScript Express project.</p> </blockquote> <h4> Meanwhile, you can try your hand at TDD by attempting any of the following exercises: <u><a href="proxy.php?url=https://www.tddbuddy.com/katas.html">TDD Buddy Exercises</a></u> </h4> <blockquote> <p><strong>Update: Exercises Added</strong></p> </blockquote> <p>1 . <u><a href="proxy.php?url=https://www.chiranjeevthomas.com/article/character-copy-kata">Character Copy Kata</a></u></p> webdev tdd jest typescript Character Copy Kata ( Test Driven Development ) Chiranjeev Thomas Thu, 19 Oct 2023 08:15:39 +0000 https://dev.to/code0monkey1/character-copy-kata-test-driven-development--1lih https://dev.to/code0monkey1/character-copy-kata-test-driven-development--1lih <p>A great kata for introducing jest mocks functions in your project.</p> <p><u><strong><a href="proxy.php?url=https://www.tddbuddy.com/katas/character-copy.html" rel="noopener noreferrer">Try attempting to solve the kata yourself before looking at the solution : Click here to view the Kata</a></strong></u></p> <blockquote> <h2> <strong>This Kata expects you to create a character copier class that reads characters from a source and copies them to a destination. The class must copy and write one character at a time</strong> </h2> <h3> <em>To do this, you need to create a Copier class that takes in a Source and a Destination</em> </h3> </blockquote> <p><strong>The <u>Source interface</u> has a method <br> <code>char ReadChar()</code> and the <u>Destination interface</u> has a method <code>void WriteChar(char c)</code>. The Copier class has a method <code>void Copy()</code> that reads from the ISource one character at a time and writes to the IDestination until a newline <code>(\n)</code> is encountered.</strong></p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdde6rnocj%2Fimage%2Fupload%2Fv1692347426%2Fchar_copy_dia_ba36c84dbd.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdde6rnocj%2Fimage%2Fupload%2Fv1692347426%2Fchar_copy_dia_ba36c84dbd.png"></a></p> <blockquote> <p>@ The copying and writing is done 1 character at a time. </p> <p>@ Only the Copier class may exist as a concrete class. </p> <p>@ You can use any language to implement the Source and Destination interfaces</p> </blockquote> <blockquote> <p>We're going to solve the following problem using <em><code>Typescript</code></em> and the <em><code>Jest</code></em> testing framework</p> </blockquote> <h3> First, lets identify all the possible tests you could think of for this scenario </h3> <blockquote> <p>[ โ“ ] No characters , ending in newline</p> <p>[ โ“ ] Once character , ending in newline</p> <p>[ โ“ ] Two characters , ending in newline</p> <p>[ โ“ ] Many characters, ending in newline</p> <p>[ โ“ ] Order of characters copied should be maintained</p> <p>[ โ“ ] Characters after newline should not be written</p> </blockquote> <p><em>Case 1 : [โ“] No characters , ending in newline</em></p> <blockquote> <p>Step 1 : <u>Define the most basic test case scenario</u><br> </p> </blockquote> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code> <span class="c1">//copy.test.ts</span> <span class="nf">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">when no character is read</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nf">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">does not call the desination interface`s writeChar method </span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="na">src</span><span class="p">:</span> <span class="nx">Source</span> <span class="o">=</span> <span class="nf">getSource</span><span class="p">([]);</span> <span class="c1">// helper method</span> <span class="kd">const</span> <span class="nx">dest</span> <span class="o">=</span> <span class="nf">getDestination</span><span class="p">();</span> <span class="c1">// helper method</span> <span class="kd">const</span> <span class="nx">sut</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Copier</span><span class="p">(</span><span class="nx">src</span><span class="p">,</span> <span class="nx">dest</span><span class="p">);</span> <span class="nx">sut</span><span class="p">.</span><span class="nf">copy</span><span class="p">();</span> <span class="nf">expect</span><span class="p">(</span><span class="nx">dest</span><span class="p">.</span><span class="nx">writeChar</span><span class="p">).</span><span class="nf">toBeCalledTimes</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> <span class="p">});</span> <span class="p">});</span> </code></pre> </div> <blockquote> <p>Step 2 : <u>Defining the helper functions</u></p> <p>To write tests, we will need two helper methods called <u>getSource </u>and <u>getDestinatio</u>n<br> that will provide us with the <strong><u>Source</u> and <u>Destination</u> interfaces</strong>. </p> <p>This will allow us to avoid repeatedly creating them for each test, and also make it easier to change any implementation details if needed</p> <p>( p.s : This step can come at a later stage too, after the tests become repetative. <br> But in our case, we'll be defining them from the very beginning )<br> </p> </blockquote> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code> <span class="c1">//copier.helper.ts</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">Source</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">./character-copier</span><span class="dl">'</span><span class="p">;</span> <span class="cm">/* The code is creating a mock function `mockCharReader` using `jest.fn()`. This mock function will be used to simulate the behavior of a character reader object. */</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">getSource</span> <span class="o">=</span> <span class="p">(</span><span class="nx">elements</span><span class="p">:</span> <span class="kr">string</span><span class="p">[]):</span> <span class="nx">Source</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">mockCharReader</span> <span class="o">=</span> <span class="nx">jest</span><span class="p">.</span><span class="nf">fn</span><span class="p">();</span> <span class="nx">elements</span><span class="p">.</span><span class="nf">forEach</span><span class="p">((</span><span class="nx">e</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">mockCharReader</span><span class="p">.</span><span class="nf">mockReturnValueOnce</span><span class="p">(</span><span class="nx">e</span><span class="p">));</span> <span class="cm">/* The line `mockCharReader.mockReturnValue('\n');` is setting the return value of the `mockCharReader` function to be the newline character (`'\n'`). This means that when `readChar` is called on the mock character reader object, it will return `'\n'` as the next character from the source. */</span> <span class="nx">mockCharReader</span><span class="p">.</span><span class="nf">mockReturnValue</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span><span class="p">);</span> <span class="cm">/* The `return` statement is creating an object with a property `readChar` that is assigned the value of the `mockCharReader` function. This object is then returned by the `getSource` function. */</span> <span class="k">return</span> <span class="p">{</span> <span class="na">readChar</span><span class="p">:</span> <span class="nx">mockCharReader</span><span class="p">,</span> <span class="p">};</span> <span class="p">};</span> </code></pre> </div> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code> <span class="c1">//copier.helper.ts</span> <span class="cm">/* The code is defining a function named `getDestination` that returns an object with two properties: `writeChar` and `getWrittenChars`. */</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">getDestination</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="na">copiedChars</span><span class="p">:</span> <span class="kr">string</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span> <span class="k">return</span> <span class="p">{</span> <span class="cm">/* The `writeChar` property is a function that is defined using `jest.fn()`. This creates a mock function that can be used to simulate the behavior of a `writeChar` function. */</span> <span class="na">writeChar</span><span class="p">:</span> <span class="nx">jest</span><span class="p">.</span><span class="nf">fn</span><span class="p">((</span><span class="na">c</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">copiedChars</span><span class="p">.</span><span class="nf">push</span><span class="p">(</span><span class="nx">c</span><span class="p">);</span> <span class="p">}),</span> <span class="cm">/* The `getWrittenChars` property is a function that returns the `copiedChars` array. This function can be used to retrieve the characters that have been written to the destination. */</span> <span class="na">getWrittenChars</span><span class="p">:</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">copiedChars</span><span class="p">,</span> <span class="p">};</span> <span class="p">};</span> </code></pre> </div> <h2> Now let's look at the code for our concrete <u>Copy</u> Class </h2> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code> <span class="err">\\</span><span class="nx">Copy</span><span class="p">.</span><span class="nx">ts</span> <span class="k">export</span> <span class="kd">class</span> <span class="nc">Copier</span> <span class="p">{</span> <span class="nf">constructor</span><span class="p">(</span> <span class="k">private</span> <span class="k">readonly</span> <span class="nx">src</span><span class="p">:</span> <span class="nx">Source</span><span class="p">,</span> <span class="k">private</span> <span class="k">readonly</span> <span class="nx">dest</span><span class="p">:</span> <span class="nx">Destination</span> <span class="p">)</span> <span class="p">{}</span> <span class="cm">/* The `copy()` method in the `Copier` class is responsible for copying characters from the source (`src`) to the destination (`dest`). */</span> <span class="nf">copy</span><span class="p">()</span> <span class="p">{</span> <span class="kd">let</span> <span class="nx">char</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">src</span><span class="p">.</span><span class="nf">readChar</span><span class="p">();</span> <span class="k">while </span><span class="p">(</span><span class="nx">char</span> <span class="o">!==</span> <span class="dl">'</span><span class="se">\n</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">dest</span><span class="p">.</span><span class="nf">writeChar</span><span class="p">(</span><span class="nx">char</span><span class="p">);</span> <span class="nx">char</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">src</span><span class="p">.</span><span class="nf">readChar</span><span class="p">();</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span> <span class="k">export</span> <span class="kr">interface</span> <span class="nx">Source</span> <span class="p">{</span> <span class="nf">readChar</span><span class="p">():</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="k">export</span> <span class="kr">interface</span> <span class="nx">Destination</span> <span class="p">{</span> <span class="nf">writeChar</span><span class="p">(</span><span class="nx">c</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="k">void</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <blockquote> <p>โœ… Result : <strong>A passing test</strong> ๐ŸŽ‰</p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdde6rnocj%2Fimage%2Fupload%2Fv1692369832%2Flarge_Screenshot_2023_08_18_at_8_12_26_PM_432a69168b.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdde6rnocj%2Fimage%2Fupload%2Fv1692369832%2Flarge_Screenshot_2023_08_18_at_8_12_26_PM_432a69168b.png" alt="A passing test "></a></p> </blockquote> <h2> In a similar format , we impelement all the rest of the test cases too </h2> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code> <span class="c1">//[+] Once character , ending in newline</span> <span class="nf">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">when 1 character is read</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">it</span><span class="p">.</span><span class="nf">each</span><span class="p">([{</span> <span class="na">char</span><span class="p">:</span> <span class="dl">'</span><span class="s1">a</span><span class="dl">'</span> <span class="p">},</span> <span class="p">{</span> <span class="na">char</span><span class="p">:</span> <span class="dl">'</span><span class="s1">b</span><span class="dl">'</span> <span class="p">},</span> <span class="p">{</span> <span class="na">char</span><span class="p">:</span> <span class="dl">'</span><span class="s1">c</span><span class="dl">'</span> <span class="p">},</span> <span class="p">{</span> <span class="na">char</span><span class="p">:</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span> <span class="p">}])(</span> <span class="dl">'</span><span class="s1">reads first char from source</span><span class="dl">'</span><span class="p">,</span> <span class="p">({</span> <span class="nx">char</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="na">src</span><span class="p">:</span> <span class="nx">Source</span> <span class="o">=</span> <span class="nf">getSource</span><span class="p">([</span><span class="nx">char</span><span class="p">]);</span> <span class="kd">const</span> <span class="nx">dest</span> <span class="o">=</span> <span class="nf">getDestination</span><span class="p">();</span> <span class="kd">const</span> <span class="nx">sut</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Copier</span><span class="p">(</span><span class="nx">src</span><span class="p">,</span> <span class="nx">dest</span><span class="p">);</span> <span class="nx">sut</span><span class="p">.</span><span class="nf">copy</span><span class="p">();</span> <span class="nf">expect</span><span class="p">(</span><span class="nx">dest</span><span class="p">.</span><span class="nx">writeChar</span><span class="p">).</span><span class="nf">toHaveBeenCalledWith</span><span class="p">(</span><span class="nx">char</span><span class="p">);</span> <span class="p">}</span> <span class="p">);</span> <span class="p">});</span> <span class="c1">//[+] Two characters , ending in newline</span> <span class="nf">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">Two characters , ending in newline</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">it</span><span class="p">.</span><span class="nf">each</span><span class="p">([</span> <span class="p">{</span> <span class="na">chars</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">a</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">b</span><span class="dl">'</span><span class="p">]</span> <span class="p">},</span> <span class="p">{</span> <span class="na">chars</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">c</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">]</span> <span class="p">},</span> <span class="p">{</span> <span class="na">chars</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">e</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">f</span><span class="dl">'</span><span class="p">]</span> <span class="p">},</span> <span class="p">])(</span><span class="dl">'</span><span class="s1">reads exactly 2 times before encountering a newline</span><span class="dl">'</span><span class="p">,</span> <span class="p">({</span> <span class="nx">chars</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="na">src</span><span class="p">:</span> <span class="nx">Source</span> <span class="o">=</span> <span class="nf">getSource</span><span class="p">(</span><span class="nx">chars</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">dest</span> <span class="o">=</span> <span class="nf">getDestination</span><span class="p">();</span> <span class="kd">const</span> <span class="nx">sut</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Copier</span><span class="p">(</span><span class="nx">src</span><span class="p">,</span> <span class="nx">dest</span><span class="p">);</span> <span class="nx">sut</span><span class="p">.</span><span class="nf">copy</span><span class="p">();</span> <span class="nf">expect</span><span class="p">(</span><span class="nx">dest</span><span class="p">.</span><span class="nx">writeChar</span><span class="p">).</span><span class="nf">toHaveBeenCalledWith</span><span class="p">(</span><span class="nx">chars</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span> <span class="nf">expect</span><span class="p">(</span><span class="nx">dest</span><span class="p">.</span><span class="nx">writeChar</span><span class="p">).</span><span class="nf">toHaveBeenCalledWith</span><span class="p">(</span><span class="nx">chars</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span> <span class="nf">expect</span><span class="p">(</span><span class="nx">dest</span><span class="p">.</span><span class="nx">writeChar</span><span class="p">).</span><span class="nf">toBeCalledTimes</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span> <span class="p">});</span> <span class="p">});</span> <span class="c1">//[+] Many characters, ending in newline</span> <span class="nf">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">Many characters, ending in newline</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">it</span><span class="p">.</span><span class="nf">each</span><span class="p">([</span> <span class="p">{</span> <span class="na">chars</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">a</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">b</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">c</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">g</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">c</span><span class="dl">'</span><span class="p">]</span> <span class="p">},</span> <span class="p">{</span> <span class="na">chars</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">c</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">e</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">f</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">f</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">]</span> <span class="p">},</span> <span class="p">{</span> <span class="na">chars</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">e</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">f</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">g</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">,</span> <span class="dl">''</span><span class="p">]</span> <span class="p">},</span> <span class="p">])(</span> <span class="dl">'</span><span class="s1">reads exactly $chars.length times before encountering a newline</span><span class="dl">'</span><span class="p">,</span> <span class="p">({</span> <span class="nx">chars</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="na">src</span><span class="p">:</span> <span class="nx">Source</span> <span class="o">=</span> <span class="nf">getSource</span><span class="p">(</span><span class="nx">chars</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">dest</span> <span class="o">=</span> <span class="nf">getDestination</span><span class="p">();</span> <span class="kd">const</span> <span class="nx">sut</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Copier</span><span class="p">(</span><span class="nx">src</span><span class="p">,</span> <span class="nx">dest</span><span class="p">);</span> <span class="nx">sut</span><span class="p">.</span><span class="nf">copy</span><span class="p">();</span> <span class="nf">expect</span><span class="p">(</span><span class="nx">dest</span><span class="p">.</span><span class="nx">writeChar</span><span class="p">).</span><span class="nf">toBeCalledTimes</span><span class="p">(</span><span class="nx">chars</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span> <span class="c1">//multiple characters written (order does not matter)</span> <span class="nx">chars</span><span class="p">.</span><span class="nf">forEach</span><span class="p">((</span><span class="nx">c</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nf">expect</span><span class="p">(</span><span class="nx">dest</span><span class="p">.</span><span class="nx">writeChar</span><span class="p">).</span><span class="nf">toHaveBeenCalledWith</span><span class="p">(</span><span class="nx">c</span><span class="p">));</span> <span class="c1">// confirm last called character</span> <span class="nf">expect</span><span class="p">(</span><span class="nx">dest</span><span class="p">.</span><span class="nx">writeChar</span><span class="p">).</span><span class="nf">toHaveBeenLastCalledWith</span><span class="p">(</span> <span class="nx">chars</span><span class="p">[</span><span class="nx">chars</span><span class="p">.</span><span class="nx">length</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="p">);</span> <span class="p">}</span> <span class="p">);</span> <span class="p">});</span> <span class="c1">//[+] Order of characters</span> <span class="nf">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">Order of characters</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">it</span><span class="p">.</span><span class="nf">each</span><span class="p">([</span> <span class="p">{</span> <span class="na">chars</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">a</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">b</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">c</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">g</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">c</span><span class="dl">'</span><span class="p">]</span> <span class="p">},</span> <span class="p">{</span> <span class="na">chars</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">c</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">e</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">f</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">f</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">]</span> <span class="p">},</span> <span class="p">{</span> <span class="na">chars</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">e</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">f</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">g</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">d</span><span class="dl">'</span><span class="p">,</span> <span class="dl">''</span><span class="p">]</span> <span class="p">},</span> <span class="p">])(</span><span class="dl">'</span><span class="s1">has all characters copied in the same order</span><span class="dl">'</span><span class="p">,</span> <span class="p">({</span> <span class="nx">chars</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="na">src</span><span class="p">:</span> <span class="nx">Source</span> <span class="o">=</span> <span class="nf">getSource</span><span class="p">(</span><span class="nx">chars</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">dest</span> <span class="o">=</span> <span class="nf">getDestination</span><span class="p">();</span> <span class="kd">const</span> <span class="nx">sut</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Copier</span><span class="p">(</span><span class="nx">src</span><span class="p">,</span> <span class="nx">dest</span><span class="p">);</span> <span class="nx">sut</span><span class="p">.</span><span class="nf">copy</span><span class="p">();</span> <span class="nf">expect</span><span class="p">(</span><span class="nx">dest</span><span class="p">.</span><span class="nx">writeChar</span><span class="p">).</span><span class="nf">toBeCalledTimes</span><span class="p">(</span><span class="nx">chars</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span> <span class="nf">expect</span><span class="p">(</span><span class="nx">dest</span><span class="p">.</span><span class="nf">getWrittenChars</span><span class="p">()).</span><span class="nf">toEqual</span><span class="p">(</span><span class="nx">chars</span><span class="p">);</span> <span class="p">});</span> <span class="p">});</span> <span class="c1">//[+] Characters after newline should not be written</span> <span class="nf">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">Characters after newline should not be written</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">it</span><span class="p">.</span><span class="nf">each</span><span class="p">([</span> <span class="p">{</span> <span class="na">chars</span><span class="p">:</span> <span class="p">[</span><span class="s2">`1`</span><span class="p">,</span> <span class="s2">`2`</span><span class="p">,</span> <span class="s2">`3`</span><span class="p">,</span> <span class="s2">`4`</span><span class="p">,</span> <span class="s2">`5`</span><span class="p">,</span> <span class="s2">`6`</span><span class="p">,</span> <span class="s2">`7`</span><span class="p">,</span> <span class="s2">`\n`</span><span class="p">,</span> <span class="s2">`a`</span><span class="p">,</span> <span class="s2">`b`</span><span class="p">],</span> <span class="na">before</span><span class="p">:</span> <span class="p">[</span><span class="s2">`1`</span><span class="p">,</span> <span class="s2">`2`</span><span class="p">,</span> <span class="s2">`3`</span><span class="p">,</span> <span class="s2">`4`</span><span class="p">,</span> <span class="s2">`5`</span><span class="p">,</span> <span class="s2">`6`</span><span class="p">,</span> <span class="s2">`7`</span><span class="p">],</span> <span class="na">after</span><span class="p">:</span> <span class="p">[</span><span class="s2">`a`</span><span class="p">,</span> <span class="s2">`b`</span><span class="p">],</span> <span class="p">},</span> <span class="p">{</span> <span class="na">chars</span><span class="p">:</span> <span class="p">[</span><span class="s2">`1`</span><span class="p">,</span> <span class="s2">`2`</span><span class="p">,</span> <span class="s2">`7`</span><span class="p">,</span> <span class="s2">`5`</span><span class="p">,</span> <span class="s2">`4`</span><span class="p">,</span> <span class="s2">`\n`</span><span class="p">,</span> <span class="s2">`c`</span><span class="p">,</span> <span class="s2">`d`</span><span class="p">],</span> <span class="na">before</span><span class="p">:</span> <span class="p">[</span><span class="s2">`1`</span><span class="p">,</span> <span class="s2">`2`</span><span class="p">,</span> <span class="s2">`7`</span><span class="p">,</span> <span class="s2">`5`</span><span class="p">,</span> <span class="s2">`4`</span><span class="p">],</span> <span class="na">after</span><span class="p">:</span> <span class="p">[</span><span class="s2">`c`</span><span class="p">,</span> <span class="s2">`d`</span><span class="p">],</span> <span class="p">},</span> <span class="p">])(</span> <span class="dl">'</span><span class="s1">has all characters before : $before , the newline and none after : $after</span><span class="dl">'</span><span class="p">,</span> <span class="p">({</span> <span class="nx">chars</span><span class="p">,</span> <span class="nx">before</span><span class="p">,</span> <span class="nx">after</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="na">src</span><span class="p">:</span> <span class="nx">Source</span> <span class="o">=</span> <span class="nf">getSource</span><span class="p">(</span><span class="nx">chars</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">dest</span> <span class="o">=</span> <span class="nf">getDestination</span><span class="p">();</span> <span class="kd">const</span> <span class="nx">sut</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Copier</span><span class="p">(</span><span class="nx">src</span><span class="p">,</span> <span class="nx">dest</span><span class="p">);</span> <span class="nx">sut</span><span class="p">.</span><span class="nf">copy</span><span class="p">();</span> <span class="nf">expect</span><span class="p">(</span><span class="nx">dest</span><span class="p">.</span><span class="nf">getWrittenChars</span><span class="p">()).</span><span class="nf">toStrictEqual</span><span class="p">(</span><span class="nx">before</span><span class="p">);</span> <span class="nf">expect</span><span class="p">(</span><span class="o">!</span><span class="nf">isIntersection</span><span class="p">(</span><span class="nx">after</span><span class="p">,</span> <span class="nx">dest</span><span class="p">.</span><span class="nf">getWrittenChars</span><span class="p">())).</span><span class="nf">toBe</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span> <span class="p">}</span> <span class="p">);</span> <span class="p">});</span> </code></pre> </div> <blockquote> <p>โœ… Result : We pass all the tests ๐Ÿ”ฅ๐Ÿ”ฅ</p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdde6rnocj%2Fimage%2Fupload%2Fv1692367254%2FScreenshot_2023_08_18_at_7_29_27_PM_738c68d077.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fdde6rnocj%2Fimage%2Fupload%2Fv1692367254%2FScreenshot_2023_08_18_at_7_29_27_PM_738c68d077.png" alt="Screenshot 2023-08-18 at 7.29.27 PM.png"></a></p> </blockquote> <p><strong>We do need one more helper function <u><code>isIntersection</code></u> , that lets us find out if any element in one array is present in another array ( i.e Is there an intersection between the values of 2 arrays ? )</strong><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code> <span class="c1">//copy.helper.ts</span> <span class="cm">/* The `isIntersection` function takes in two arrays `a` and `b` as parameters. It checks if there is any element in array `a` that is also present in array `b`. It does this by using the `some` method on array `a`, which returns `true` if at least one element satisfies the provided condition. The condition being checked is whether `b` includes the current element `e` from array `a`. If there is at least one element that satisfies this condition, the function returns `true`, indicating that there is an intersection between the two arrays. Otherwise, it returns `false`, indicating that there is no intersection. */</span> <span class="k">export</span> <span class="kd">const</span> <span class="nx">isIntersection</span> <span class="o">=</span> <span class="p">(</span><span class="nx">a</span><span class="p">:</span> <span class="kr">string</span><span class="p">[],</span> <span class="nx">b</span><span class="p">:</span> <span class="kr">string</span><span class="p">[])</span> <span class="o">=&gt;</span><span class="nx">a</span><span class="p">.</span><span class="nf">some</span><span class="p">((</span><span class="nx">e</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">b</span><span class="p">.</span><span class="nf">includes</span><span class="p">(</span><span class="nx">e</span><span class="p">));</span> </code></pre> </div> <h2> ๐Ÿ‘ Great !! This concludes our tutorial . </h2> <h3> If you want , you can have a look at the souce code on GitHub : <em><u><a href="proxy.php?url=https://github.com/code0monkey1/character-copier-tdd-typescript" rel="noopener noreferrer">Click Here To View Source Code</a></u></em> </h3> <blockquote> <p>If you have any questions regarding the exercise , you're free to add a comment on the blog post and i'll try to answer it ASAP ๐Ÿง‘โ€๐Ÿ’ป ! </p> </blockquote> tdd typescript node webdev Writing User Info to files in CSV format ( TDD Kata ) - Part 2 Chiranjeev Thomas Thu, 19 Oct 2023 08:12:25 +0000 https://dev.to/code0monkey1/writing-user-info-to-files-in-csv-format-tdd-kata-part-2-3970 https://dev.to/code0monkey1/writing-user-info-to-files-in-csv-format-tdd-kata-part-2-3970 <p>Part 2 of the <code>Writing User Info to files in CSV format</code> kata. </p> <p>In this particular kata, we are faced with the challenge of batch-processing our user data, as importing and writing millions of records resulted in our server crashing.</p> <h1> <strong><em>Summary :</em></strong> </h1> <p><br><br> We continue from <strong><u><a href="proxy.php?url=https://www.chiranjeevthomas.com/article/writing-user-info-to-files-in-csv-format-tdd-kata-part-1">Part 1 ( start with this post ) </a></u></strong> , where we defined the problem and suggested the probable solution for it , given the scenario .<br> <br><br> Now, we have a different requriement. Our product manager has informed us that because we're trying to write millions of customer records to a file , our backend server keeps on crashing ... and because of that we're been tasked with creating a batched processing feature . <br> <br><br> So , in this case , we'll be able to specify the amout of Customer Info we'll batch together and write in 1 go , before the next batch comes in . Thereby preventing the backend from getting overwhelmened and eventually crasing ! <br> <br><br> We need to also ensure that previous tests don't break, which can be achieved by building on top of the previous impelemtations , rather than modifying them , i.e : adhere to the <u>Open Closed Principle</u> and the <u>Single Responsibility Principle</u> .</p> <blockquote> <p>Objectives of ths Post : <br> <br><br> 1 : Create a batched processing system</p> <p>2 : Extend the functionality of the existing app , without failing previous tests</p> <p>3 : Manage the test suit , while expanding the functionality </p> <p>4 : Follow the SOLID principles ( particularly Single Responsibility and Open Closed Principles )</p> </blockquote> webdev node tdd typescript Writing User Info to files in CSV format ( TDD Kata ) - Part 1 Chiranjeev Thomas Thu, 19 Oct 2023 08:10:37 +0000 https://dev.to/code0monkey1/writing-user-info-to-files-in-csv-format-tdd-kata-part-1-4f42 https://dev.to/code0monkey1/writing-user-info-to-files-in-csv-format-tdd-kata-part-1-4f42 <p>How does one handle changes to the requirements in a real-life setting while building software using Test Driven Development?</p> <p>This article explores different techniques one can apply in situations where requirements for a feature keep changing constantly</p> <h1> <strong><em>Summary :</em></strong> </h1> <p>How does one handle <u>changes in feature requirements</u> while building software using Test Driven Development ? </p> <p>This article explores different techniques one can apply in situations where requirements for a feature keep changing constantly .<br> <br><br> We will require the knowledge of <u>S.O.L.I.D principles</u> , <u>Typescript </u> and some knowledge of <u>creating and using mocks in Jest</u> ( However , you're free to use whichever language and testing framework you like )<br><br> <br></p> <p>There will be <u>4 different parts to this exercise</u> , reflecting the different requirements that were brought in by the product manager at different stages of the prouduct development process</p> <blockquote> <p>Task 1 : Write Customer information to a file in CSV format </p> <p>Task 2 : Write Customer information in batches of N customers at a time </p> <p>Task 3 : Wrire Customer information , with no duplicate entries</p> <p>Task 4 : Write Customer information to 2 different sources ( one with duplicates and one without for monitoring purposes )</p> </blockquote> <h2> <u>In this specific post , we'll be concerntating on the 1st task and getting things set up !</u> </h2> <h3> First Let's look at the UML Diagram for the particular situation : </h3> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--YGHHt4Vs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1692877213/Screenshot_2023_08_24_at_5_08_47_PM_f7a9c489d6.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--YGHHt4Vs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1692877213/Screenshot_2023_08_24_at_5_08_47_PM_f7a9c489d6.png" alt="Screenshot 2023-08-24 at 5.08.47 PM.png" width="800" height="284"></a></p> <p><br><br> <strong><u>Code</u> :</strong><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>//Customer.ts class Customer { constructor(private _name: string, private _contactNumber: string) {} constructor(private _name: string, private _contactNumber: string) {} public get name(): string { return this._name; } public set name(value: string) { this._name = value; } public get contactNumber(): string { return this._contactNumber; } public set contactNumber(value: string) { this._contactNumber = value; } } export default Customer; </code></pre> </div> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>\\FileWriter.ts interface FileWriter { writeLine(fileName: string, line: string): void; } export default FileWriter; </code></pre> </div> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>//CustomerFileWriter.ts export class CustomerFileWriter { constructor(private readonly fileWriter: FileWriter) {} writeCustomers(fileName: string, customers: Customer[]) { // to implement !!! } private customerToString = (customer: Customer) =&gt; { return `${customer.name},${customer.contactNumber}`; }; } </code></pre> </div> <p>( We'll be exploring a slightly <u>different flavor</u> of Test Driven Development called<u> Behavior Driven Development</u> )<br><br> <br></p> <h5> What separates this methodology from traditional T.D.D is the focus on getting a feature to work, and s<u>ingle-mindedly </u>writing tests to achieve the same. </h5> <blockquote> <p>Personally, I aim to get the <em>happy paths</em> for a given feature passing, as soon as possible !</p> </blockquote> <p><strong>The are 3 things you should always keep in mind while formulating a B.D.D test</strong> </p> <blockquote> <ol> <li><code>GIVEN ( the starting condition )</code></li> <li><code>WHEN ( some action occurs )</code></li> <li><code>THEN ( expected result achieved )</code></li> </ol> </blockquote> <h2> To further discuss the test cases for the behavior we expect when attempting to write a customer's information to a file, we can consider the following scenarios: </h2> <p><em>Case 1</em> : </p> <blockquote> <p>given : <strong>there is no customer object in the customers array</strong></p> <p>when : <strong>we try to write customers to a file</strong></p> <p>then : <strong>nothing is written to a file ( i.e <u>writeLine</u> function is not called)</strong><br> </p> </blockquote> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>\\customer-file-writer.test.ts it(' will not write to file , when there is no customer', () =&gt; { //arrange const fileWriter = getFileWriter(); const fileName = 'myfile.pdf'; //act const sut = getCustomerFileWriter(fileWriter); sut.writeCustomers(fileName, []); //assert expect(fileWriter.writeLine).toBeCalledTimes(0); }); </code></pre> </div> <p><em>Helper functions :</em><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> \\customer-file-writer.helper.ts export const getFileWriter = (): FileWriter =&gt; { return { writeLine: jest.fn((_fileName: string, _line: string) =&gt; {}), }; }; export const getCustomerFileWriter = ( fileWriter: FileWriter ): ICustomerFileWriter =&gt; { return new CustomerFileWriter(fileWriter); }; </code></pre> </div> <blockquote> <p>Result : โœ… Passing Test </p> </blockquote> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--fmhQ1XTI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1692881144/Screenshot_2023_08_24_at_6_14_32_PM_bc1ea91e6f.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--fmhQ1XTI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1692881144/Screenshot_2023_08_24_at_6_14_32_PM_bc1ea91e6f.png" alt="Screenshot 2023-08-24 at 6.14.32 PM.png" width="800" height="158"></a></p> <blockquote> <p>๐Ÿง <u>Let's reflect on what we just did</u> : <br> <br><br> We are able to pass this test without even implementing any logic ! Great !</p> <p>So now what would happen when we have <u>1 customer is the customer array </u>? <br> <br><br> What do we expect in that case ? </p> </blockquote> <p><em>Case 2</em> : </p> <blockquote> <p>given : <strong>single customer object is present</strong></p> <p>when : <strong>we try to write the object info to file</strong></p> <p>then : <strong>only single object with the given info gets written</strong><br> </p> </blockquote> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>\\customer-file-writer.test.ts it('single customer object is present', () =&gt; { //arrange const fileWriter = getFileWriter(); const customer = getCustomer('Glen', '32'); const fileName = 'myfile.pdf'; //act const sut = getCustomerFileWriter(fileWriter); sut.writeCustomers(fileName, [customer]); //assert expect(fileWriter.writeLine).toBeCalledTimes(1); expect(fileWriter.writeLine).toHaveBeenLastCalledWith( fileName, customerToString(customer) ); }); </code></pre> </div> <p>Helper function :<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>//customer-file-writer.helper.ts export const getCustomer = (name: string, contactNumber: string): Customer =&gt; { return new Customer(name, contactNumber); }; export const customerToString = (customer: Customer) =&gt; { return `${customer.name},${customer.contactNumber}`; }; </code></pre> </div> <blockquote> <p>โŒ Now if you try to run the test , you'll encounter a test failure . </p> <p>To mitigate that you'll have to write the least possible code in <u>CustomerFileWriter</u> to get the test passing<br> </p> </blockquote> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>Before Modification : customer-file-writer.ts //customer-file-writer.ts export class CustomerFileWriter { constructor(private readonly fileWriter: FileWriter) {} writeCustomers(fileName: string, customers: Customer[]) { // to implement !!! } private customerToString = (customer: Customer) =&gt; { return `${customer.name},${customer.contactNumber}`; }; } </code></pre> </div> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>\\ Modified : customer-file-writer.ts export class CustomerFileWriter implements ICustomerFileWriter { constructor(private readonly fileWriter: FileWriter) {} writeCustomers(fileName: string, customers: Customer[]) { // this is the single implementation change we make this.fileWriter.writeLine(fileName, this.customerToString(customers[0])); // } private customerToString = (customer: Customer) =&gt; { return `${customer.name},${customer.contactNumber}`; }; } </code></pre> </div> <blockquote> <p>Result : โœ… Passing test ! <br> <a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--LT6gGCps--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1692883797/Screenshot_2023_08_24_at_6_40_37_PM_935aba2c84.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--LT6gGCps--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1692883797/Screenshot_2023_08_24_at_6_40_37_PM_935aba2c84.png" alt="Screenshot 2023-08-24 at 6.40.37 PM.png" width="800" height="110"></a></p> </blockquote> <blockquote> <p>๐Ÿง <u>Let's reflect on what we did</u> : <br> <br><br> We changed a single line , and wrote a single customer's information to a file .<br> <br><br> Will this work for <u>multiple customers</u> ? </p> </blockquote> <p><em>Case 3</em> :</p> <blockquote> <p>given : <strong>multiple customer objects are present</strong></p> <p>when : <strong>we try to write the objects info to file</strong></p> <p>then : <strong>the exact number and the exact order of customers are written to file</strong><br> </p> </blockquote> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>\\ customer-file-writer.test.ts it.each([ { customers: createCustomers(3), }, { customers: createCustomers(12), }, ])( '$customers.length customers info gets written , in the proper order ', ({ customers }) =&gt; { //arrange const fileWriter = getFileWriter(); const fileName = 'narnia.ts'; //act const sut = getCustomerFileWriter(fileWriter); sut.writeCustomers(fileName, customers); //assert assertCustomersHaveBeenWritten(fileWriter, fileName, customers); //has same number of items as the number of customers expect(fileWriter.writeLine).toHaveBeenCalledTimes(customers.length); // items written in same order , i.e last item written last expect(fileWriter.writeLine).toHaveBeenLastCalledWith( fileName, `${customers[customers.length - 1].name},${ customers[customers.length - 1].contactNumber }` ); } </code></pre> </div> <p>Helper Function :<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> //customer-file-writer.helper.ts export const createCustomers = (count: number) =&gt; { const customers: Customer[] = []; for (let i = 1; i &lt;= count; i++) { customers.push(getCustomer(i + '', i + '')); } return customers; }; </code></pre> </div> <blockquote> <p>โŒ This again results in a test failure <br> <br><br> Now, let's again think about the least amount of code we can write to make the tests pass .<br> </p> </blockquote> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>\\ Modified : customer-file-writer.ts export class CustomerFileWriter implements ICustomerFileWriter { constructor(private readonly fileWriter: FileWriter) {} writeCustomers(fileName: string, customers: Customer[]) { // we modified this line : this.fileWriter.writeLine(fileName, this.customerToString(customers[0])); // rather than writing a single customer , this time , we write multiple customers customers.forEach((customer) =&gt; { this.fileWriter.writeLine(fileName, this.customerToString(customer)); }); } } </code></pre> </div> <blockquote> <p>Result : โœ… Our tests for multiple customer pass ๐Ÿ”ฅ๐Ÿ”ฅ</p> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--uNmxvt1h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1692886481/Screenshot_2023_08_24_at_7_42_16_PM_7f7f76cf2b.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--uNmxvt1h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1692886481/Screenshot_2023_08_24_at_7_42_16_PM_7f7f76cf2b.png" alt="Screenshot 2023-08-24 at 7.42.16 PM.png" width="800" height="188"></a></p> </blockquote> <p><br><br> <br><br> <em>That's all for now !</em><br> <br><br> <br><br> <strong>Stay tuned for when our <u>product manager</u> arrives with a new set of modifications for our Customer File Writer feature .</strong><br> <br><br> <br><br> <strong>We'll have to modify and adapt to the new requrements , and ensure we do the least amount of modification to our code and keep our implementation SOLID compliant .</strong></p> <h2> <br> </h2> <p><u><a href="proxy.php?url=https://github.com/code0monkey1/csv-file-kata">Click Here : GitHub Code Repo </a></u></p> webdev tdd typescript node Clean Architecture: Notes Backend in Nodeโ€Š-โ€ŠTypescript Chiranjeev Thomas Mon, 18 Sep 2023 08:48:10 +0000 https://dev.to/code0monkey1/clean-architecture-notes-backend-in-node-typescript-amm https://dev.to/code0monkey1/clean-architecture-notes-backend-in-node-typescript-amm <blockquote> <h1> I hate complicated code! ๐Ÿ˜  </h1> <p><em>But do you know what I <u>absolutely despise</u> <del>from the depths of my cold, black heart</del>ย ?</em></p> <p><em>" Needlessly Complicated Code "</em>โ€Š-โ€ŠThat is the worstย !! ๐Ÿคฌย <br> ย <br> <strong>An abomination of the highest order, I tell youย !!!</strong></p> </blockquote> <p><br><br> So, you can imagine the horror of me seeing my codebase mutate into a quagmire<br> ย of interconnected code segments, i.e. <u> files with a needlessly long list of dependencies that would break if I even looked at them the wrong way</u>ย .<strong>It was a messย !!</strong><br> <br></p> <p>So, what did I do next? I started researching <strong>Software Architecture</strong>.<br> I was looking for something that would produce decoupled code and give the added advantage of helping us migrate to <u>microservices</u>ย , if the need arose.</p> <blockquote> <p>Long story short, I decided to go with <em>Clean Architecture</em><br> <br><br> Why you ask? Well, you can read it all about hereย : <u><em><a href="proxy.php?url=https://www.chiranjeevthomas.com/article/embracing-clean-architecture-a-journey-to-better-code-at-dev-referral">Embracing Clean Architecture: A Journey to Better Code</a></em></u></p> </blockquote> <h1> Let's now look into the fundamentals of Clean Architecture before trying to implement it for a simple Notes App </h1> <p><br>ย <br> ( Why a simple notes app? Cause, understanding clean architecture is hard enoughย โ€ฆ If we add more complexity, it would end up being incomprehensible for someone getting introduced to clean architecture for the first time )ย <br> <br></p> <p><u> Clean Architecture stands on the following pillars: </u></p> <ol> <li>1๏ธโƒฃ Inversion of Control ( Dependency Inversion )</li> <li>2๏ธโƒฃ Separation of Concerns ( Software Divided into layersโ€Š-โ€ŠPresentation, Data, Application, Domain )ย </li> <li>3๏ธโƒฃ One-way flow of data ( From the outer layer to the inner layer, with the inner layers knowing nothing about the outer layers)</li> </ol> <p>* * You can read about Clean Architecture in detail onย : <u><em><a href="proxy.php?url=https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">Uncle Bob's Website </a></em></u><br> <br></p> <h1> <u> <em>I found this to be the simplest way to visualise Clean Architecture</em> </u> </h1> <p>( WHAT'S GREAT ABOUT THIS PICTURE IS THAT YOU CAN DIRECTLY TRANSLATE IT TO CODE )<br> <br><br> <a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--X1eXXTOm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694768114/clean_architecture_ce31e2e471.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--X1eXXTOm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694768114/clean_architecture_ce31e2e471.png" width="800" height="414"></a><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code>main.ts </code></pre> </div> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s---rYCgGFm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694850829/main_5e6f5bf16f.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s---rYCgGFm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694850829/main_5e6f5bf16f.png" width="800" height="671"></a><br> <br><br> <br><br> <br></p> <h1> <u><strong>A look at our Project`s Directory Structureย :</strong></u> </h1> <p><code></code><code><br> /src<br> โ”‚โ”€โ”€ main.ts<br> โ”‚โ”€โ”€ server.ts<br> โ”‚โ”€โ”€ presentation<br> โ”‚ โ””โ”€โ”€ routers<br> โ”‚ โ””โ”€โ”€ notes-router.ts<br> โ”œโ”€โ”€ domain<br> โ”‚ โ”œโ”€โ”€ interfaces<br> โ”‚ โ”‚ โ”œโ”€โ”€ repositories<br> โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ notes-repository.ts<br> โ”‚ โ”‚ โ””โ”€โ”€ use-cases<br> โ”‚ โ”‚ โ””โ”€โ”€ notes<br> โ”‚ โ”‚ โ”œโ”€โ”€ create-note-use-case.ts<br> โ”‚ โ”‚ โ”œโ”€โ”€ delete-note-use-case.ts<br> โ”‚ โ”‚ โ”œโ”€โ”€ get-all-notes-use-case.ts<br> โ”‚ โ”‚ โ”œโ”€โ”€ get-one-note-use-case.ts<br> โ”‚ โ”‚ โ””โ”€โ”€ update-note-use-case.ts<br> โ”‚ โ”œโ”€โ”€ models<br> โ”‚ โ”‚ โ””โ”€โ”€ note.ts<br> โ”‚ โ”œโ”€โ”€ repositories<br> โ”‚ โ”‚ โ””โ”€โ”€ notes-repository.ts<br> โ”‚ โ””โ”€โ”€ use-cases<br> โ”‚ โ””โ”€โ”€ notes<br> โ”‚ โ”œโ”€โ”€ create-note.ts<br> โ”‚ โ”œโ”€โ”€ delete-note.ts<br> โ”‚ โ”œโ”€โ”€ get-all-notes.ts<br> โ”‚ โ”œโ”€โ”€ get-one-note.ts<br> โ”‚ โ””โ”€โ”€ update-note.ts<br> โ””โ”€โ”€ data<br> ย โ”œโ”€โ”€ interfaces<br> ย โ”‚ โ””โ”€โ”€ data-sources<br> ย โ”‚ โ”œโ”€โ”€ nosql-database-wrapper.ts<br> ย โ”‚ โ””โ”€โ”€ note-data-source.ts<br> ย โ””โ”€โ”€ data-sources<br> ย โ”œโ”€โ”€ mongodb<br> ย โ””โ”€โ”€ mongodb-notes-data-source.ts<br> ย <br> </code><code></code></p> <h1> <u>Let's start with our first use case test</u>ย : [ <em>Create Note</em> ] </h1> <p>(Observe how few dependencies we have in our test file )<br> <a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--HWFf-Yo---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694851280/create_note_test_c2c3c5b44a.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--HWFf-Yo---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694851280/create_note_test_c2c3c5b44a.png" alt="create-note-test.png" width="800" height="711"></a><br> <br></p> <blockquote> <h3> In each Test File, we are going to test the implementation of a <u>Single Unit of Test ( SUT )</u>, and are going to <u>mock all the related dependencies </u> </h3> <p>So in the case of the <em>create note use case</em> our <u>SUT</u> is the implementation of the <em>create note use case interfaceย ,i.eย : the <u>CreateNote class</u></em>ย , and we have a <strong>single dependency</strong> to mockย : <u> <em>NotesRepository</em> </u>ย </p> </blockquote> <p> When we try to execute this file, we'll encounter failure immediately, as nothing has been implemented as yet! That's great, and it's by designย โ€ฆ as we'll be practising the <u>TDD</u> way of doing things.ย  <br> The great thing about following Clean Architecture is that it allows you to apply TDD practices from the get-go and in a very convenient way. The whole system of Clean Architecture promotes <u>separation of concerns</u> and <u>dependency injection</u>, which are great for testing, as mocks can be easily created and injected into a <u>SUT</u> </p> <h1> So, let's get started by defining the <u>CreateNotesUseCase </u> Interface </h1> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--RvrlVVT3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694849832/create_note_use_case_fbedbec188.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--RvrlVVT3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694849832/create_note_use_case_fbedbec188.png" alt="create_note_use_case.png" width="800" height="257"></a><br> <br></p> <p>Now, we observe that we need to define 2 more types to work with the Create Note Use Case Interface.ย <br> <br></p> <p>These 2 types, namely the <strong>NoteRequestModel</strong> and <strong>NoteResponseModel</strong> describe the <em>format</em> of the input data that are supplied to the CreateNoteUseCase's execute method and the format of the data we'll get in response to saving it to the <u>Database</u> using the <u>NotesRepository</u> abstraction.ย <br> <br></p> <h2> Let's quickly define these 2 typesย : </h2> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--ipOrBbRx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694850642/create_note_use_case_types_3521540a36.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--ipOrBbRx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694850642/create_note_use_case_types_3521540a36.png" alt="create_note_use_case_types.png" width="800" height="355"></a></p> <p> Great, now we need to mock our singular dependency for the create note use caseย : ( NotesRepository )ย  <br> The NotesReposity interface would provide the methods to <u>CREATE</u>, <u>READ</u>, <u>UPDATE</u> and <u>DELETE</u> notes. <br> Let's define the NotesRepository interface before mocking itย :ย  </p> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--oHAKojr9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694852459/notes_repository_5f08746e8c.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--oHAKojr9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694852459/notes_repository_5f08746e8c.png" alt="notes_repository.png" width="800" height="333"></a><br> <br></p> <h2> Finallyย , it's time for us to create the <u>NotesRepository mock</u>ย , and a <u>helper</u> function called <u>getMockNotesRepository</u> </h2> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--ul3xXXGV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694853227/mock_notes_repository_c9a9f3dcbd.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--ul3xXXGV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694853227/mock_notes_repository_c9a9f3dcbd.png" alt="mock_notes_repository.png" width="800" height="612"></a></p> <h2> Another helper required to run our testย : </h2> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--5Uk7tDuT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694858720/expected_Notes_Output_a5eda43dfd.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--5Uk7tDuT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694858720/expected_Notes_Output_a5eda43dfd.png" alt="expectedNotesOutput.png" width="800" height="390"></a><br> <br></p> <h2> Now, we need to define our SUT ( the concrete implementation of the CreateNoteUseCase ) </h2> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--j2Ec5hqy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694853566/create_note_impl_622291f7a3.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--j2Ec5hqy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694853566/create_note_impl_622291f7a3.png" alt="create-note-impl.png" width="800" height="379"></a><br> <br></p> <p><br><br> With all the setup done, we can finally go ahead and run our test! <br> <br><br> <a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--HWFf-Yo---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694851280/create_note_test_c2c3c5b44a.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--HWFf-Yo---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694851280/create_note_test_c2c3c5b44a.png" alt="create-note-test.png" width="800" height="711"></a><br> <br></p> <h2> And, we get a passing testย : </h2> <p><br><br> <a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--nLnWJ6ev--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694854326/Screenshot_2023_09_16_at_2_21_05_PM_2dfbc63dcb.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--nLnWJ6ev--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/dde6rnocj/image/upload/v1694854326/Screenshot_2023_09_16_at_2_21_05_PM_2dfbc63dcb.png" alt="Screenshot 2023โ€“09โ€“16 at 2.21.05 PM.png" width="800" height="96"></a></p> <p><br><br> What's next? Well as an assignment, go ahead and try to implement the rest of the use cases ( <u>DeleteNoteUseCase</u>, <u>UpdateNoteUseCase</u>, <u>GetNotesUseCase</u>, <u>GetOneNoteUseCase</u> ).ย </p> <h2> Do follow the same TDD methodology while implementing your tests </h2> <h1> We'll meet again and test the NotesRepository, till thenย โ€ฆ Adiosย !! </h1> <p>You can view the complete code and the repository with all test cases implemented over here: <u> <strong><a href="proxy.php?url=https://github.com/DevReferral/clean-architecture-notes-typescript-tdd/tree/main">GutHub Repo</a></strong> </u>ย <br> <br></p> <p>My blog is inspired by a number of other blogs on the same subject, which are as followsย :</p> <p>1ย . <a href="proxy.php?url=https://nanosoft.co.za/blog/post/clean-api">Clean Architecture Contacts</a><br> 2ย . <a href="proxy.php?url=https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">Clean Architecture Fundamentals </a><br> 3.<a href="proxy.php?url=https://www.codurance.com/katas/fizzbuzz">TDD Fundamentals </a></p> webdev typescript tdd cleancode