feat(deploy): production deployment setup and fixes
- Debian-based api image (bookworm-slim), docker/debian mirrors, prisma binaryTargets for openssl 3.0 - nginx: admin SPA under /admin, TLS via acme.sh (ZeroSSL) + auto-renewal cron, http->https redirect - prisma: add origin_goods.delisted migration, sync missing schema (good_image/tag_font_color/good_tags), fix users.createdAt Timestamptz - api: CORS wildcard reflection, helmet CORP cross-origin, price backfill in persistProductDetail, categoryIcon ancestor fallback, mediaByColor per-color gallery in public goods detail - admin: /admin base path (vite + router) - import-data.mjs: udt_name casting, serial sequence advance fix
This commit is contained in:
@@ -1,104 +0,0 @@
|
||||
---
|
||||
title: Avoid Service Locator Anti-Pattern
|
||||
impact: HIGH
|
||||
impactDescription: Hides dependencies and breaks testability
|
||||
tags: dependency-injection, anti-patterns, testing
|
||||
---
|
||||
|
||||
## Avoid Service Locator Anti-Pattern
|
||||
|
||||
Avoid using `ModuleRef.get()` or global containers to resolve dependencies at runtime. This hides dependencies, makes code harder to test, and breaks the benefits of dependency injection. Use constructor injection instead.
|
||||
|
||||
**Incorrect (service locator anti-pattern):**
|
||||
|
||||
```typescript
|
||||
// Use ModuleRef to get dependencies dynamically
|
||||
@Injectable()
|
||||
export class OrdersService {
|
||||
constructor(private moduleRef: ModuleRef) {}
|
||||
|
||||
async createOrder(dto: CreateOrderDto): Promise<Order> {
|
||||
// Dependencies are hidden - not visible in constructor
|
||||
const usersService = this.moduleRef.get(UsersService);
|
||||
const inventoryService = this.moduleRef.get(InventoryService);
|
||||
const paymentService = this.moduleRef.get(PaymentService);
|
||||
|
||||
const user = await usersService.findOne(dto.userId);
|
||||
// ... rest of logic
|
||||
}
|
||||
}
|
||||
|
||||
// Global singleton container
|
||||
class ServiceContainer {
|
||||
private static instance: ServiceContainer;
|
||||
private services = new Map<string, any>();
|
||||
|
||||
static getInstance(): ServiceContainer {
|
||||
if (!this.instance) {
|
||||
this.instance = new ServiceContainer();
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
get<T>(key: string): T {
|
||||
return this.services.get(key);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Correct (constructor injection with explicit dependencies):**
|
||||
|
||||
```typescript
|
||||
// Use constructor injection - dependencies are explicit
|
||||
@Injectable()
|
||||
export class OrdersService {
|
||||
constructor(
|
||||
private usersService: UsersService,
|
||||
private inventoryService: InventoryService,
|
||||
private paymentService: PaymentService,
|
||||
) {}
|
||||
|
||||
async createOrder(dto: CreateOrderDto): Promise<Order> {
|
||||
const user = await this.usersService.findOne(dto.userId);
|
||||
const inventory = await this.inventoryService.check(dto.items);
|
||||
// Dependencies are clear and testable
|
||||
}
|
||||
}
|
||||
|
||||
// Easy to test with mocks
|
||||
describe('OrdersService', () => {
|
||||
let service: OrdersService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [
|
||||
OrdersService,
|
||||
{ provide: UsersService, useValue: mockUsersService },
|
||||
{ provide: InventoryService, useValue: mockInventoryService },
|
||||
{ provide: PaymentService, useValue: mockPaymentService },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get(OrdersService);
|
||||
});
|
||||
});
|
||||
|
||||
// VALID: Factory pattern for dynamic instantiation
|
||||
@Injectable()
|
||||
export class HandlerFactory {
|
||||
constructor(private moduleRef: ModuleRef) {}
|
||||
|
||||
getHandler(type: string): Handler {
|
||||
switch (type) {
|
||||
case 'email':
|
||||
return this.moduleRef.get(EmailHandler);
|
||||
case 'sms':
|
||||
return this.moduleRef.get(SmsHandler);
|
||||
default:
|
||||
return this.moduleRef.get(DefaultHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference: [NestJS Module Reference](https://docs.nestjs.com/fundamentals/module-ref)
|
||||
Reference in New Issue
Block a user