You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This document outlines the key principles, patterns, and practices for the Offchain department. It serves as a guide to ensure consistency, scalability, and maintainability in our projects, covering design patterns, architectural principles, TypeScript best practices, testing, scripting, and documentation standards.
@@ -8,21 +7,21 @@ By adhering to these guidelines, we ensure robust and modular development, foste
8
7
## Patterns
9
8
10
9
- Design Patterns:
11
-
-**Factory**: Creates objects dynamically based on input or configuration without exposing the instantiation logic.
12
-
-**Proxy**: Acts as a placeholder to control access, add caching, or optimize performance.
13
-
-**Singleton**: Ensures a class has only one instance and provides a global point of access to it.
10
+
-**Factory**: Creates objects dynamically based on input or configuration without exposing the instantiation logic.
11
+
-**Proxy**: Acts as a placeholder to control access, add caching, or optimize performance.
12
+
-**Singleton**: Ensures a class has only one instance and provides a global point of access to it.
14
13
15
14
## **Principles**
16
15
17
16
- Over-abstraction leads to tighter coupling in the code; avoid it.
18
17
- Favor **composition over inheritance** to build reusable and flexible components.
19
18
- Use **dependency injection** to decouple components and facilitate testing.
20
19
- Follow **SOLID principles**:
21
-
1.**Single Responsibility Principle**: A class should have one and only one reason to change.
22
-
2.**Open/Closed Principle**: Software entities should be open for extension but closed for modification.
23
-
3.**Liskov Substitution Principle**: Derived classes must be substitutable for their base classes.
24
-
4.**Interface Segregation Principle**: Many specific interfaces are better than a single, general-purpose interface.
25
-
5.**Dependency Inversion Principle**: Depend on abstractions, not concretions.
20
+
1.**Single Responsibility Principle**: A class should have one and only one reason to change.
21
+
2.**Open/Closed Principle**: Software entities should be open for extension but closed for modification.
22
+
3.**Liskov Substitution Principle**: Derived classes must be substitutable for their base classes.
23
+
4.**Interface Segregation Principle**: Many specific interfaces are better than a single, general-purpose interface.
24
+
5.**Dependency Inversion Principle**: Depend on abstractions, not concretions.
26
25
27
26
### **Architectural Patterns**
28
27
@@ -35,8 +34,8 @@ By adhering to these guidelines, we ensure robust and modular development, foste
35
34
### **1. Module Structure**
36
35
37
36
- Follow the [Internal Module Pattern](https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de) to avoid circular dependencies:
38
-
- Create an `external.ts` file to explicitly list exported components, types, and interfaces.
39
-
- Import and re-export them in the entry `index.ts` file for easier access.
37
+
- Create an `external.ts` file to explicitly list exported components, types, and interfaces.
38
+
- Import and re-export them in the entry `index.ts` file for easier access.
40
39
41
40
### **2. Asynchronous Initialization**
42
41
@@ -45,15 +44,15 @@ By adhering to these guidelines, we ensure robust and modular development, foste
45
44
### **3. Runtime Type Checking**
46
45
47
46
- Validate environment variables at runtime using libraries like:
48
-
-[Zod](https://zod.dev/)
49
-
-[t3-env](https://github.com/t3-oss/t3-env)
50
-
-[envalid](https://github.com/af/envalid).
47
+
-[Zod](https://zod.dev/)
48
+
-[t3-env](https://github.com/t3-oss/t3-env)
49
+
-[envalid](https://github.com/af/envalid).
51
50
52
51
### **4. Type Safety**
53
52
54
53
- Avoid the use of `any`; prefer `unknown` and use type narrowing to ensure safety.
55
54
- Leverage Branded types pattern for adding clarity, safety and correctness to our code:
@@ -62,9 +61,9 @@ By adhering to these guidelines, we ensure robust and modular development, foste
62
61
A `Service` typically encapsulates a broader business logic or workflow. It might orchestrate various components or interact with multiple data sources or APIs to fulfill a specific domain-related task.
63
62
A `Provider` usually focuses on supplying a specific type of data or resource. It’s often more narrowly scoped, providing access to a particular piece of data, a configuration, or a service needed by other parts of the application.
64
63
65
-
Ex:
64
+
Ex:
66
65
67
-
- Classes that interacts with a metadata source like `Github` ,`JsonFile` &`Ipfs`should implement the interface `IMetadataProvider` and should be called `GithubProvider,JsonFileProvider` &`IpfsProvider`.
66
+
- Classes that interacts with a metadata source like `Github` ,`JsonFile` &`Ipfs` should implement the interface `IMetadataProvider` and should be called `GithubProvider,JsonFileProvider` &`IpfsProvider`.
68
67
- A class that aggregates multiple sources like `Metadata` ,`Pricing` &`BlockchainEvents` , should be called, for example, `AgreggatorService` the word aggregator might change, based on the bussiness logic. Could be, for example, `MetricsService` .
69
68
70
69
If we look at it from composability a `Service` can be made up of `Providers` and the service works on applying the business and orchestration logic
@@ -73,40 +72,40 @@ If we look at it from composability a `Service` can be made up of `Providers` an
73
72
74
73
- Enable the `useUnknownInCatchVariables` flag in your `tsconfig.json`
75
74
- Enable the `noUncheckedIndexedAccess` flag for safe object access
76
-
- Avoid throwing literals like, enforce it with an ESLint rule (https://typescript-eslint.io/rules/no-throw-literal/
75
+
- Avoid throwing literals like, enforce it with an ESLint rule https://eslint.org/docs/latest/rules/no-throw-literal
77
76
- Write custom error classes
78
-
- Use declarative and descriptive names
79
-
- Avoid the usage of suffixes like `Exception` or `Error` :
- Avoid usage of `should` each time you are writing an `it` statement.
85
-
- Ex: Don’t write `it('should run successfully'`), write `it('runs successfully')`.
84
+
- Ex: Don’t write `it('should run successfully'`), write `it('runs successfully')`.
86
85
87
86
### **Key points:**
88
87
89
88
1.**Purpose of Tests**:
90
-
1. Use tests to clearly state the expected behavior for core business scenarios. Well-written tests can often be more clarifying than code comments.
89
+
1. Use tests to clearly state the expected behavior for core business scenarios. Well-written tests can often be more clarifying than code comments.
91
90
2.**Mindset**:
92
-
1. Approach testing with the mindset of, "I've considered how this situation impacts the business and I expect the code to behave as indicated by the test." This confirms that the implemented code functions as intended.
91
+
1. Approach testing with the mindset of, "I've considered how this situation impacts the business and I expect the code to behave as indicated by the test." This confirms that the implemented code functions as intended.
93
92
3.**Efficiency**:
94
-
1. Avoid blind testing, which is time-consuming. Instead, focus on testing the most critical business scenarios.
95
-
2. For example, if the business cares about a scenario where an action as a consequence of one of many sub-actions (Promise all for example) , write a test that expects failure when any operation fails. This approach efficiently communicates the expected behavior.
93
+
1. Avoid blind testing, which is time-consuming. Instead, focus on testing the most critical business scenarios.
94
+
2. For example, if the business cares about a scenario where an action as a consequence of one of many sub-actions (Promise all for example) , write a test that expects failure when any operation fails. This approach efficiently communicates the expected behavior.
96
95
4.**Clarity:**
97
-
1. Aim for a test suite that clearly indicates the intended behavior of the code. Reading the test results should provide a clear understanding of what the code is supposed to do.
96
+
1. Aim for a test suite that clearly indicates the intended behavior of the code. Reading the test results should provide a clear understanding of what the code is supposed to do.
98
97
99
98
## **Scripting**
100
99
101
100
### **Monorepo Projects**
102
101
103
102
- Use `process.cwd()` to reference the root directory within scripts.
104
103
- Organize scripts in the `package.json` file using the format:
105
-
- Infrastructure scripts: `script:infra:{name}`
106
-
- Utility scripts: `script:util:{name}`
104
+
- Infrastructure scripts: `script:infra:{name}`
105
+
- Utility scripts: `script:util:{name}`
107
106
- Create a `scripts` folder with the following structure:
108
-
-`infra/` for infrastructure scripts.
109
-
-`utilities/` for utility scripts.
107
+
-`infra/` for infrastructure scripts.
108
+
-`utilities/` for utility scripts.
110
109
111
110
### **Package Management**
112
111
@@ -119,4 +118,4 @@ If we look at it from composability a `Service` can be made up of `Providers` an
119
118
120
119
## **Discord**
121
120
122
-
- We use Discord for communication—whenever you have a PR ready for review make sure to drop it in the pr-reviews channel!
121
+
- We use Discord for communication—whenever you have a PR ready for review make sure to drop it in the pr-reviews channel!
0 commit comments