- Added request/response flow diagrams to api-design and api-gateway-advanced skills for better visualization of processes. - Introduced configuration loading flow in configuration-management skill to clarify the configuration process. - Included error propagation flow in error-handling-patterns skill to illustrate error handling across layers. - Enhanced various skills with additional diagrams to improve understanding of complex concepts. These updates aim to provide clearer guidance and improve the overall documentation experience for developers.
417 lines
14 KiB
Markdown
417 lines
14 KiB
Markdown
# Các Pattern Service Layer
|
|
|
|
Service layer organization and patterns for GoodGo microservices including business logic implementation, dependency injection, service composition, and separation of concerns.
|
|
> Tổ chức và các patterns của service layer cho GoodGo microservices bao gồm implement business logic, dependency injection, service composition, và separation of concerns.
|
|
|
|
## Tổng Quan
|
|
|
|
The service layer is where business logic lives in GoodGo microservices. It sits between controllers (HTTP layer) and repositories (data layer), orchestrating business operations, enforcing business rules, and coordinating data access. This guide covers service organization patterns, dependency injection, service composition, and best practices.
|
|
|
|
Service layer là nơi chứa business logic trong GoodGo microservices. Nó nằm giữa controllers (HTTP layer) và repositories (data layer), điều phối business operations, thực thi business rules, và phối hợp data access. Hướng dẫn này bao gồm các patterns tổ chức service, dependency injection, service composition, và best practices.
|
|
|
|
## Khi Nào Sử Dụng
|
|
|
|
Use service layer patterns when:
|
|
- Implementing business logic in services
|
|
- Organizing service layer code
|
|
- Using dependency injection patterns
|
|
- Composing multiple services together
|
|
- Separating concerns between controllers, services, and repositories
|
|
- Handling business rules and validations
|
|
- Implementing service composition patterns
|
|
|
|
Sử dụng service layer patterns khi:
|
|
- Implement business logic trong services
|
|
- Tổ chức service layer code
|
|
- Sử dụng dependency injection patterns
|
|
- Kết hợp nhiều services với nhau
|
|
- Tách biệt concerns giữa controllers, services, và repositories
|
|
- Xử lý business rules và validations
|
|
- Implement service composition patterns
|
|
|
|
## Khái Niệm Chính
|
|
|
|
### Kiến Trúc Service Layer
|
|
|
|
Service layer tuân theo mô hình kiến trúc ba tầng, tách biệt các concerns giữa xử lý HTTP, business logic, và truy cập dữ liệu:
|
|
|
|
```mermaid
|
|
graph TB
|
|
subgraph "HTTP Layer"
|
|
Controller["Controller<br/>- HTTP request/response<br/>- Input validation<br/>- Status codes"]
|
|
end
|
|
|
|
subgraph "Business Layer"
|
|
Service["Service<br/>- Business logic<br/>- Business rules<br/>- Orchestration<br/>- Caching<br/>- Logging"]
|
|
end
|
|
|
|
subgraph "Data Layer"
|
|
Repository["Repository<br/>- Data access<br/>- CRUD operations<br/>- Database queries"]
|
|
Database[("Database<br/>Prisma")]
|
|
end
|
|
|
|
Controller -->|"Calls"| Service
|
|
Service -->|"Uses"| Repository
|
|
Repository -->|"Queries"| Database
|
|
|
|
style Controller fill:#e1f5ff
|
|
style Service fill:#fff4e1
|
|
style Repository fill:#e8f5e9
|
|
style Database fill:#f3e5f5
|
|
```
|
|
|
|
### Trách Nhiệm Service Layer
|
|
|
|
The service layer has specific responsibilities that distinguish it from controllers and repositories.
|
|
|
|
Service layer có các trách nhiệm cụ thể phân biệt nó với controllers và repositories.
|
|
|
|
**Service Layer Responsibilities / Trách Nhiệm**:
|
|
- **Business Logic / Logic Nghiệp Vụ**: Contains domain-specific business rules and logic
|
|
- **Orchestration / Điều Phối**: Orchestrates calls to repositories and other services
|
|
- **Validation / Xác Thực**: Validates business rules (not just input validation)
|
|
- **Cross-cutting Concerns / Concerns Xuyên Suốt**: Handles caching, logging, auditing
|
|
- **Coordination / Phối Hợp**: Coordinates multiple repositories or services
|
|
- **Transport Independence / Độc Lập Transport**: Independent of HTTP transport layer
|
|
|
|
**NOT Service Responsibilities / KHÔNG Phải Trách Nhiệm**:
|
|
- HTTP request/response handling (that's controllers)
|
|
- Database queries (that's repositories)
|
|
- Authentication/authorization (that's middleware)
|
|
|
|
### Tiêm Phụ Thuộc
|
|
|
|
Services use constructor injection to receive dependencies, making them testable and loosely coupled. Luồng dependency injection cho thấy cách các components được kết nối với nhau:
|
|
|
|
```mermaid
|
|
graph LR
|
|
subgraph "Module Initialization"
|
|
Prisma[("Prisma Client")]
|
|
Cache[("Cache Service")]
|
|
end
|
|
|
|
subgraph "Dependency Creation"
|
|
Repo["UserRepository<br/>(prisma)"]
|
|
Service["UserService<br/>(repository, cache)"]
|
|
Controller["UserController<br/>(service)"]
|
|
end
|
|
|
|
subgraph "Router Setup"
|
|
Router["Router<br/>(controller methods)"]
|
|
end
|
|
|
|
Prisma -->|"Injected"| Repo
|
|
Cache -->|"Injected"| Service
|
|
Repo -->|"Injected"| Service
|
|
Service -->|"Injected"| Controller
|
|
Controller -->|"Bound to"| Router
|
|
|
|
style Prisma fill:#f3e5f5
|
|
style Cache fill:#f3e5f5
|
|
style Repo fill:#e8f5e9
|
|
style Service fill:#fff4e1
|
|
style Controller fill:#e1f5ff
|
|
style Router fill:#e1f5ff
|
|
```
|
|
|
|
**Ví Dụ Implementation:**
|
|
|
|
Services sử dụng constructor injection để nhận dependencies, làm cho chúng testable và loosely coupled:
|
|
|
|
```typescript
|
|
export class UserService {
|
|
constructor(
|
|
private userRepository: UserRepository,
|
|
private cacheService: CacheService,
|
|
private logger: Logger
|
|
) {}
|
|
}
|
|
```
|
|
|
|
## Patterns
|
|
|
|
### Luồng Request Qua Các Layer
|
|
|
|
Sơ đồ sequence sau minh họa cách một request chảy qua các layer Controller → Service → Repository:
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Client
|
|
participant Controller
|
|
participant Service
|
|
participant Cache
|
|
participant Repository
|
|
participant Database
|
|
|
|
Client->>Controller: HTTP Request (GET /users/:id)
|
|
Controller->>Controller: Validate input
|
|
Controller->>Service: getUserById(id)
|
|
|
|
Service->>Cache: Check cache
|
|
alt Cache Hit
|
|
Cache-->>Service: Return cached user
|
|
Service-->>Controller: Return user
|
|
else Cache Miss
|
|
Service->>Repository: findById(id)
|
|
Repository->>Database: Query user
|
|
Database-->>Repository: User data
|
|
Repository-->>Service: User entity
|
|
|
|
alt User Not Found
|
|
Service-->>Controller: Throw NotFoundError
|
|
Controller-->>Client: 404 Not Found
|
|
else User Found
|
|
Service->>Cache: Set cache (TTL: 5min)
|
|
Service-->>Controller: Return user
|
|
Controller-->>Client: 200 OK + User data
|
|
end
|
|
end
|
|
```
|
|
|
|
### Pattern Service Cơ Bản
|
|
|
|
Basic service implementation with repository dependency.
|
|
|
|
Implementation service cơ bản với repository dependency.
|
|
|
|
```typescript
|
|
import { logger } from '@goodgo/logger';
|
|
import { userRepository } from '../repositories/user.repository';
|
|
import { NotFoundError } from '../errors/http-error';
|
|
|
|
export class UserService {
|
|
async getUserById(id: string) {
|
|
logger.info('Fetching user by ID', { id });
|
|
|
|
const user = await userRepository.findById(id);
|
|
if (!user) {
|
|
throw new NotFoundError('User', { id });
|
|
}
|
|
|
|
return user;
|
|
}
|
|
|
|
async createUser(data: CreateUserInput) {
|
|
logger.info('Creating user', { email: data.email });
|
|
|
|
const user = await userRepository.create(data);
|
|
logger.info('User created', { userId: user.id });
|
|
|
|
return user;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Tham Khảo**: [`services/iam-service/src/modules/feature/feature.service.ts`](../../../services/iam-service/src/modules/feature/feature.service.ts)
|
|
|
|
### Service Với Caching
|
|
|
|
Services implement caching to improve performance for frequently accessed data.
|
|
|
|
Services implement caching để cải thiện hiệu suất cho dữ liệu thường xuyên truy cập.
|
|
|
|
```typescript
|
|
export class UserService {
|
|
constructor(
|
|
private repository: UserRepository,
|
|
private cacheService: CacheService
|
|
) {}
|
|
|
|
async getUserById(id: string) {
|
|
const cacheKey = cacheService.keys.user(id);
|
|
|
|
// Thử cache trước
|
|
const cached = await this.cacheService.get<User>(cacheKey);
|
|
if (cached) return cached;
|
|
|
|
// Cache miss - fetch từ repository
|
|
const user = await this.repository.findById(id);
|
|
if (!user) {
|
|
throw new NotFoundError('User');
|
|
}
|
|
|
|
// Cache trong 5 phút
|
|
await this.cacheService.set(cacheKey, user, 300);
|
|
return user;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Tham Khảo**: [`services/iam-service/src/modules/rbac/rbac.service.ts`](../../../services/iam-service/src/modules/rbac/rbac.service.ts)
|
|
|
|
### Kết Hợp Services
|
|
|
|
Services can depend on other services to compose complex operations. Sơ đồ sau minh họa service composition:
|
|
|
|
```mermaid
|
|
graph TB
|
|
subgraph "AccessRequestService"
|
|
ARService["AccessRequestService"]
|
|
ARRepo["AccessRequestRepository"]
|
|
end
|
|
|
|
subgraph "Dependencies"
|
|
UserService["UserService"]
|
|
RBACService["RBACService"]
|
|
end
|
|
|
|
subgraph "Supporting Services"
|
|
UserRepo["UserRepository"]
|
|
RBACRepo["RBACRepository"]
|
|
end
|
|
|
|
ARService -->|"Uses"| UserService
|
|
ARService -->|"Uses"| RBACService
|
|
ARService -->|"Uses"| ARRepo
|
|
|
|
UserService -->|"Uses"| UserRepo
|
|
RBACService -->|"Uses"| RBACRepo
|
|
|
|
style ARService fill:#fff4e1
|
|
style UserService fill:#fff4e1
|
|
style RBACService fill:#fff4e1
|
|
style ARRepo fill:#e8f5e9
|
|
style UserRepo fill:#e8f5e9
|
|
style RBACRepo fill:#e8f5e9
|
|
```
|
|
|
|
**Ví Dụ Implementation:**
|
|
|
|
Services có thể phụ thuộc vào các services khác để kết hợp các operations phức tạp:
|
|
|
|
```typescript
|
|
export class AccessRequestService {
|
|
constructor(
|
|
private accessRequestRepository: AccessRequestRepository,
|
|
private userService: UserService,
|
|
private rbacService: RBACService
|
|
) {}
|
|
|
|
async createRequest(userId: string, data: CreateRequestDto) {
|
|
// Sử dụng các services khác
|
|
const user = await this.userService.getUserById(userId);
|
|
const hasPermission = await this.rbacService.checkPermission(userId, 'CREATE_REQUEST');
|
|
|
|
if (!hasPermission) {
|
|
throw new ForbiddenError('Insufficient permissions');
|
|
}
|
|
|
|
return await this.accessRequestRepository.create({ ...data, userId });
|
|
}
|
|
}
|
|
```
|
|
|
|
### Xác Thực Business Logic
|
|
|
|
Services validate business rules beyond simple input validation.
|
|
|
|
Services validate business rules vượt quá input validation đơn giản.
|
|
|
|
```typescript
|
|
export class UserService {
|
|
async createUser(data: CreateUserInput) {
|
|
// Business rule: Kiểm tra email đã tồn tại
|
|
const existing = await this.repository.findByEmail(data.email);
|
|
if (existing) {
|
|
throw new ConflictError('User with this email already exists');
|
|
}
|
|
|
|
// Business rule: Validate organization
|
|
if (data.organizationId) {
|
|
const org = await this.orgRepository.findById(data.organizationId);
|
|
if (!org) {
|
|
throw new NotFoundError('Organization');
|
|
}
|
|
}
|
|
|
|
return await this.repository.create(data);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Pattern Service Module
|
|
|
|
Organize services into modules with controllers and routers.
|
|
|
|
Tổ chức services thành modules với controllers và routers.
|
|
|
|
```typescript
|
|
export class FeatureModule {
|
|
private controller: FeatureController;
|
|
private service: FeatureService;
|
|
private router: Router;
|
|
|
|
constructor() {
|
|
const repository = new FeatureRepository(prisma);
|
|
this.service = new FeatureService(repository);
|
|
this.controller = new FeatureController(this.service);
|
|
this.router = this.createRouter();
|
|
}
|
|
|
|
getRouter(): Router {
|
|
return this.router;
|
|
}
|
|
|
|
private createRouter(): Router {
|
|
const router = Router();
|
|
router.get('/', asyncHandler(this.controller.findAll.bind(this.controller)));
|
|
router.post('/', asyncHandler(this.controller.create.bind(this.controller)));
|
|
return router;
|
|
}
|
|
}
|
|
```
|
|
|
|
**Tham Khảo**: [`services/iam-service/src/modules/feature/feature.module.ts`](../../../services/iam-service/src/modules/feature/feature.module.ts)
|
|
|
|
## Thực Hành Tốt Nhất
|
|
|
|
1. **Single Responsibility / Trách Nhiệm Đơn**: Mỗi service xử lý một domain area
|
|
2. **Dependency Injection / Tiêm Phụ Thuộc**: Sử dụng constructor injection để dễ test
|
|
3. **Business Logic Only / Chỉ Business Logic**: Giữ HTTP concerns trong controllers
|
|
4. **Use Repositories / Sử Dụng Repositories**: Không truy cập database trực tiếp
|
|
5. **Error Handling / Xử Lý Lỗi**: Throw appropriate domain errors
|
|
6. **Logging / Ghi Log**: Log các operations quan trọng và errors
|
|
7. **Caching / Caching**: Implement caching trong services, không phải repositories
|
|
8. **Composition / Kết Hợp**: Kết hợp services cho complex operations
|
|
9. **Pure Functions / Functions Thuần**: Giữ service methods pure khi có thể
|
|
10. **Transaction Coordination / Phối Hợp Transaction**: Coordinate transactions cho multi-step operations
|
|
|
|
## Lỗi Thường Gặp
|
|
|
|
1. **HTTP in Services / HTTP Trong Services**: Sử dụng `req`/`res` trong services
|
|
2. **Direct Database Access / Truy Cập Database Trực Tiếp**: Truy cập Prisma trực tiếp thay vì repositories
|
|
3. **Too Many Responsibilities / Quá Nhiều Trách Nhiệm**: Service làm quá nhiều việc
|
|
4. **No Error Handling / Không Xử Lý Lỗi**: Không throw appropriate errors
|
|
5. **Business Logic in Controllers / Business Logic Trong Controllers**: Đưa business logic vào controllers
|
|
6. **Tight Coupling / Kết Hợp Chặt**: Services tightly coupled với specific implementations
|
|
7. **Missing Validation / Thiếu Validation**: Không validate business rules
|
|
|
|
## Xử Lý Sự Cố
|
|
|
|
### Phụ Thuộc Vòng Tròn
|
|
|
|
**Problem / Vấn Đề**: Circular dependencies giữa services
|
|
|
|
**Solution / Giải Pháp**:
|
|
- Refactor để loại bỏ circular dependencies
|
|
- Extract shared logic vào common service hoặc utility
|
|
- Use dependency injection properly
|
|
|
|
### Service Quá Lớn
|
|
|
|
**Problem / Vấn Đề**: Service class quá lớn với quá nhiều methods
|
|
|
|
**Solution / Giải Pháp**:
|
|
- Split service thành multiple smaller services
|
|
- Extract related functionality vào separate services
|
|
- Use service composition instead of one large service
|
|
|
|
## Tài Nguyên
|
|
|
|
- [Feature Service](../../../services/iam-service/src/modules/feature/feature.service.ts) - Example service implementation
|
|
- [User Service](../../../services/iam-service/src/modules/identity/user/user.service.ts) - User management service
|
|
- [RBAC Service](../../../services/iam-service/src/modules/rbac/rbac.service.ts) - Role-based access control service
|
|
- [Repository Pattern](./repository-pattern.md) - Repository patterns
|
|
- [Caching Patterns](./caching-patterns.md) - Caching in services
|
|
- [Error Handling](./error-handling-patterns.md) - Error handling patterns
|