Czysty kod za pomocą Node.js
Jedną z najważniejszych części pisania czystego kodu są praktyki, których przestrzegamy podczas konstruowania kodu. Oczywiście łatwiej jest przestrzegać zasad czystego kodu w JAVA, ponieważ jest ona zorientowana obiektowo, ale w przypadku JavaScript nie ma jasnych instrukcji jako takich. Elastyczność, jaką oferuje JavaScript, jest zarówno dobrodziejstwem, jak i zmorą samą w sobie. W tym artykule omówimy sposoby pisania monolitycznej aplikacji node js, zainspirowanej czystym kodem Uncle Boba .
Co to jest czysty kod?
Podstawą architektury czystego kodu jest rozdzielenie problemów, innymi słowy podzielenie naszej aplikacji na warstwy, tak aby każda warstwa była niezależna od drugiej.
Korzyści są następujące.
- Niezależność od wszelkich bazowych ram.
- Swoboda podłączania i grania z dowolną bazą danych.
- Popraw testowalność kodu.
- Pomaga w łatwym utrzymaniu większej bazy kodu.
Nadrzędną regułą, która sprawia, że ta architektura działa, jest reguła zależności. Ta reguła mówi, że zależności kodu źródłowego mogą wskazywać tylko do wewnątrz. Nic w kręgu wewnętrznym nie może nic wiedzieć o czymś w kręgu zewnętrznym.
Teraz przedmiotem przesłuchania jest sposób komunikacji między warstwami, ponieważ są one od siebie odizolowane, i jaki ma to wpływ na działanie aplikacji. Można to osiągnąć za pomocą czegoś, co nazywa się wstrzykiwaniem zależności. Zapoznaj się z poniższym przykładem, aby uzyskać jasne zrozumienie.
interface IEngine {
start: () => void
stop: () => void
...
}
class PowerfulEngine implements IEngine {
start() {}
stop() {}
// ...
}
class MorePowerfulEngine implements IEngine {
start() {}
stop() {}
// ...
}
class CarBody {
constructor(private engine: IEngine) {}
// ...
}
const car1 = CarBody(new PowerfulEngine())
const car2 = CarBody(new MorePowerfulEngine())
Ponieważ jest to rozległy i szerszy temat, w który należy się zagłębić, proszę o przeczytanie tego artykułu , aby uzyskać lepszy wgląd w warstwy.
Strukturyzacja naszej aplikacji Node.JS
W tym segmencie omówimy, w jaki sposób aplikacje węzłowe mogą być zorganizowane przy użyciu zasad czystego kodu, z pomocą aplikacji Todo ( github repo link ).
Aby to ułatwić, przejdźmy przez każdą pojedynczą warstwę z modułu aplikacji, aby zapoznać się z podstawowymi pojęciami.
Do stworzenia aplikacji będziemy używać MongoDB, Node, TypeScript, express i inversify .
Co to jest inwersja?
Za pomocą inversify możesz zautomatyzować wstrzykiwanie zależności, zamiast za każdym razem ręcznie przekazywać zależność, możesz utworzyć kontener, a za pomocą dekoratorów TS możesz łatwo przekazać zależność do swoich obiektów, oto przykład przekazywania zależności z inwersją i bez niej
z inwersją
interface ITodoService {...}
@injectable()
class TodoService implements ITodoService {
@inject(Types.Todo_REPOSITORY)
private todoRepository: ITodoRepository;
...
}
interface ITodoRepository {...}
@injectable()
class TodoRepository implements ITodoRepository {
@inject(Types.ToDo_TABLE)
private todoTable: TodoAppDataSource<ITodoModel>;
...
}
export interface ITodoController {...}
@injectable()
class TodoController implements ITodoController {
@inject(Types.Todo_SERVICE)
private todoService: ITodoService;
...
}
// using todo controller
const todoController = dIContainer.get<ITodoController>(Types.Todo_CONTROLLER);
interface ITodoService {...}
class TodoService implements ITodoService {
constructor(private todoRepository: ITodoRepository) {...}
...
}
interface ITodoRepository {...}
class TodoRepository implements ITodoRepository {
constructor(private todoTable: TodoAppDataSource<ITodoModel>) {...}
...
}
export interface ITodoController {...}
class TodoController implements ITodoController {
constructor(private todoService: ITodoService) {...}
...
}
// using todo controller
const todoController = new TodoController(new TodoService(new TodoRepository()))
Warstwy aplikacji
Nasza aplikacja jest podzielona na 5 warstw, oto szczegółowe wyjaśnienie każdej warstwy.
Warstwa 1 (trasy)
Trasy to najbardziej wewnętrzna warstwa, ponieważ bardzo rzadko zdarza się, że zmienisz nazwę trasy, aby stała się rdzeniem aplikacji.
export default function TodoRoutes() {
const router = express.Router();
const todoController = dIContainer.get<ITodoController>(
Types.Todo_CONTROLLER
);
router
.route("/")
.post(todoController.createNewTodo)
.get(todoController.getAllTodo)
.patch(todoController.updateTodo)
.delete(todoController.deleteTodo);
return router;
}
Warstwa 2 (kontroler)
Administrator odpowiada za
- Obsługa żądania przychodzącego.
- Weryfikacja treści żądania.
- Wysyłanie odpowiedzi do klienta.
@injectable()
export class TodoController implements ITodoController {
@inject(Types.Todo_SERVICE)
private todoService: ITodoService;
public createNewTodo = async (req: AuthenticatedRequest, res: Response) => {
...
};
public getAllTodo = async (
req: AuthenticatedRequest,
res: Response
): Promise<Response> => {
...
};
public updateTodo = async (req: AuthenticatedRequest, res: Response) => {
...
};
public deleteTodo = async (req: AuthenticatedRequest, res: Response) => {
...
};
}
Service to nasza trzecia warstwa odpowiedzialna za obsługę naszej logiki biznesowej, jest zależna od TodoRepository i każdą klasę spełniającą interfejs ITodoRepository można wstrzyknąć do TodoService.
Warstwa 4 (Repozytorium)
Pod numerem 4 mamy repozytorium, które jest odpowiedzialne za wszelkiego rodzaju połączenia ze światem zewnętrznym, niezależnie od tego, czy jest to wywołanie API usługi zewnętrznej, czy rozmowa z bazą danych, wszystkie takie rzeczy są obsługiwane w tej warstwie.
@injectable()
class TodoRepository implements ITodoRepository {
@inject(Types.ToDo_TABLE)
private todoTable: TodoAppDataSource<ITodoModel>;
private getParameterObj = (
content: string,
userId: string
): Omit<ITodoModel, "_id"> => ({
...
});
createNewEntry = async (
content: string,
userId: string
): Promise<ITodoModel> => { ... };
getAllUserTodo = async (userId: string) => { ... };
deleteTodo = async (
userId: string,
todoId: string
): Promise<ITodoModel> =>{ ... };
updateTodoDetails = async (
userId: string,
todoId: string,
todoDetails: Partial<ITodoModel>
): Promise<ITodoModel> => { ... };
}
Warstwa 5 (baza danych)
Pod numerem 5 zachowaliśmy naszą warstwę danych, zawiera ona wszystkie nasze zapytania DB i udostępnia je za pomocą jednego interfejsu, istnieje jedna klasa podstawowa dla każdego typu DB, patrz przykład pokazany poniżej dla mongoDB i postgresql.
Wspólny interfejs
export interface TodoAppDataSource<T> {
create(data: T): Promise<T>;
findOne(filter: Partial<T>, project?: Projection): Promise<T>;
findMany(filter: Partial<T>, project?: Projection): Promise<T[]>;
findOneAndUpdate(filter: Partial<T>, updates: Partial<T>): Promise<T>;
}
export class MongoDataSource<T> implements TodoAppDataSource<T> {
private table: mongoose.Model<T>;
constructor(tableName: DB_TABLES) {
this.table = ALL_TABLES[tableName] as mongoose.Model<T>;
}
public async findOne<T>(
selectQuery: Partial<T>,
project: Projection = {}
): Promise<T> {
return this.table.findOne(selectQuery as FilterQuery<T>, project);
}
public async create<T>(data: T): Promise<T> {
const newRecord = new this.table(data);
return newRecord.save() as Promise<T>;
}
public async findOneAndUpdate<T>(
selectQuery: Partial<T>,
updates: Partial<T>
): Promise<T> {
return this.table.findOneAndUpdate(selectQuery as FilterQuery<T>, updates, {
new: true,
});
}
public findMany = async (
filter: Partial<T>,
project?: Projection
): Promise<Array<T>> => {
const result = await this.table.find(filter as FilterQuery<T>, project);
return result as unknown as Promise<Array<T>>;
};
}
export class UserTable extends MongoDataSource<IUserModel> {
constructor() {
super(DB_TABLES.USER);
}
}
Wiem, że może to być przytłaczające dla czytelników, którzy czytają po raz pierwszy, ale zaufaj mi, gdy twoje repozytorium kodu zostanie skonfigurowane, skalowanie kodu będzie bardzo łatwe. Proszę, abyś przejrzał kod tutaj , zaczynając od main.ts.
Wniosek
Badania to sformalizowana ciekawość, to szturchanie i wścibstwo w określonym celu. Nie ma twardej i szybkiej zasady tworzenia lub wydajnej struktury repozytorium kodu. Dlatego eksploracja, badanie i eksperymentowanie to najlepszy sugerowany sposób.
Zwięzły sposób pisania kodu i struktury jest jedynym powodem tego artykułu. Wszelkie sugestie i poprawki do tego artykułu są mile widziane. Wrócę z kolejnym ciekawym artykułem. Do tego czasu szczęśliwego kodowania! ❤
Więcej treści na PlainEnglish.io .
Zapisz się do naszego bezpłatnego cotygodniowego biuletynu . Śledź nas na Twitterze , LinkedIn , YouTube i Discordzie .
Zainteresowany skalowaniem startupu oprogramowania ? Sprawdź obwód .

![Czym w ogóle jest lista połączona? [Część 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































