New skills (14): - nestjs-best-practices: 40 priority-ranked rules (kadajett) - fastapi: Pydantic v2, async SQLAlchemy, JWT auth (jezweb) - architecture-patterns: Clean Architecture, Hexagonal, DDD (wshobson) - python-performance-optimization: Profiling and optimization (wshobson) - ai-sdk: Vercel AI SDK streaming and agent patterns (vercel) - create-agent: Modular agent architecture with OpenRouter (openrouterteam) - proactive-agent: WAL Protocol, compaction recovery, self-improvement (halthelobster) - brand-guidelines: Brand identity enforcement (anthropics) - ui-animation: Motion design with accessibility (mblode) - marketing-ideas: 139 ideas across 14 categories (coreyhaines31) - pricing-strategy: SaaS pricing and tier design (coreyhaines31) - programmatic-seo: SEO at scale with playbooks (coreyhaines31) - competitor-alternatives: Comparison page architecture (coreyhaines31) - referral-program: Referral and affiliate programs (coreyhaines31) README reorganized by domain: Code Quality, Frontend, Backend, Auth, AI/Agent Building, Marketing, Design, Meta. Mosaic Stack is not limited to coding — the Orchestrator serves coding, business, design, marketing, writing, logistics, and analysis. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
107 lines
2.9 KiB
Markdown
107 lines
2.9 KiB
Markdown
---
|
|
title: Single Responsibility for Services
|
|
impact: CRITICAL
|
|
impactDescription: "40%+ improvement in testability"
|
|
tags: architecture, services, single-responsibility
|
|
---
|
|
|
|
## Single Responsibility for Services
|
|
|
|
Each service should have a single, well-defined responsibility. Avoid "god services" that handle multiple unrelated concerns. If a service name includes "And" or handles more than one domain concept, it likely violates single responsibility. This reduces complexity and improves testability by 40%+.
|
|
|
|
**Incorrect (god service anti-pattern):**
|
|
|
|
```typescript
|
|
// God service anti-pattern
|
|
@Injectable()
|
|
export class UserAndOrderService {
|
|
constructor(
|
|
private userRepo: UserRepository,
|
|
private orderRepo: OrderRepository,
|
|
private mailer: MailService,
|
|
private payment: PaymentService,
|
|
) {}
|
|
|
|
async createUser(dto: CreateUserDto) {
|
|
const user = await this.userRepo.save(dto);
|
|
await this.mailer.sendWelcome(user);
|
|
return user;
|
|
}
|
|
|
|
async createOrder(userId: string, dto: CreateOrderDto) {
|
|
const order = await this.orderRepo.save({ userId, ...dto });
|
|
await this.payment.charge(order);
|
|
await this.mailer.sendOrderConfirmation(order);
|
|
return order;
|
|
}
|
|
|
|
async calculateOrderStats(userId: string) {
|
|
// Stats logic mixed in
|
|
}
|
|
|
|
async validatePayment(orderId: string) {
|
|
// Payment logic mixed in
|
|
}
|
|
}
|
|
```
|
|
|
|
**Correct (focused services with single responsibility):**
|
|
|
|
```typescript
|
|
// Focused services with single responsibility
|
|
@Injectable()
|
|
export class UsersService {
|
|
constructor(private userRepo: UserRepository) {}
|
|
|
|
async create(dto: CreateUserDto): Promise<User> {
|
|
return this.userRepo.save(dto);
|
|
}
|
|
|
|
async findById(id: string): Promise<User> {
|
|
return this.userRepo.findOneOrFail({ where: { id } });
|
|
}
|
|
}
|
|
|
|
@Injectable()
|
|
export class OrdersService {
|
|
constructor(private orderRepo: OrderRepository) {}
|
|
|
|
async create(userId: string, dto: CreateOrderDto): Promise<Order> {
|
|
return this.orderRepo.save({ userId, ...dto });
|
|
}
|
|
|
|
async findByUser(userId: string): Promise<Order[]> {
|
|
return this.orderRepo.find({ where: { userId } });
|
|
}
|
|
}
|
|
|
|
@Injectable()
|
|
export class OrderStatsService {
|
|
constructor(private orderRepo: OrderRepository) {}
|
|
|
|
async calculateForUser(userId: string): Promise<OrderStats> {
|
|
// Focused stats calculation
|
|
}
|
|
}
|
|
|
|
// Orchestration in controller or dedicated orchestrator
|
|
@Controller('orders')
|
|
export class OrdersController {
|
|
constructor(
|
|
private orders: OrdersService,
|
|
private payment: PaymentService,
|
|
private notifications: NotificationService,
|
|
) {}
|
|
|
|
@Post()
|
|
async create(@CurrentUser() user: User, @Body() dto: CreateOrderDto) {
|
|
const order = await this.orders.create(user.id, dto);
|
|
await this.payment.charge(order);
|
|
await this.notifications.sendOrderConfirmation(order);
|
|
return order;
|
|
}
|
|
}
|
|
```
|
|
|
|
Reference: [NestJS Providers](https://docs.nestjs.com/providers)
|