Loading...
Loading...
Code style, architecture patterns, and development guide for the Droplinked e-commerce backend. NestJS + Prisma (MongoDB) with strict layered architecture: Controller to Service Facade to UseCase to Repository. Use this skill when: (1) Creating new features, modules, or endpoints in the Droplinked backend (2) Refactoring existing code to follow project patterns (3) Writing tests for use cases and services (4) Reviewing code for architectural compliance (5) Understanding the data model (shops, products, orders, carts, merchants, customers) (6) Implementing cross-module communication (7) Adding event-driven side effects (8) Working with Prisma and MongoDB in this codebase Triggers: droplinked, nest module, usecase, service facade, repository pattern, cart, order, shop, product, merchant
npx skill4agent add erfanabedinpour/droplinked-skills droplinked-backendController -> 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 |
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 documentationUseCase<TRequest, TResponse>src/common/services/use-case.base.ts@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 { ... }
}doExecuteinterfacethis.prismadoExecute// 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);@Is...class-validatorexport class CreateOrderDto {
@IsString()
@IsNotEmpty()
cartId: string;
@IsOptional()
@IsString()
note?: string;
}IsObjectIdPipe@Get(':id')
findOne(@Param('id', IsObjectIdPipe) id: string) { ... }validate()doExecute()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');
}
}EventEmitter2// 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);
}prisma/schema.prismaselectPromise.allfindUniquefindFirstfindOrderWithRelations()findOne()// 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 },
});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 Swagger