プロジェクトコピー機-nestJS
46479 ワード
📣 このシリーズは...
これは優雅な科学技術サマーキャンプの第4期の最後のプロジェクトの完全な再記述ドキュメントシリーズです. これは個人の学習記録に対するドキュメントで、私が書いていないコードが含まれています. ET四大物像-GitHub Repository/リンクの配置現在の配備リンクは、内部の問題のため、APIサーバが動作しません.近いうちに解決する...
NestJS
NestJSフレームワークは、 APIサーバを構成するために使用される. DDDアーキテクチャを構成しようとしたが、ドメイン間の依存性のため、DDDは正常に動作しなかった. package.json
ElasticSearchやTypeorm Nest内部でサポートされているものが多い
設定は私がやったのではありませんが、主な議論の内容はnestの構造と方法を使うことですか?中に入るので、精霊も十分なので、このまま行きましょう.
ディレクトリ構造とドメイン構造
このプロジェクトはドメイン駆動設計モードを採用しているため、各ディレクトリは1つのドメインに分割されます.もちろん完全なDDDではありません.各ドメインは相互に接続されているので...
各ドメインは5~6個のサブディレクトリで構成され、各サブディレクトリは1つ
core.ts
モジュールレコーダを使用してNestjsにするAPPモジュール全体を作成します.
各ドメインのモジュールとMySQL、jwt関連モジュールのインポートとマージ
ログイン関連処理を行うjwtミドルウェアも適用される.
jwt-middleware.ts
Injectable Decoratorを使用してProviderを作成し、NestMiddlewareを実装し、NestJSで使用されるミドルウェアにする
私たちのプロジェクトにはクッキーにaccessTokenが含まれているため、この中間チャネルを通過すると、このトークンの有効性をチェックし、userIdをbodyに入れるか、エラー処理を行います.
その後、コントローラからBody Decoratorのパラメータを使用してuserIdを抽出できます!
main.ts
サーバー・アプリケーションのプライマリ・ファイルを作成します.
corsオプション、フィルタ、各ミドルウェアなど!
decorator
ディレクトリ構造を表示する前に、Decoratorについて簡単に説明します.
Decoratorは,新しい関数を返すことで伝達される関数/メソッド動作を修正する関数である.
詳細については、もう少し深く検討してみましょう.このステップでは、定義する関数/クラスをnestJSフレームワークという形式に変換するだけです.
entity
データベース・テーブル構造を定義します.
typeormモジュールから必要なコールバック関数をインポートします. Entity:@Entity()形式でエンティティークラス宣言セクションに使用します.テーブル名に2つ以上の単語がある場合、通常のDBではSnake case()が使用されます. ですがjavascriptのクラスは主にPaskel caseを使用しています.この2つに合わせるために、以下のようにDBで使用する名前です. は、データ転送オブジェクト(DTO)の事前定義プロセス間でデータを転送するオブジェクトである. は、サーバ上のデータをクライアントに転送するために使用される. あるネットユーザーによると、 DTOの直接転送がなくても大丈夫だが、すぐに設備データに返信できないという.したがって、レプリケーションには方法 が必要である.人にとって、DTOの役割は応答値を決定する形式である.したがってinterface/typeで十分であるが、それ以外にも、DBクエリとしてインポートされたデータをDTO形式に変換するためにofmethodが必要である. したがって、は、以下のような形態を形成する. select文を使用してテーブルの特定のカラムのみをインポートするよりも、テーブル全体をインポートした後、DTOから必要なデータ/を選択して変換し、必要な応答値を生成します. presentation
3階層アーキテクチャでコントローラの役割を果たします.
@Controller Decoratorを使用します.コントローラが使用するパスを入力します.
コンストラクション関数のパラメータに内部で使用されるサービスを渡します.
コントローラクラスのメソッドでは、HTTPメソッドに対応するデータコーディネータを貼り付け、リクエストに一致するパスを入力します!
application
3階層アーキテクチャでサービスの役割を果たす.
InjectableはNest ProviderにするDecoratorです.サービスをプロバイダにする
Providerの核心は依存項目を注入できることです!
コンストラクション関数のパラメータに内部で使用されるドメインを渡します.
domain
3階層アーキテクチャでRepositoryとして機能します.
同様に、ProviderとしてInjectable Decoratorを使用します.
内部で使用されているエンティティRepositoryは、コンストラクション関数のパラメータでInjectRepositorDecoratorを使用して渡されます.
Repositoryは、エンティティに対応するDBにアクセスできるオブジェクトとして扱われます.
を使用したレポート方法は、次のとおりです. find:条件を満たすすべてのローを検索します. findOne:理論的には、条件に対応する最初の行を探します.しかし、条件自体は独特である.idまたは insert:row追加 delete:削除行 update:1番目のパラメータ条件に一致する行を2番目の行に更新します. count:条件付き行数 query:SQL query文 を実行 putObject:S 3は、 をキー構造で記憶する.削除対象:S 3のデータ を削除する. save:転送されたインスタンスを保存します.(新しく追加されたものではありません!)
すべてのドメインにがあるわけではありませんが、S 3やGitHub/GoogleOuth、bcryptなどの外部サービスやDBに直接関連しない要素を含むディレクトリです.
これは
NestJSフレームワークは、
"dependencies": {
"@nestjs/common": "^8.0.0",
"@nestjs/config": "^1.0.1",
"@nestjs/core": "^8.0.0",
"@nestjs/elasticsearch": "^8.0.0",
"@nestjs/jwt": "^8.0.0",
"@nestjs/platform-express": "^8.0.0",
"@nestjs/typeorm": "^8.0.2",
},
"devDependencies": {
"@nestjs/cli": "^8.0.0",
"@nestjs/schematics": "^8.0.0",
"@nestjs/testing": "^8.0.0",
},
ElasticSearchやTypeorm Nest内部でサポートされているものが多い
設定は私がやったのではありませんが、主な議論の内容はnestの構造と方法を使うことですか?中に入るので、精霊も十分なので、このまま行きましょう.
src
|-- cart
| |-- application
| |-- domain
| |-- dto
| |-- entity
| `-- presentation
|-- config
| |-- filter
| `-- properties
|-- destination
| |-- application
| |-- domain
| |-- dto
| |-- entity
| `-- presentation
|-- infra
| `-- mysql
|-- order
| |-- application
| |-- domain
| |-- dto
| |-- entity
| `-- presentation
|-- payment
| `-- presentation
|-- product
| |-- application
| |-- domain
| |-- dto
| |-- entity
| |-- infrastructure
| `-- presentation
`-- user
|-- application
|-- domain
|-- dto
|-- entity
|-- infrastructure
`-- presentation
core-module.ts
jwt-middleware.ts
main.ts
このプロジェクトはドメイン駆動設計モードを採用しているため、各ディレクトリは1つのドメインに分割されます.もちろん完全なDDDではありません.各ドメインは相互に接続されているので...
各ドメインは5~6個のサブディレクトリで構成され、各サブディレクトリは1つ
const jwtConfig = properties.auth;
@Module({
imports: [
MysqlConfig,
JwtModule.register({
secret: jwtConfig.secret,
signOptions: { expiresIn: jwtConfig.expiresIn },
}),
ProductModule,
DestinationModule,
CartModule,
OrderModule,
UserModule,
PaymentModule,
],
})
export class AppModule {
configure(consumer: MiddlewareConsumer): any {
consumer
.apply(LoggerMiddleware)
.exclude("/auth")
.exclude("/auth/*")
.exclude("/users")
.exclude("/users/*")
.forRoutes("*");
}
}
モジュールレコーダを使用してNestjsにするAPPモジュール全体を作成します.
各ドメインのモジュールとMySQL、jwt関連モジュールのインポートとマージ
ログイン関連処理を行うjwtミドルウェアも適用される.
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
constructor(private readonly jwtService: JwtService) {}
use(@Req() req: Request, @Res() res: Response, @Next() next: NextFunction) {
try {
const token = req.cookies[properties.auth.tokenKey];
if (token) {
const result = this.jwtService.verify(token)["userId"];
if (!result) throw Error("token expired");
req.body.userId = this.jwtService.decode(token)["userId"];
}
next();
} catch (e) {
res.clearCookie(properties.auth.tokenKey);
res.status(HttpStatus.PRECONDITION_FAILED);
res.send(messages.failed.EXPIRED_TOKEN);
}
}
}
Injectable Decoratorを使用してProviderを作成し、NestMiddlewareを実装し、NestJSで使用されるミドルウェアにする
私たちのプロジェクトにはクッキーにaccessTokenが含まれているため、この中間チャネルを通過すると、このトークンの有効性をチェックし、userIdをbodyに入れるか、エラー処理を行います.
その後、コントローラからBody Decoratorのパラメータを使用してuserIdを抽出できます!
const serverPort = properties.server.port;
const nestApplication = async () => {
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: [properties.client],
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
preflightContinue: false,
optionsSuccessStatus: 204,
credentials: true,
});
app.useGlobalFilters(new HttpExceptionFilter());
app.use(cookieParser());
await app.listen(serverPort);
};
nestApplication();
サーバー・アプリケーションのプライマリ・ファイルを作成します.
corsオプション、フィルタ、各ミドルウェアなど!
ディレクトリ構造を表示する前に、Decoratorについて簡単に説明します.
Decoratorは,新しい関数を返すことで伝達される関数/メソッド動作を修正する関数である.
@
接頭辞とともに使用し、飾りたいclass、関数などに適用してnestJSで使用する形にすることができます.詳細については、もう少し深く検討してみましょう.このステップでは、定義する関数/クラスをnestJSフレームワークという形式に変換するだけです.
データベース・テーブル構造を定義します.
typeormモジュールから必要なコールバック関数をインポートします.
@Entity(product_option)
class ProductOption
-P r i maryGeneratedColumn:intタイプのPKが自動的に生成されます.@PrimaryGeneratedColumn()
id: number;
-primaryColumn:PKバー.@PrimaryColumn({ type: "char", length: 32 })
id: string;
M-anyToOne:1:n関係におけるn個の表のうち1個の表のコラムニスト@ManyToOne(() => User, (user) => user.wishes, { lazy: true })
-oneToMany:1:n関係の1つのテーブルのnテーブルのコラムニスト@OneToMany(() => Wish, (wish) => wish.user)
wishes: Wish[];
-oneToOne:1:1関係@OneToOne(() => Review, (review) => review.order)
review: Review;
-JoinColumn:コラム名、参照コラム名**をマージする**コラムを作成します.私の知る限り、外部キー関係ではManyToOneToManyとともに使用されます.@ManyToOne(() => Product, (product) => product.images, {
nullable: false,
onDelete: "CASCADE",
})
@JoinColumn({ name: "product_id" })
product: Product;
-Column:通常の列を作成します.@Column({ type: "char", length: 32, nullable: true })
image: string;
-CReateDateColumn:角度の生成、更新時間などの時間列の自動生成に使用します.@CreateDateColumn({
type: "timestamp",
name: "created_at",
default: () => "CURRENT_TIMESTAMP(6)",
})
createdAt: Date;
dtoexport class ReviewResponse {
averageRate: number;
rates: ReviewRate[];
reviews: ReviewDTO[];
static of(productReviews: Review[]): ReviewResponse {
const RATES: ReviewRate[] = [
{ rate: 1, count: 0 },
{ rate: 2, count: 0 },
{ rate: 3, count: 0 },
{ rate: 4, count: 0 },
{ rate: 5, count: 0 },
];
const averageRate = Number(
(
productReviews.reduce((result, review) => {
return result + review.rate;
}, 0) / productReviews.length
).toFixed(1)
);
const rates = productReviews.reduce(
(result: ReviewRate[], review): ReviewRate[] => {
result[review.rate - 1].count++;
return result;
},
RATES
);
const reviews: ReviewDTO[] = productReviews.map((review): ReviewDTO => {
return {
id: review.id,
rate: review.rate,
content: review.content,
image: review.image,
authorName: review.order.user.name,
createdAt: review.createdAt,
};
});
return {
averageRate,
rates,
reviews,
};
}
}
@Controller("/my")
export class MyController {
constructor(private readonly myService: MyService) {}
@Get("/info")
async getMyInfo(@Body("userId") userId: number): Promise<MyInfoResponse> {
return await this.myService.getMyInfo(userId);
}
...
}
3階層アーキテクチャでコントローラの役割を果たします.
@Controller Decoratorを使用します.コントローラが使用するパスを入力します.
コンストラクション関数のパラメータに内部で使用されるサービスを渡します.
コントローラクラスのメソッドでは、HTTPメソッドに対応するデータコーディネータを貼り付け、リクエストに一致するパスを入力します!
@Injectable()
export class MyService {
constructor(
private readonly carts: Carts,
private readonly reviews: Reviews,
private readonly destinations: Destinations,
private readonly questions: Questions,
private readonly users: Users,
private readonly orders: Orders,
private readonly wishes: Wishes
) {}
async getMyInfo(userId) {
try {
const user = await this.users.findUserById(userId);
return MyInfoResponse.of(user);
} catch (e) {
throw new ETException(400, messages.failed.FAILTED_TO_FIND_MY_INFO);
}
}
...
3階層アーキテクチャでサービスの役割を果たす.
InjectableはNest ProviderにするDecoratorです.サービスをプロバイダにする
Providerの核心は依存項目を注入できることです!
コンストラクション関数のパラメータに内部で使用されるドメインを渡します.
@Injectable()
export class Wishes {
constructor(
@InjectRepository(Wish)
private readonly wishRepository: Repository<Wish>,
@InjectRepository(Product)
private readonly productRepository: Repository<Product>
) {}
...
3階層アーキテクチャでRepositoryとして機能します.
同様に、ProviderとしてInjectable Decoratorを使用します.
内部で使用されているエンティティRepositoryは、コンストラクション関数のパラメータでInjectRepositorDecoratorを使用して渡されます.
Repositoryは、エンティティに対応するDBにアクセスできるオブジェクトとして扱われます.
async findMyWishesByUserId(userId: number): Promise<Wish[]> {
return await this.wishRepository.find({
relations: ["product", "product.images", "user"],
where: { user_id: userId },
});
}
Repositoty.find({ where, relations);
Repositoty.findOne(where, relations);
Repositoty.findOne({ id });
Repositoty.insert(newRow);
Repositoty.delete({ id });
Repositoty.update({ id }, newRow);
Repositoty.count({ id }, newRow);
Repositoty.query(query);
s3Repository.putObject(key, value);
s3Repository.deleteObject(id);
this.productRepository.findOne(wish.productId).then((product) => {
if (product.wishCount > 0) {
product.wishCount++;
}
this.productRepository.save(product);
});
infrastructureすべてのドメインに
Reference
この問題について(プロジェクトコピー機-nestJS), 我々は、より多くの情報をここで見つけました https://velog.io/@jjunyjjuny/ET네-만물상-프로젝트-복기-NestJSテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol