DEV Community

Cover image for Clean Architecture: Notes Backend in Node - Typescript
Chiranjeev Thomas
Chiranjeev Thomas

Posted on

Clean Architecture: Notes Backend in Node - Typescript

I hate complicated code! 😠

But do you know what I absolutely despise from the depths of my cold, black heart ?

" Needlessly Complicated Code " - That is the worst !! 🤬 
 
An abomination of the highest order, I tell you !!!



So, you can imagine the horror of me seeing my codebase mutate into a quagmire
 of interconnected code segments, i.e. files with a needlessly long list of dependencies that would break if I even looked at them the wrong way .It was a mess !!

So, what did I do next? I started researching Software Architecture.
I was looking for something that would produce decoupled code and give the added advantage of helping us migrate to microservices , if the need arose.

Long story short, I decided to go with Clean Architecture


Why you ask? Well, you can read it all about here : Embracing Clean Architecture: A Journey to Better Code

Let's now look into the fundamentals of Clean Architecture before trying to implement it for a simple Notes App


 
( 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 ) 

Clean Architecture stands on the following pillars:

  1. 1️⃣ Inversion of Control ( Dependency Inversion )
  2. 2️⃣ Separation of Concerns ( Software Divided into layers - Presentation, Data, Application, Domain ) 
  3. 3️⃣ One-way flow of data ( From the outer layer to the inner layer, with the inner layers knowing nothing about the outer layers)

* * You can read about Clean Architecture in detail on : Uncle Bob's Website

I found this to be the simplest way to visualise Clean Architecture

( WHAT'S GREAT ABOUT THIS PICTURE IS THAT YOU CAN DIRECTLY TRANSLATE IT TO CODE )



main.ts
Enter fullscreen mode Exit fullscreen mode







A look at our Project`s Directory Structure :


/src
│── main.ts
│── server.ts
│── presentation
│ └── routers
│ └── notes-router.ts
├── domain
│ ├── interfaces
│ │ ├── repositories
│ │ │ └── notes-repository.ts
│ │ └── use-cases
│ │ └── notes
│ │ ├── create-note-use-case.ts
│ │ ├── delete-note-use-case.ts
│ │ ├── get-all-notes-use-case.ts
│ │ ├── get-one-note-use-case.ts
│ │ └── update-note-use-case.ts
│ ├── models
│ │ └── note.ts
│ ├── repositories
│ │ └── notes-repository.ts
│ └── use-cases
│ └── notes
│ ├── create-note.ts
│ ├── delete-note.ts
│ ├── get-all-notes.ts
│ ├── get-one-note.ts
│ └── update-note.ts
└── data
 ├── interfaces
 │ └── data-sources
 │ ├── nosql-database-wrapper.ts
 │ └── note-data-source.ts
 └── data-sources
 ├── mongodb
 └── mongodb-notes-data-source.ts
 

Let's start with our first use case test : [ Create Note ]

(Observe how few dependencies we have in our test file )
create-note-test.png

In each Test File, we are going to test the implementation of a Single Unit of Test ( SUT ), and are going to mock all the related dependencies


So in the case of the create note use case our SUT is the implementation of the create note use case interface ,i.e : the CreateNote class , and we have a single dependency to mock : NotesRepository  


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 TDD way of doing things. 
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 separation of concerns and dependency injection, which are great for testing, as mocks can be easily created and injected into a SUT

So, let's get started by defining the CreateNotesUseCase Interface

create_note_use_case.png

Now, we observe that we need to define 2 more types to work with the Create Note Use Case Interface. 

These 2 types, namely the NoteRequestModel and NoteResponseModel describe the format 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 Database using the NotesRepository abstraction. 

Let's quickly define these 2 types :

create_note_use_case_types.png

Great, now we need to mock our singular dependency for the create note use case : ( NotesRepository ) 
The NotesReposity interface would provide the methods to CREATE, READ, UPDATE and DELETE notes.
Let's define the NotesRepository interface before mocking it : 

notes_repository.png

Finally , it's time for us to create the NotesRepository mock , and a helper function called getMockNotesRepository

mock_notes_repository.png

Another helper required to run our test :

expectedNotesOutput.png

Now, we need to define our SUT ( the concrete implementation of the CreateNoteUseCase )

create-note-impl.png



With all the setup done, we can finally go ahead and run our test!


create-note-test.png

And, we get a passing test :



Screenshot 2023–09–16 at 2.21.05 PM.png



What's next? Well as an assignment, go ahead and try to implement the rest of the use cases ( DeleteNoteUseCase, UpdateNoteUseCase, GetNotesUseCase, GetOneNoteUseCase ). 

Do follow the same TDD methodology while implementing your tests


We'll meet again and test the NotesRepository, till then … Adios !!


You can view the complete code and the repository with all test cases implemented over here: GutHub Repo  


My blog is inspired by a number of other blogs on the same subject, which are as follows :

1 . Clean Architecture Contacts
2 . Clean Architecture Fundamentals
3.TDD Fundamentals


Top comments (0)