droplinked-backend
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDroplinked Backend Development Guide
Droplinked后端开发指南
Architecture Overview
架构概述
Strict layered architecture with clear separation of concerns:
Controller -> Service Facade -> UseCase -> Repository| Layer | Role | Rules |
|---|---|---|
| Controller | HTTP only | Transform params, call service. No logic. Use |
| Service Facade | Thin orchestration | Get UseCase via |
| UseCase | All business logic | Extend |
| Repository | Data access only | Extend |
具有清晰关注点分离的严格分层架构:
Controller -> Service Facade -> UseCase -> Repository| 层级 | 职责 | 规则 |
|---|---|---|
| Controller | 仅处理HTTP | 转换参数,调用服务。无业务逻辑。使用 |
| Service Facade | 轻量编排 | 通过 |
| UseCase | 所有业务逻辑 | 继承 |
| Repository | 仅数据访问 | 继承 |
Module Structure
模块结构
All file names in :
kebab-casesrc/modules/<module-name>/
├── controllers/ # HTTP Controllers
│ └── <module>.controller.ts
├── services/ # Service Facades
│ └── <module>.service.ts
├── use-cases/ # Business Logic (one file per operation)
│ ├── create-order.use-case.ts
│ └── get-order.use-case.ts
├── repositories/ # Data Access Layer
│ └── <entity>.repository.ts
├── dtos/ # Validation with class-validator
│ ├── create-order.dto.ts
│ └── order-response.dto.ts
├── events/ # EventEmitter definitions
│ └── order-created.event.ts
├── listeners/ # @OnEvent handlers
│ └── order.listener.ts
├── <module>.module.ts
└── docs.md # UseCase documentation所有文件名采用命名:
kebab-casesrc/modules/<module-name>/
├── controllers/ # HTTP控制器
│ └── <module>.controller.ts
├── services/ # Service Facade
│ └── <module>.service.ts
├── use-cases/ # 业务逻辑(每个操作对应一个文件)
│ ├── create-order.use-case.ts
│ └── get-order.use-case.ts
├── repositories/ # 数据访问层
│ └── <entity>.repository.ts
├── dtos/ # 使用class-validator做验证
│ ├── create-order.dto.ts
│ └── order-response.dto.ts
├── events/ # EventEmitter定义
│ └── order-created.event.ts
├── listeners/ # @OnEvent处理器
│ └── order.listener.ts
├── <module>.module.ts
└── docs.md # UseCase文档UseCase Implementation Pattern
UseCase实现模式
Extend from :
UseCase<TRequest, TResponse>src/common/services/use-case.base.tstypescript
@Injectable()
export class CreateOrderUseCase extends UseCase<CreateOrderRequest, CreateOrderResponse> {
validate(request: CreateOrderRequest): void {
if (!isValidObjectId(request.cartId))
throw new BadRequestException('Invalid cart ID');
}
async doExecute(request: CreateOrderRequest): Promise<CreateOrderResponse> {
// 1. Fetch and validate context
const context = await this.validateAndFetchContext(request);
// 2. Perform core business logic
const result = this.performCoreLogic(context);
// 3. Persist changes
await this.persistChanges(result);
// 4. Return final result
return this.fetchFinalResult(result.id);
}
// Use interfaces for data between private methods
private async validateAndFetchContext(req: Request): Promise<ExecutionContext> { ... }
private performCoreLogic(ctx: ExecutionContext): CalculationResult { ... }
}Key rules:
- reads like a table of contents, not the implementation
doExecute - Use numbered comments for step-by-step flow
- Define s for data passed between private methods
interface - Never call directly in
this.prisma; use semantic private methodsdoExecute
从继承:
src/common/services/use-case.base.tsUseCase<TRequest, TResponse>typescript
@Injectable()
export class CreateOrderUseCase extends UseCase<CreateOrderRequest, CreateOrderResponse> {
validate(request: CreateOrderRequest): void {
if (!isValidObjectId(request.cartId))
throw new BadRequestException('Invalid cart ID');
}
async doExecute(request: CreateOrderRequest): Promise<CreateOrderResponse> {
// 1. 获取并验证上下文
const context = await this.validateAndFetchContext(request);
// 2. 执行核心业务逻辑
const result = this.performCoreLogic(context);
// 3. 持久化变更
await this.persistChanges(result);
// 4. 返回最终结果
return this.fetchFinalResult(result.id);
}
// 私有方法间的数据传递使用接口
private async validateAndFetchContext(req: Request): Promise<ExecutionContext> { ... }
private performCoreLogic(ctx: ExecutionContext): CalculationResult { ... }
}核心规则:
- 的写法应类似目录,而非具体实现
doExecute - 使用编号注释展示分步流程
- 为私有方法间传递的数据定义
interface - 切勿在中直接调用
doExecute;使用语义化私有方法this.prisma
Cross-Module Data Access
跨模块数据访问
CRITICAL: Never query another module's tables directly.
typescript
// WRONG: Direct Prisma access to another module's table
const user = await this.prisma.user.findUnique({ where: { id } });
// CORRECT: Use the owning module's service
const user = await this.userService.findUserById(id);Each module owns its tables exclusively. Cross-module data flows through Service Facades.
重要提示:切勿直接查询其他模块的表。
typescript
// 错误写法:直接访问其他模块的表
const user = await this.prisma.user.findUnique({ where: { id } });
// 正确写法:使用所属模块的服务
const user = await this.userService.findUserById(id);每个模块独占其表。跨模块数据通过Service Facade流转。
Validation Strategy
验证策略
Layer 1: Syntactic (DTOs)
层级1:语法验证(DTO)
All fields require decorators from :
@Is...class-validatortypescript
export class CreateOrderDto {
@IsString()
@IsNotEmpty()
cartId: string;
@IsOptional()
@IsString()
note?: string;
}所有字段需使用的装饰器:
class-validator@Is...typescript
export class CreateOrderDto {
@IsString()
@IsNotEmpty()
cartId: string;
@IsOptional()
@IsString()
note?: string;
}Layer 2: Controller Params
层级2:控制器参数
Use for MongoDB ObjectIds:
IsObjectIdPipetypescript
@Get(':id')
findOne(@Param('id', IsObjectIdPipe) id: string) { ... }对MongoDB ObjectId使用:
IsObjectIdPipetypescript
@Get(':id')
findOne(@Param('id', IsObjectIdPipe) id: string) { ... }Layer 3: Semantic (UseCase)
层级3:语义验证(UseCase)
Business rules checked in or early in :
validate()doExecute()typescript
private async validateBusinessRules(cart: CartV2, product: Product) {
if (cart.shopId !== product.shopId) {
throw new BadRequestException('Product does not belong to this shop');
}
if (product.inventory < 1) {
throw new BadRequestException('Product out of stock');
}
}在或早期检查业务规则:
validate()doExecute()typescript
private async validateBusinessRules(cart: CartV2, product: Product) {
if (cart.shopId !== product.shopId) {
throw new BadRequestException('Product does not belong to this shop');
}
if (product.inventory < 1) {
throw new BadRequestException('Product out of stock');
}
}Event-Driven Side Effects
事件驱动的副作用
Use for non-blocking operations (emails, analytics, webhooks):
EventEmitter2typescript
// Publisher (in UseCase)
this.eventEmitter.emit('order.created', new OrderCreatedEvent(order));
// Subscriber (in listeners/)
@OnEvent('order.created')
async handleOrderCreated(payload: OrderCreatedEvent) {
await this.emailService.sendReceipt(payload.order.email);
}When to use events:
- Sending notifications (email, SMS)
- Updating analytics/logs
- Syncing with external systems (webhooks)
When NOT to use events:
- Core logic requiring strict consistency (use direct calls)
使用处理非阻塞操作(邮件、分析、Webhook):
EventEmitter2typescript
// 发布者(在UseCase中)
this.eventEmitter.emit('order.created', new OrderCreatedEvent(order));
// 订阅者(在listeners/中)
@OnEvent('order.created')
async handleOrderCreated(payload: OrderCreatedEvent) {
await this.emailService.sendReceipt(payload.order.email);
}何时使用事件:
- 发送通知(邮件、短信)
- 更新分析/日志
- 与外部系统同步(Webhook)
何时不使用事件:
- 需要强一致性的核心逻辑(使用直接调用)
Database Conventions (Prisma + MongoDB)
数据库规范(Prisma + MongoDB)
- Schema at
prisma/schema.prisma - Use to fetch only needed fields
select - Use for independent parallel queries
Promise.all - Use over
findUniquefor indexed lookupsfindFirst - Semantic repository methods: , not
findOrderWithRelations()findOne()
typescript
// Good: Parallel independent queries
const [user, cart] = await Promise.all([
this.fetchUser(userId),
this.fetchCart(cartId),
]);
// Good: Select only needed fields
this.prisma.product.findUnique({
where: { id },
select: { id: true, price: true },
});- 架构文件位于
prisma/schema.prisma - 使用仅获取所需字段
select - 对独立并行查询使用
Promise.all - 索引查询使用而非
findUniquefindFirst - 语义化仓库方法:,而非
findOrderWithRelations()findOne()
typescript
// 推荐:并行独立查询
const [user, cart] = await Promise.all([
this.fetchUser(userId),
this.fetchCart(cartId),
]);
// 推荐:仅选择所需字段
this.prisma.product.findUnique({
where: { id },
select: { id: true, price: true },
});Quick Commands
快速命令
bash
npm run db:generate # Prisma generate + Swagger docs
npm run start:dev # Development with watch
npm run test:e2e # End-to-end tests
npm run docs:generate # Generate unified Swaggerbash
npm run db:generate # Prisma生成 + Swagger文档
npm run start:dev # 开发模式(带热重载)
npm run test:e2e # 端到端测试
npm run docs:generate # 生成统一Swagger文档Reference Files
参考文件
- Architecture patterns: See references/architecture.md for detailed UseCase patterns, Saga pattern, and repository guidelines
- Entity models: See references/entities.md for core data models (Shop, Product, Order, Cart, Customer, Merchant)
- Developer checklist: See references/checklists.md for pre-commit and PR review checklists
- 架构模式:查看references/architecture.md获取详细的UseCase模式、Saga模式及仓库指南
- 实体模型:查看references/entities.md获取核心数据模型(Shop、Product、Order、Cart、Customer、Merchant)
- 开发者检查清单:查看references/checklists.md获取提交前和PR审查检查清单