Skip to content

Commit

Permalink
Merge pull request #1240 from goldbergyoni/section-1-improvements
Browse files Browse the repository at this point in the history
Modernizing to 2023 - various new itams
  • Loading branch information
goldbergyoni authored May 9, 2023
2 parents 1b829bf + 0047834 commit 92ad4d9
Show file tree
Hide file tree
Showing 13 changed files with 420 additions and 243 deletions.
16 changes: 8 additions & 8 deletions .github/workflows/lint-and-generate-html-from-markdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ jobs:
NODE_ENV: test

steps:
- name: Checkout
uses: actions/checkout@v3
- name: Checkout
uses: actions/checkout@v3

- name: Setup Node.js environment
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Setup Node.js environment
uses: actions/setup-node@v3
with:
node-version: "18"

- run: npm install
- run: npm run lint
- run: npm install
- run: npm run lint
226 changes: 151 additions & 75 deletions README.md

Large diffs are not rendered by default.

Binary file added assets/images/The backend testing checklist.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion .operations/package.json → package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "[✔]: assets/images/checkbox-small-blue.png",
"main": "gen-html.js",
"scripts": {
"lint": "markdownlint ../README*.md"
"lint": "markdownlint ./README*.md"
},
"repository": {
"type": "git",
Expand Down
34 changes: 31 additions & 3 deletions sections/projectstructre/breakintcomponents.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### One Paragraph Explainer

For medium sized apps and above, monoliths are really bad - having one big software with many dependencies is just hard to reason about and often leads to spaghetti code. Even smart architects — those who are skilled enough to tame the beast and 'modularize' it — spend great mental effort on design, and each change requires carefully evaluating the impact on other dependent objects. The ultimate solution is to develop small software: divide the whole stack into self-contained components that don't share files with others, each constitutes very few files (e.g. API, service, data access, test, etc.) so that it's very easy to reason about it. Some may call this 'microservices' architecture — it's important to understand that microservices are not a spec which you must follow, but rather a set of principles. You may adopt many principles into a full-blown microservices architecture or adopt only a few. Both are good as long as you keep the software complexity low. The very least you should do is create basic borders between components, assign a folder in your project root for each business component and make it self-contained - other components are allowed to consume its functionality only through its public interface or API. This is the foundation for keeping your components simple, avoid dependency hell and pave the way to full-blown microservices in the future once your app grows.
For medium sized apps and above, *non-modular* monoliths are really bad - having one big software with 'spaghetti' of dependencies is just hard to reason about. The ultimate solution is to develop smaller software: divide the whole stack into self-contained components that don't share files with others, each is a standalone logical app (e.g. has its own API, service, data access, test, etc.) so that onboarding into it and changing the code is much easier than dealing with the whole system. Some may call this 'microservices' architecture — it's important to understand that microservices are not a spec which you must follow, but rather a set of principles. You may adopt many principles into a full-blown microservices architecture or adopt only a few. The very least you should do is create basic borders between components, assign a folder or repository in your system root for each business component and make it self-contained. Other components are allowed to consume its functionality only through its public interface or API. This is the foundation for keeping your components simple, avoid dependency hell and pave the way to full-blown microservices in the future once your app grows

<br/><br/>

Expand All @@ -28,10 +28,38 @@ So what does the architecture of your application scream? When you look at the t

### Good: Structure your solution by self-contained components

![alt text](../../assets/images/structurebycomponents.PNG "Structuring solution by components")
```bash
my-system
├─ apps (components)
│ ├─ orders
│ │ ├─ package.json
│ │ ├─ api
│ │ ├─ domain
│ │ ├─ data-access
│ ├─ users
│ ├─ payments
├─ libraries (generic cross-component functionality)
│ ├─ logger
│ ├─ authenticator
```


<br/><br/>

### Bad: Group your files by technical role

![alt text](../../assets/images/structurebyroles.PNG "Structuring solution by technical roles")
```bash
my-system
├─ controllers
│ ├─ user-controller.js
│ ├─ order-controller.js
│ ├─ payment-controller.js
├─ services
│ ├─ user-service.js
│ ├─ order-service.js
│ ├─ payment-service.js
├─ models
│ ├─ user-model.js
│ ├─ order-model.js
│ ├─ payment-model.js
```
44 changes: 44 additions & 0 deletions sections/projectstructre/choose-framework.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Structure your solution by components

<br/><br/>

### Recommended frameworks: Pros and cons

Unlike other choices, choosing the core framework determines strategic factors like the development style and how likely the team is to hit a wall. We believe that framework popularity is a supreme consideration and put our focus on the top 4 most popular frameworks in terms of downloads and GitHub stars. The text below outlines the pros and cons of each framework and how to match a framework to the right application type

**express.js**

Pros: Unmatched popularity; gigantic eco-system of extensions and middleware; simple to learn and use; familiar to almost every Node.js developer; tons of community articles and videos are based on express

Cons: Covers a small subset of a typical application needs - merely a web server that invokes the app function per URL. Choosing express means leaving a handful of app concerns uncovered; outdated mechanics - no native support for async-await; barely maintained and updated; Slower than others

**Nest.js**

Pros: More batteries than any other option - covers many application concern including message queues, scheduled jobs and more; OOP-style is an advantage for teams who appreciate this design style; awesome docs; well-maintained; high popularity with a vibrant community

Cons: High-level abstractions that cloud built-in Node.js conventions; The inclusion of many features, heavy usage of TypeScript and reference to sophisticated patterns might push teams for increased complexity; Steeper learning curve due to a handful of unique narratives (e.g., interceptors, guards, modules, and more); Highly opinionated

**Fastify**

Pros: Relatively simple and lean; mostly based on Node.js/JavaScript standards; relatively shallow learning curve; with its official plugins cover many application concerns though not as rich as Nest.js;

Cons: Younger than others and not as popular yet; smaller eco-system compared to express and Nest.js

**Koa**

Pros When compared with express: it's Simpler and nimbler; modern API with async/await support; better performance

Cons: Covers a small subset of a typical application needs - leaves a handful of app concerns uncovered; Not as popular as express and Nest.js

### A brief choosing guide

**Prefer express.js when** - having an experienced architect onboard _and_ in a need to control the fine-grained pieces of the puzzle. In this circumstances, Koa is also a solid option with a more modern API than express but a much smaller eco-system

**Prefer Fastify when -** The app consists of reasonably-sized components/Microservices (i.e., not a huge monolith); for teams who have solid JavaScript & Node.js knowledge; when sticking to Node.js narratives and spirit is desirable

**Prefer Nest.js when** - It's desirable to design and code in OOP style; when the team is highly experienced with Java/Spring/Angular or similar; for large size app that can't be broken down (i.e. monolith) to autonomous component; for a team that lacks fundamental JavaScript/Node.js skills (not exclusively, this yet another consideration); when the decision-making overhead should be minimized; when the time to the first delivery is a critical factor

<br/><br/>


Get lost with express; Nest.js with Fastify, Fastify covers a lot,
25 changes: 20 additions & 5 deletions sections/projectstructre/createlayers.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,27 @@

<br/><br/>

### Separate component code into layers: web, services, and Data Access Layer (DAL)
### Separate component code into 3 layers

![alt text](../../assets/images/structurebycomponents.PNG "Separate component code into layers")
The root of every component should hold 3 folders that represent common concerns and stages of every transaction:

<br/><br/>
```bash
my-system
├─ apps (components)
│ ├─ component-a
│ ├─ entry-points
│ │ ├─ api # controller comes here
│ │ ├─ message-queue # message consumer comes here
│ ├─ domain # features and flows: DTO, services, logic
│ ├─ data-access # DB calls w/o ORM
```

### 1 min explainer: The downside of mixing layers
**- Entry-points -** This is where requests and flows start, whether it's REST API, Graph, message queue, scheduled jobs or any other _door_ to the application. This layer's responsibility is quite minimal - adapt the payload (e.g., JSON) to the app format, including first validation, call the logic/domain layer and return a response. This is typically achieved with a few lines of code. Many use the term "controller" for this type of code also technically, its just an adapter

![alt text](../../assets/images/keepexpressinweb.gif "The downside of mixing layers")
**- Domain -** This is where the app flows, logic and data live. This layer accepts protocol-agnostic payload, plain JavaScript object and returns one as well. Technically it contains common code objects like services, dto/entities, and clients that call external services. It also typically calls the data-access layer to retrieve or persist information

**- Data-access -** This is where the app holds code that interacts with DB. Ideally, it should externalize an interface that returns/gets plain JavaScript object that is DB agnostic (also known as the repository-pattern). This layer involves DB helper utilities like query builders, ORMs, DB drivers and other implementation libraries

**What is the merit? -** When having flexible infrastructure that allows adding more API calls and DB queries promptly, a developer can code a feature faster by focusing on the domain folder. In other words, less time is spent on technical activities and more on activities with added value. This is a ubiquitous trait that is at the heart of most software architectures like DDD, hexagonal, clean-architecture and others. On top of this, when the domain layer is not aware of any edge protocol, it can serve any client and not only HTTP calls

**Why not MVC or clean architecture? -** The 3-tier pattern strikes a great balance between achieving the separation goal while still keeping the structure simple. It also lacks abstractions - each tier represents real-world physical tier where every request will visit. On the other hand, MVC is a simplistic pattern where the letters VC represent a few lines of a code only and the letter M means anything else. Clean architecture is architecture with high level of abstractions that can achieve even greater separation but the price tag is unproportionally higher due to the increased complexity
100 changes: 0 additions & 100 deletions sections/projectstructre/separateexpress.md

This file was deleted.

23 changes: 23 additions & 0 deletions sections/projectstructre/typescript-considerations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Use TypeScript sparingly and thoughtfully

<br/><br/>

### One Paragraph Explainer

TypeScript has won the community's hearts and is almost a standard for modern JavaScript apps. Compared to plain JS, it brings much better coding ergonomics, facilitates editor code completions, even for historical libraries that were written with JavaScript and was proven to [prevent specific type of bugs](https://earlbarr.com/publications/typestudy.pdf). With that, if you look carefully under the hype, TypeScript actually brings two **mutually-exclusive** offerings to the table: type-safety and advanced design constructs like abstract classes, interfaces, namespaces and more. Many teams chose TypeScript for better type safety yet _unintentionally_, without any proper planning, use it for other purposes, such as OOP. These teams change their design style unintentionally due to the ['law of the instruments'](https://en.wikipedia.org/wiki/Law_of_the_instrument) — a cognitive bias that involves using the tooling in hand whether they are the right choice for the mission or not. In other words, if an 'abstract class' exists in the toolbox — developers will use it. If teams consciously opted for the coding techniques mentioned above — that's fair and legit. For others, positively consider coding with classic JavaScript, plain functions and objects, which are simply decorated with primitive types. The latter option is likely to result in lower complexity

<br/><br/>

### Research Quote: "15% less bugs"

From the research [To Type or Not to Type](https://earlbarr.com/publications/typestudy.pdf)

> "our central finding is that both static type systems find an important percentage of public bugs: both Flow 0.30 and TypeScript 2.0 successfully detect 15%!"
<br/><br/>

### Blog Quote: "TypeScript will always miss 80% of bugs"

From the post [The TypeScript tax](https://medium.com/javascript-scene/the-typescript-tax-132ff4cb175b)

> "Some will argue that TypeScript provides realtime bug feedback, so you can catch the bugs earlier, but so do type inference, lint, and testing... You may argue that these other measures have a cost, but because TypeScript will always miss 80% of bugs, you can’t safely skip them either way, so their cost applies to both sides of the ROI math, and is already factored in"
51 changes: 0 additions & 51 deletions sections/testingandquality/citools.md

This file was deleted.

Loading

0 comments on commit 92ad4d9

Please sign in to comment.