последовательность
С момента разработки iCode 3.0 прошел почти целый год, хотя время, которое я инвестировал в него, составляло всего 4 месяца, когда я был единственным, кто инвестировал в бэкэнд в начале, можно сказать, что я провел некоторое исследование Вещи, пробираясь по мутной воде два-три раза и меняя структуру пять-шесть раз туда-сюда, я провел в душе много времени. Однако это уже не важно, после того, как материя ушла, сила (синь) и имя (кислый) глубоко скрыты. Оглядываясь назад, я просто кратко записал некоторые исследования того времени, и Куан должен поставить точку в этом опыте. . .
перерыв
возлюбленная детства
AiCode — это приложение Node.В экономике Али того времени, когда дело доходит до структуры приложения Node, можно сказать, что Egg.js известен всем. Будучи важным продуктом с открытым исходным кодом, зарекомендовавшим себя Ali, в последние годы он также занял лидирующие позиции в группе. Поэтому Egg.js, конечно, наш первый выбор. И я с этим раньше боролся в Turing Project и UTT, а теперь снова встречаю, должно быть очень знакомо, и на три и на пять делений можно установить целый каркас. Так что я сделал то, что сказал, и сразу скомпилировал каркас кода для первого отчета по спецификации Egg.js.
Распоряжение надзирателя, слова свахи
В первом отчете супервайзер, естественно, хотел сначала продвигаться, поэтому под командованием супервайзера я подытожил два пункта для улучшения и понял, чего в итоге хочет супервайзер: стандартизированная, но хорошо масштабируемая структура обслуживания. Заключительные мысли в сторону, давайте посмотрим, каковы эти две болевые точки.
Во-первых, Egg.js — это фреймворк, в котором соглашение важнее конфигурации.
Яйцо преследует»Соглашение о конфигурации",согласно сединый набор соглашенийПри разработке приложений использование этого метода внутри команды может снизить затраты на обучение разработчиков, а разработчики больше не являются «гвоздями» и могут работать в потоке.
Из-за этого существует ограничение на спецификацию каталога в Egg.js.Структура каталогов базового проекта Egg.js выглядит следующим образом:
egg-project
├── package.json
├── app.js (可选)
├── agent.js (可选)
├── app
| ├── router.js
│ ├── controller
│ | └── home.js
│ ├── service (可选)
│ | └── user.js
│ ├── middleware (可选)
│ | └── response_time.js
│ ├── schedule (可选)
│ | └── my_task.js
│ ├── public (可选)
│ | └── reset.css
│ ├── view (可选)
│ | └── home.tpl
│ └── extend (可选)
│ ├── helper.js (可选)
│ ├── request.js (可选)
│ ├── response.js (可选)
│ ├── context.js (可选)
│ ├── application.js (可选)
│ └── agent.js (可选)
├── config
| ├── plugin.js
| ├── config.default.js
│ ├── config.prod.js
| ├── config.test.js (可选)
| ├── config.local.js (可选)
| └── config.unittest.js (可选)
└── test
├── middleware
| └── response_time.test.js
└── controller
└── home.test.js
Как видите, в нашем приложении каталога кодов все файлы кодов классифицируются в соответствии с их функциями.Например, все коды контроллеров будут размещены в одном каталоге, а все сервисные коды также будут размещены в каталоге сервисов. Согласитесь, это разумная классификация. Однако иногда для некоторых команд разработчиков, когда модулей много, необходимо во время разработки переключать файлы, разбросанные по разным директориям, туда-сюда, что приносит неудобства в разработке, а разброс кода одного и того же модуля также приведет к чтение проектов препятствие. Итак, можем ли мы заставить Egg.js поддерживать помодульную структуру каталогов, как показано ниже?
egg-project
├── package.json
├── app.js (可选)
├── agent.js (可选)
├── src
│ ├── router.js
│ ├── home
│ │ ├── home.controller.ts
│ │ ├── home.service.ts
│ │ └── home.tpl
│ └── user
│ ├── user.controller.ts
│ └── user.service.ts
├── config
| ├── plugin.js
| ├── config.default.js
│ ├── config.prod.js
| ├── config.test.js (可选)
| ├── config.local.js (可选)
| └── config.unittest.js (可选)
---
После некоторого изучения документации Egg.js и исходного кода egg-core я обнаружил, что он предоставляет способ расширения Loader для настройки поведения загрузки каталогов, но из-за следующих ограничений, если мы хотим настроить загрузчик, мы должны Создайте новый фреймворк на основе Egg, а затем разработайте на основе этого фреймворка.
Яйцо реализовано на базе LoaderAppWorkerLoader а также AgentWorkerLoader, верхняя структура расширяется на основе этих двух классов,Расширение загрузчика можно сделать только во фреймворке.
Итак, что нам нужно сделать примерно:
- использовать
npm init egg --type=framework
построить основу - существует
lib/loader
напиши свой загрузчик - Указываем каркас яйца в качестве каркаса в нашем проекте
'use strict';
const fs = require('fs');
const path = require('path');
const egg = require('egg');
const extend = require('extend2');
class AiMakeAppWorkerLoader extends egg.AppWorkerLoader {
constructor(opt) {
super(opt);
this.opt = opt;
}
loadControllers() {
super.loadController({
directory: [ path.join(this.opt.baseDir, 'src/') ],
match: '**/*.controller.(js|ts)',
caseStyle: filepath => {
return customCamelize(filepath, '.controller');
},
});
}
loadServices() {
super.loadService({
directory: [ path.join(this.opt.baseDir, 'src/') ],
match: '**/*.service.(js|ts)',
caseStyle: filepath => {
return customCamelize(filepath, '.service');
},
});
}
load() {
this.loadApplicationExtend();
this.loadRequestExtend();
this.loadResponseExtend();
this.loadContextExtend();
this.loadHelperExtend();
this.loadCustomLoader();
// app > plugin
this.loadCustomApp();
// app > plugin
this.loadServices();
// app > plugin > core
this.loadMiddleware();
// app
this.loadControllers();
// app
this.loadRouter(); // Dependent on controllers
}
}
//...略过工具函数代码
module.exports = AiMakeAppWorkerLoader;
На данный момент мы преодолели первую болевую точку. Второй момент заключается в том, что Egg.js — это фреймворк, разработанный на основе JavaScript, но теперь, когда пришло время 2019 года, TypeScript, как надмножество JavaScript, может принести нам различные преимущества сильной системы типов и обеспечить более полную реализацию. объектно-ориентированного программирования. Нет никаких причин, по которым мы не должны выбирать TypeScript при разработке общей инфраструктуры обслуживания. Однако в Egg.js изначально нет поддержки TypeScript, возможно, на это есть исторические причины, но для нас это неприемлемо. Итак, после некоторых поисков, согласноЭта проблемаЯ снова нашел способ использовать TypeScript в Egg.js. Конкретные шаги уже подробно описаны по ссылке.На самом деле есть два основных момента:
- При инициализации проекта яйца добавьте
--type=ts
параметр - Используйте **egg-ts-helper** для автоматической генерации файлов d.ts во время разработки.
Таким образом, вы можете использовать TypeScript для более удобного написания кода Egg.js.
Наконец, две болевые точки были в основном решены мной, так что я был счастлив и побежал ко второму отчету.
шпилька феникс
Второй отчет был не таким простым, и руководитель подверг сокрушительной критике глубину моего мышления. Это заставило меня осознать, что, хотя метод пользовательского загрузчика может решить мои поверхностные проблемы, фундаментальные ограничения не исчезли, и этот метод негибок, и пользователи не могут адаптировать наш сервисный фреймворк к своему. .js, чтобы избавиться от привычки систематизировать файлы. Кроме того, в Egg.js изначально отключена поддержка TypeScript, даже если для написания ts-кода используется egg-ts-helper, поддержка различных сторонних библиотек не контролируется, и пользователям все равно приходится сильно рисковать.
Другого пути нет, Egg.js, лучше забыть друг друга в реках и озерах.
Зал полон красивых женщин, и вдруг Ду и Ю Си становятся неразлучны.
После "расставания" искал подходящие фреймворки везде на гитхабе.Хотя и нашел несколько запасных покрышек, но та, что всегда попадалась на глаза, никогда не попадалась на глаза. Когда я был взволнован и запутан, маленький партнер пекинской команды, который вместе обсуждал это, упомянул об этом, NestJS. После того, как я внимательно изучил его домашнюю страницу на github, я сразу почувствовал себя авторизованным. Ну правильно, так и есть!
Теперь, когда у нас есть новая любовь, мы должны представить ее всем.Давайте сначала послушаем ее самоотчет:
Nest — это инструмент для создания эффективных, масштабируемыхNode.jsФреймворк для серверных приложений. Он использует прогрессивный JavaScript, встроенный и полностью поддерживаемыйTypeScript(но все еще позволяет разработчикам кодировать на чистом JavaScript) и сочетает в себе элементы ООП (объектно-ориентированное программирование), FP (функциональное программирование) и FRP (функционально-реактивное программирование). Под капотом Nest использует мощные платформы HTTP-серверов, такие как Express (по умолчанию) и Fastify. Nest обеспечивает уровень абстракции поверх этих фреймворков, а также предоставляет свой API напрямую разработчикам. Это упрощает использование бесчисленных сторонних модулей для каждой платформы.
Обратите внимание, что это фреймворк, изначально поддерживающий TypeScript, а это значит, что NestJS и все плагины в его экосистеме должны быть TypeScript, что сразу решает мою вторую проблему. Есть ли решение первого вопроса? Не волнуйтесь, позвольте мне рассказать вам медленно.
При первом взгляде на NestJS мы все можем почувствовать, что это очень прямолинейно, и это нормально.Для тех из нас, кто использует стек технологий Vue и React, образ мышления NestJS действительно не так прост для понимания. Но если вы сталкивались с AngularJS, он может показаться вам немного знакомым. Если вы были бэкенд-разработчиком и умеете пользоваться Java и Spring, вы можете вскочить и закричать: Разве это не загрузка Spring!
Ваша интуиция верна. NestJS похож на AngularJS и Spring. Оба являются фреймворками, разработанными на основе принципа инверсии управления (IoC = инверсия управления), и оба используют DI = внедрение зависимостей для решения проблемы сцепления.
Что такое внедрение зависимостей? В качестве простого примера предположим, что у нас есть класс Car и класс Engine, мы организуем код следующим образом:
// 引擎
export class Engine {
public cylinders = '引擎发动机1';
}
export class Car {
public engine: Engine;
public description = 'No DI';
constructor() {
this.engine = new Engine();
}
drive() {
return `${this.description} car with ` +
`${this.engine.cylinders} cylinders`;
}
}
// 示例参考 https://juejin.cn/post/6844903740953067534
В этот момент наш движок инициализируется в экземпляре Car. Итак, если однажды движок будет обновлен, в конструктор будет добавлен новый параметр:
// 引擎
export class Engine {
public cylinders = '';
constructor(_cylinders:string) {
this.cylinders = _cylinders;
}
}
Затем автомобиль, использующий двигатель, должен изменить код конструктора в классе Car, чтобы адаптироваться к изменениям двигателя. Это неразумно, потому что детали реализации двигателя не должны касаться автомобиля. На данный момент мы говорим, что класс Car зависит от Engine.
Затем, если мы используем внедрение зависимостей для реализации класса Car:
export class Engine {
public cylinders = '引擎发动机1';
}
export class Car {
public description = 'DI';
// 通过构造函数注入Engine和Tires
constructor(public engine: Engine) {}
drive() {
return `${this.description} car with ` +
`${this.engine.cylinders} cylinders`;
}
}
На этом этапе класс Car больше не создает сам Engine, а только получает и использует экземпляр Engine. Экземпляр Engine внедряется через конструктор при создании экземпляра класса Car. Таким образом, класс Car и класс Engine не связаны. Если мы хотим обновить класс Engine, нам нужно только внести изменения в оператор создания экземпляра Car.
export class Engine {
public cylinders = '';
constructor(_cylinders:string) {
this.cylinders = _cylinders;
}
}
export class Car {
public description = 'DI';
// 通过构造函数注入Engine和Tires
constructor(public engine: Engine) {}
drive() {
return `${this.description} car with ` +
`${this.engine.cylinders} cylinders`;
}
}
main(){
const car = new Car(new Engine('引擎启动机2'), new Tires1());
car.drive();
}
Это внедрение зависимостей.
Конечно, это всего лишь самый простой пример, на практике процесс создания экземпляра класса в NestJS делегируется IoC-контейнеру (то есть системе выполнения NestJS). Нам не нужно вводить вручную каждый раз.
Итак, после всего сказанного, имеет ли какое-либо отношение внедрение зависимостей к нашему первому вопросу? Конечно, есть! Мы знаем, почему Egg.js нужно указывать структуру каталогов, потому что в коде загрузчика egg-core загрузка Controller, Service, Config и т. д. выполняется различными функциями загрузки, ищущими указанный каталог. Так что если они не найдены в указанном месте, то Egg.js не может их получить и смонтировать под ctx. NestJS другой, зависимости прописаны в контейнере сами, то есть NestJS не нужно искать зависимости по указанному местоположению. Нам нужно только внедрить в модуль контроллер, сервис и т. д., которые необходимо выполнить, и модуль сможет их получить и использовать.
// app.controller.ts
import { Controller, Get, Inject } from '@nestjs/common';
@Controller()
export class AppController {
@Inject('appService')
private readonly appService;
@Get()
getHello(): string {
return this.appService.getHello();
}
}
// app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app/app.controller';
import { AppService } from './app/app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [{
provide: 'appService',
useClass: AppService,
}],
})
export class AppModule {}
Как и выше, мы видим, что Служба, украшенная @Injectable, может использоваться напрямую при использовании в app.controller.ts после регистрации.@Inject('appService')
для внедрения экземпляра службы в свойство. На данный момент нам не нужно заботиться о том, где находится app.service.ts при его использовании, каталог можно организовать произвольно, и единственное требование — завершить регистрацию в контейнере. Благодаря внедрению зависимостей мы можем гибко вводить конфигурацию во время разработки, а из-за отделения зависимостей тестируемость также выше.
Конечно, преимущества NestJS заключаются не только в этих двух моментах.В качестве основы для бенчмаркинга Java Spring на стороне узла его концепция дизайна и ограничения разработки могут очень помочь в разработке крупномасштабных проектов. Более того, он, естественно, поддерживает микросервисы, а последующее расширение будет удобнее для масштабных проектов. Сочетая вышеперечисленные преимущества, мы окончательно решили выбрать NestJS.
Huangtian окупается На этот раз у наблюдателя не было палки, чтобы бить мандаринок, и, наконец, он завершил этот путь отбора.
срочный
Внезапно оглянувшись, мужчина оказался в тусклом свете
Прошло много времени, и битва между Egg.js и NestJS уже подошла к концу. Айкод также разрабатывается полным ходом уже более полугода. Однажды вечером я получил электронное письмо от Midway, Egg.js наконец-то выполнил свою историческую миссию, эстафету принял Midway и стал эталоном фреймворка внутри группы. Вспоминая, что когда я занимался исследованиями в то время, я также видел Мидуэй и специально спрашивал совета у ответственного бога. Конечно, в конце концов, поскольку я был не очень хорошо с ним знаком и чувствовал, что Egg.js был опорой в группе, у меня не было выбора. Теперь, когда нет бремени выбора, буду изучать текущий Мидуэй в свободное время. Это действительно структура, которая идет в ногу со временем.
Midway, изначально поддерживающий TypeScript, больше не нужно критиковать, как Egg.js. И он совместим со многими плагинами Egg.js, что также упрощает его использование при разработке различных сценариев в группе. Основанный на дизайне DI, он также возрождается в архитектуре. Что еще более радикально, так это то, что Midway использует механизм автоматического сканирования зависимостей, и даже этап ручной регистрации зависимостей может быть опущен.По сравнению с NestJS, это действительно для меня неожиданность.
Midway использует механизм автоматического сканирования, перед инициализацией приложения будут проверены все файлы, а также файлы, содержащие декораторы.автоматическая привязкак контейнеру.
Если использовать Midway, некоторые из наших болевых точек в то время могут быть решены, и код значительно упростится. В этот момент я не мог не думать об этом. Однако, поскольку история заставила меня выбрать NestJS, давайте начнем с самого начала.
// app/controller/user.ts
import { Context, controller, get, inject, provide } from '@ali/midway';
@provide()
@controller('/user')
export class UserController {
@inject()
ctx: Context;
@inject('userService')
service;
@get('/:id')
async getUser(): Promise<void> {
const id: number = this.ctx.params.id;
const user = await this.service.getUser({id});
this.ctx.body = {success: true, message: 'OK', data: user};
}
}
// service/user.ts
import { provide } from '@ali/midway';
import { IUserService, IUserOptions, IUserResult } from '../interface';
@provide('userService')
export class UserService implements IUserService {
async getUser(options: IUserOptions): Promise<IUserResult> {
return {
id: options.id,
username: 'mockedName',
phone: '12345678901',
email: 'xxx.xxx@xxx.com',
};
}
}
Улинг люди
С момента разработки прошел почти год, front-end разработка меняется с каждым днем, и в выборе технологии нет абсолютно правильного или неправильного. Я отсутствовал в Node больше полугода, и я давно знаю Вея и Джина. Запись — это всего лишь запись, то, что я пишу вам, — это история, а то, что я пишу себе, — это мысль. Облако слов автора: Недостаточно для посторонних
Статья может быть воспроизведена по желанию, но ссылка на оригинал должна быть сохранена.
Добро пожаловать!ES2049 Studio, пожалуйста, отправьте свое резюме наcaijun.hcj@alibaba-inc.com.