690404:1139 Modify ADR
This commit is contained in:
@@ -20,34 +20,16 @@
|
||||
- Webhook endpoint: `/webhook/ai-processing`
|
||||
- Environment variables: `N8N_BASIC_AUTH_USER`, `N8N_BASIC_AUTH_PASSWORD`
|
||||
- [ ] **Ollama Service:**
|
||||
- Pull model: `gemma:9b` (GPU optimized, higher accuracy)
|
||||
- Pull model: `gemma4:e4b` (GPU optimized, higher accuracy)
|
||||
- API endpoint: `http://localhost:11434`
|
||||
- Health check: `GET /api/tags`
|
||||
- Memory requirement: Minimum 8GB VRAM for 9B model
|
||||
- ollama run gemma4:9b-q5_K_M / gemma4:9b-q4_K_M
|
||||
- สร้างไฟล์ %USERPROFILE%\.ollama\config
|
||||
```config
|
||||
# ใช้ GPU เป็นหลัก
|
||||
gpu: true
|
||||
num_gpu: 1
|
||||
|
||||
# เปิด KV cache เพื่อให้ตอบเร็วขึ้น
|
||||
kv_cache: true
|
||||
|
||||
# จำกัด batch size ให้เหมาะกับ VRAM 8GB
|
||||
gpu_batch_size: 512
|
||||
|
||||
# ปรับ num_thread ให้เหมาะกับ CPU 6–8 คอร์
|
||||
num_thread: 6
|
||||
|
||||
# เปิด mmap เพื่อโหลดโมเดลเร็วขึ้น
|
||||
mmap: true
|
||||
|
||||
# ปรับ max_seq_len ให้เหมาะกับงาน DMS
|
||||
max_seq_len: 4096
|
||||
|
||||
# ปรับ temp ต่ำเพื่อให้ผลลัพธ์เสถียร
|
||||
temperature: 0.2
|
||||
- ollama run gemma4
|
||||
- ตั้ง Windows System Environment Variables หรือใน PowerShell:
|
||||
- [System.Environment]::SetEnvironmentVariable('OLLAMA_HOST', '0.0.0.0', 'User')
|
||||
- [System.Environment]::SetEnvironmentVariable('OLLAMA_KEEP_ALIVE', '30m', 'User')
|
||||
- [System.Environment]::SetEnvironmentVariable("OLLAMA_NUM_GPU", "1", "User")
|
||||
- [System.Environment]::SetEnvironmentVariable("OLLAMA_NUM_THREAD", "8", "User")
|
||||
```
|
||||
|
||||
- [ ] **PaddleOCR Service:**
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
# Task BE-API-01: API Design Strategy Implementation
|
||||
|
||||
**Phase:** Backend API Standardization
|
||||
**ADR Compliance:** ADR-003 (API Design Strategy), ADR-019 (UUID Strategy)
|
||||
**Priority:** 🟡 Important - API Consistency & Developer Experience
|
||||
|
||||
> **Context:** การ implement Hybrid REST + Action Endpoints ตาม ADR-003 เพื่อให้ API สอดคล้องกันทั่วทั้งระบบ
|
||||
|
||||
---
|
||||
|
||||
## 📋 Implementation Tasks
|
||||
|
||||
### **API-1.1: Base Controller & Patterns**
|
||||
- [ ] **Create Base Controller Class:**
|
||||
- File: `backend/src/common/controllers/base.controller.ts`
|
||||
- Features: Standard CRUD operations, pagination, filtering
|
||||
- Methods: `findAll()`, `findOne(uuid)`, `create()`, `update(uuid)`, `remove(uuid)`
|
||||
- [ ] **Create Standard Response DTOs:**
|
||||
- File: `backend/src/common/dto/response.dto.ts`
|
||||
- Types: `ApiResponse<T>`, `PaginatedResponse<T>`, `ActionResponse`
|
||||
- Include meta information: timestamp, pagination, version
|
||||
- [ ] **Create Action Endpoint Decorator:**
|
||||
- File: `backend/src/common/decorators/action-endpoint.decorator.ts`
|
||||
- Purpose: Mark business action endpoints for Swagger documentation
|
||||
- Usage: `@ActionEndpoint('submit', 'Submit correspondence for routing')`
|
||||
|
||||
### **API-1.2: Correspondence API Refactor**
|
||||
- [ ] **Update Correspondence Controller:**
|
||||
- File: `backend/src/modules/correspondence/correspondence.controller.ts`
|
||||
- REST endpoints: GET, POST, PUT, PATCH, DELETE `/correspondences`
|
||||
- Action endpoints:
|
||||
- POST `/correspondences/:uuid/submit`
|
||||
- POST `/correspondences/:uuid/approve`
|
||||
- POST `/correspondences/:uuid/reject`
|
||||
- POST `/correspondences/:uuid/forward`
|
||||
- [ ] **Create Action DTOs:**
|
||||
- File: `backend/src/modules/correspondence/dto/correspondence-actions.dto.ts`
|
||||
- DTOs: `SubmitDto`, `ApproveDto`, `RejectDto`, `ForwardDto`
|
||||
- Validation: Business rules for each action
|
||||
- [ ] **Update Correspondence Service:**
|
||||
- Methods: `submit()`, `approve()`, `reject()`, `forward()`
|
||||
- Integration: Workflow engine transitions
|
||||
- Error handling: Business exceptions for invalid states
|
||||
|
||||
### **API-1.3: RFA API Implementation**
|
||||
- [ ] **Create RFA Controller:**
|
||||
- File: `backend/src/modules/rfa/rfa.controller.ts`
|
||||
- REST endpoints: Standard CRUD for RFAs
|
||||
- Action endpoints:
|
||||
- POST `/rfa/:uuid/submit-for-review`
|
||||
- POST `/rfa/:uuid/approve`
|
||||
- POST `/rfa/:uuid/request-changes`
|
||||
- POST `/rfa/:uuid/final-approve`
|
||||
- [ ] **Create RFA Action DTOs:**
|
||||
- File: `backend/src/modules/rfa/dto/rfa-actions.dto.ts`
|
||||
- DTOs: `SubmitForReviewDto`, `ApproveDto`, `RequestChangesDto`
|
||||
- Include: Comments, attachments, change requests
|
||||
- [ ] **Update RFA Service:**
|
||||
- Workflow integration: Document numbering, state transitions
|
||||
- Business logic: Review cycles, approval chains
|
||||
- Notifications: Trigger email/LINE notifications
|
||||
|
||||
### **API-1.4: API Documentation & Testing**
|
||||
- [ ] **Update Swagger Configuration:**
|
||||
- File: `backend/src/main.ts`
|
||||
- Group endpoints by module and type (REST vs Actions)
|
||||
- Add examples for all DTOs and responses
|
||||
- Configure security: JWT Bearer authentication
|
||||
- [ ] **Create API Client Library:**
|
||||
- File: `frontend/lib/api/client.ts`
|
||||
- Methods: Typed API calls for all endpoints
|
||||
- Error handling: Parse structured error responses
|
||||
- Type safety: TypeScript interfaces for all DTOs
|
||||
- [ ] **Add Integration Tests:**
|
||||
- Directory: `backend/test/api/`
|
||||
- Coverage: All REST and action endpoints
|
||||
- Scenarios: Success cases, error cases, permission tests
|
||||
- Tools: Jest, Supertest
|
||||
|
||||
### **API-1.5: Performance & Optimization**
|
||||
- [ ] **Implement Response Caching:**
|
||||
- Master data endpoints: Organizations, projects, types
|
||||
- Cache keys: `master:${entity}:${projectId}`
|
||||
- TTL: 1 hour for master data, 15 minutes for dynamic data
|
||||
- [ ] **Add Request Validation:**
|
||||
- DTO validation: class-validator decorators
|
||||
- Business rule validation: Custom validators
|
||||
- Rate limiting: By user role and endpoint type
|
||||
- [ ] **Database Query Optimization:**
|
||||
- Pagination: Limit/offset with total count
|
||||
- Filtering: Dynamic where clauses
|
||||
- Relations: Eager loading for common queries
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Technical Implementation Details
|
||||
|
||||
### Base Controller Structure
|
||||
|
||||
```typescript
|
||||
// File: backend/src/common/controllers/base.controller.ts
|
||||
export abstract class BaseController<T extends BaseEntity> {
|
||||
constructor(
|
||||
protected readonly service: BaseService<T>,
|
||||
protected readonly entityName: string
|
||||
) {}
|
||||
|
||||
@Get()
|
||||
@ApiResponse({ status: 200, description: `List ${entityName}` })
|
||||
async findAll(@Query() query: PaginationDto): Promise<PaginatedResponse<T>> {
|
||||
return this.service.findAll(query);
|
||||
}
|
||||
|
||||
@Get(':uuid')
|
||||
@ApiResponse({ status: 200, description: `Get ${entityName} by UUID` })
|
||||
async findOne(@Param('uuid') uuid: string): Promise<ApiResponse<T>> {
|
||||
return this.service.findOne(uuid);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@ApiResponse({ status: 201, description: `Create ${entityName}` })
|
||||
async create(@Body() dto: CreateDto): Promise<ApiResponse<T>> {
|
||||
return this.service.create(dto);
|
||||
}
|
||||
|
||||
// ... other standard methods
|
||||
}
|
||||
```
|
||||
|
||||
### Action Endpoint Pattern
|
||||
|
||||
```typescript
|
||||
// File: backend/src/modules/correspondence/correspondence.controller.ts
|
||||
@Controller('correspondences')
|
||||
@ApiTags('Correspondences')
|
||||
export class CorrespondenceController extends BaseController<Correspondence> {
|
||||
|
||||
@Post(':uuid/submit')
|
||||
@ActionEndpoint('submit', 'Submit correspondence for routing')
|
||||
@ApiResponse({ status: 200, description: 'Correspondence submitted successfully' })
|
||||
async submit(
|
||||
@Param('uuid') uuid: string,
|
||||
@Body() dto: SubmitCorrespondenceDto,
|
||||
@CurrentUser() user: User
|
||||
): Promise<ActionResponse> {
|
||||
return this.correspondenceService.submit(uuid, dto, user.id);
|
||||
}
|
||||
|
||||
@Post(':uuid/approve')
|
||||
@ActionEndpoint('approve', 'Approve correspondence')
|
||||
async approve(
|
||||
@Param('uuid') uuid: string,
|
||||
@Body() dto: ApproveCorrespondenceDto,
|
||||
@CurrentUser() user: User
|
||||
): Promise<ActionResponse> {
|
||||
return this.correspondenceService.approve(uuid, dto, user.id);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Frontend API Client
|
||||
|
||||
```typescript
|
||||
// File: frontend/lib/api/correspondence.ts
|
||||
export class CorrespondenceApi {
|
||||
private client = new ApiClient('/correspondences');
|
||||
|
||||
// REST operations
|
||||
async findAll(query?: CorrespondenceListQuery): Promise<PaginatedCorrespondences> {
|
||||
return this.client.get('', { params: query });
|
||||
}
|
||||
|
||||
async findOne(uuid: string): Promise<Correspondence> {
|
||||
return this.client.get(`/${uuid}`);
|
||||
}
|
||||
|
||||
async create(dto: CreateCorrespondenceDto): Promise<Correspondence> {
|
||||
return this.client.post('', dto);
|
||||
}
|
||||
|
||||
// Action operations
|
||||
async submit(uuid: string, dto: SubmitCorrespondenceDto): Promise<ActionResponse> {
|
||||
return this.client.post(`/${uuid}/submit`, dto);
|
||||
}
|
||||
|
||||
async approve(uuid: string, dto: ApproveCorrespondenceDto): Promise<ActionResponse> {
|
||||
return this.client.post(`/${uuid}/approve`, dto);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Success Criteria
|
||||
|
||||
### Functional Requirements
|
||||
- [ ] All correspondence endpoints follow hybrid pattern
|
||||
- [ ] RFA endpoints implemented with actions
|
||||
- [ ] Swagger documentation complete with examples
|
||||
- [ ] Frontend API client typed and tested
|
||||
- [ ] Integration tests cover all scenarios
|
||||
|
||||
### Non-Functional Requirements
|
||||
- [ ] API response times < 200ms (p90)
|
||||
- [ ] Error responses consistent and user-friendly
|
||||
- [ ] Documentation auto-generated and up-to-date
|
||||
- [ ] Type safety across frontend and backend
|
||||
- [ ] Rate limiting and security implemented
|
||||
|
||||
### Compliance Requirements
|
||||
- [ ] ADR-003 patterns followed consistently
|
||||
- [ ] ADR-019 UUID strategy implemented
|
||||
- [ ] ADR-007 error handling integrated
|
||||
- [ ] ADR-010 logging for all API calls
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Deployment & Rollout
|
||||
|
||||
### Phase 1: Backend Implementation (Week 1-2)
|
||||
- Implement base controller and patterns
|
||||
- Refactor correspondence API
|
||||
- Create comprehensive tests
|
||||
|
||||
### Phase 2: Frontend Integration (Week 3)
|
||||
- Update API client library
|
||||
- Implement error handling UI
|
||||
- Add integration tests
|
||||
|
||||
### Phase 3: Documentation & Monitoring (Week 4)
|
||||
- Complete Swagger documentation
|
||||
- Add API monitoring
|
||||
- Performance testing and optimization
|
||||
|
||||
---
|
||||
|
||||
## 📋 Dependencies & Prerequisites
|
||||
|
||||
### Must Have
|
||||
- ✅ ADR-003 Approved
|
||||
- ✅ ADR-007 Error Handling Strategy
|
||||
- ✅ Base infrastructure (NestJS, TypeORM, Redis)
|
||||
- ✅ Authentication system (JWT, CASL)
|
||||
|
||||
### Nice to Have
|
||||
- API analytics dashboard
|
||||
- Automated API testing pipeline
|
||||
- Client code generation
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Review & Acceptance
|
||||
|
||||
### Code Review Checklist
|
||||
- [ ] All endpoints follow hybrid pattern
|
||||
- [ ] DTOs properly validated
|
||||
- [ ] Error handling consistent
|
||||
- [ ] Documentation complete
|
||||
- [ ] Tests comprehensive
|
||||
- [ ] Performance optimized
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] API documentation available at `/api/docs`
|
||||
- [ ] All tests passing (>90% coverage)
|
||||
- [ ] Performance benchmarks met
|
||||
- [ ] Security scan passed
|
||||
- [ ] Frontend integration verified
|
||||
|
||||
---
|
||||
|
||||
**Owner:** Backend Team Lead
|
||||
**Estimated Effort:** 3-4 weeks (1 developer)
|
||||
**Risk Level:** Medium (API breaking changes)
|
||||
**Rollback Plan:** Version previous API endpoints alongside new ones
|
||||
@@ -0,0 +1,406 @@
|
||||
# Task BE-DB-01: Database Schema Strategy Implementation
|
||||
|
||||
**Phase:** Database Standardization & Optimization
|
||||
**ADR Compliance:** ADR-004 (Database Schema Design), ADR-009 (Migration Strategy), ADR-019 (UUID Strategy)
|
||||
**Priority:** 🟡 Important - Data Integrity & Performance
|
||||
|
||||
> **Context:** การ implement Selective Normalization Patterns ตาม ADR-004 เพื่อให้ database schema สอดคล้องกันและมีประสิทธิภาพ
|
||||
|
||||
---
|
||||
|
||||
## 📋 Implementation Tasks
|
||||
|
||||
### **DB-1.1: Base Entity Pattern Implementation**
|
||||
- [ ] **Create Base Entity Classes:**
|
||||
- File: `backend/src/common/entities/base.entity.ts`
|
||||
- Features: UUID, timestamps, soft delete, audit fields
|
||||
- Inheritance: All entities extend from base
|
||||
- [ ] **Create UuidBaseEntity (ADR-019):**
|
||||
- File: `backend/src/common/entities/uuid-base.entity.ts`
|
||||
- Properties: `id` (INT PK), `uuid` (UUID public), `createdAt`, `updatedAt`
|
||||
- Decorators: `@Exclude()` for internal ID, `@Expose()` for UUID
|
||||
- [ ] **Update All Entity Classes:**
|
||||
- Directory: `backend/src/modules/*/entities/`
|
||||
- Extend from base entities
|
||||
- Add proper decorators and relationships
|
||||
- Ensure UUID handling compliance
|
||||
|
||||
### **DB-1.2: Audit Trail Implementation**
|
||||
- [ ] **Create Audit Log Entity:**
|
||||
- File: `backend/src/common/entities/audit-log.entity.ts`
|
||||
- Table: `audit_logs`
|
||||
- Fields: `tableName`, `recordId`, `action`, `oldValues`, `newValues`, `userId`
|
||||
- Indexes: Performance optimization for queries
|
||||
- [ ] **Create Audit Trail Service:**
|
||||
- File: `backend/src/common/services/audit-trail.service.ts`
|
||||
- Methods: `logCreate()`, `logUpdate()`, `logDelete()`, `logSoftDelete()`
|
||||
- Features: Automatic change detection, JSON diff, user tracking
|
||||
- [ ] **Implement Audit Interceptor:**
|
||||
- File: `backend/src/common/interceptors/audit.interceptor.ts`
|
||||
- Trigger: Before/After database operations
|
||||
- Scope: All entity operations (CRUD)
|
||||
- Performance: Async logging to avoid blocking
|
||||
|
||||
### **DB-1.3: Workflow State Management**
|
||||
- [ ] **Create Workflow State Entities:**
|
||||
- File: `backend/src/modules/workflow/entities/`
|
||||
- Entities: `WorkflowState`, `WorkflowInstance`, `WorkflowHistory`
|
||||
- Relationships: State machine definitions and instances
|
||||
- [ ] **Implement State Transition Logic:**
|
||||
- File: `backend/src/modules/workflow/services/workflow-state.service.ts`
|
||||
- Methods: `validateTransition()`, `executeTransition()`, `getCurrentState()`
|
||||
- Features: Business rule validation, history tracking
|
||||
- [ ] **Create Workflow History Tables:**
|
||||
- Tables: `workflow_histories`, `correspondence_histories`, `rfa_histories`
|
||||
- Purpose: Track all state changes with context
|
||||
- Features: JSON snapshots, user attribution, timestamps
|
||||
|
||||
### **DB-1.4: Schema Optimization & Indexing**
|
||||
- [ ] **Analyze Current Query Patterns:**
|
||||
- Tools: MySQL slow query log, EXPLAIN plans
|
||||
- Focus: Correspondence list, RFA workflows, user permissions
|
||||
- Document: Current performance bottlenecks
|
||||
- [ ] **Implement Strategic Indexes:**
|
||||
- Foreign key indexes: All FK columns indexed
|
||||
- Composite indexes: Common query patterns
|
||||
- Partial indexes: Active records only (WHERE deleted_at IS NULL)
|
||||
- [ ] **Optimize Table Structures:**
|
||||
- Review: Data types, column sizes, NULL constraints
|
||||
- Normalize: Where beneficial for integrity
|
||||
- Denormalize: Where beneficial for performance
|
||||
|
||||
### **DB-1.5: Migration & Evolution Strategy**
|
||||
- [ ] **Create Migration Scripts:**
|
||||
- Directory: `backend/src/database/migrations/`
|
||||
- Format: SQL scripts (following ADR-009)
|
||||
- Features: Rollback capability, dependency tracking
|
||||
- [ ] **Implement Schema Validation:**
|
||||
- File: `backend/src/common/validators/schema.validator.ts`
|
||||
- Checks: Naming conventions, required fields, relationships
|
||||
- Automation: Pre-commit hooks, CI validation
|
||||
- [ ] **Create Database Documentation:**
|
||||
- Auto-generation: From TypeORM entities
|
||||
- Format: Markdown with ER diagrams
|
||||
- Include: Field descriptions, relationships, indexes
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Technical Implementation Details
|
||||
|
||||
### Base Entity Implementation
|
||||
|
||||
```typescript
|
||||
// File: backend/src/common/entities/base.entity.ts
|
||||
import { CreateDateColumn, UpdateDateColumn, DeleteDateColumn, Column } from 'typeorm';
|
||||
import { UuidBaseEntity } from './uuid-base.entity';
|
||||
|
||||
export abstract class BaseEntity extends UuidBaseEntity {
|
||||
@CreateDateColumn({
|
||||
type: 'timestamp',
|
||||
name: 'created_at',
|
||||
comment: 'Record creation time'
|
||||
})
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({
|
||||
type: 'timestamp',
|
||||
name: 'updated_at',
|
||||
comment: 'Last update time'
|
||||
})
|
||||
updatedAt: Date;
|
||||
|
||||
@DeleteDateColumn({
|
||||
type: 'datetime',
|
||||
name: 'deleted_at',
|
||||
nullable: true,
|
||||
comment: 'Soft delete timestamp'
|
||||
})
|
||||
deletedAt?: Date;
|
||||
|
||||
@Column({
|
||||
type: 'int',
|
||||
name: 'created_by',
|
||||
nullable: true,
|
||||
comment: 'User who created record'
|
||||
})
|
||||
createdBy?: number;
|
||||
|
||||
@Column({
|
||||
type: 'int',
|
||||
name: 'updated_by',
|
||||
nullable: true,
|
||||
comment: 'User who last updated record'
|
||||
})
|
||||
updatedBy?: number;
|
||||
}
|
||||
```
|
||||
|
||||
### UuidBaseEntity (ADR-019)
|
||||
|
||||
```typescript
|
||||
// File: backend/src/common/entities/uuid-base.entity.ts
|
||||
import { PrimaryGeneratedColumn, Column, Exclude, Expose } from 'typeorm';
|
||||
|
||||
export abstract class UuidBaseEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
@Exclude()
|
||||
id: number;
|
||||
|
||||
@Column({
|
||||
type: 'uuid',
|
||||
unique: true,
|
||||
name: 'uuid',
|
||||
comment: 'Public UUID identifier (ADR-019)'
|
||||
})
|
||||
@Expose({ name: 'id' }) // Expose UUID as 'id' in API
|
||||
uuid: string;
|
||||
}
|
||||
```
|
||||
|
||||
### Audit Trail Service
|
||||
|
||||
```typescript
|
||||
// File: backend/src/common/services/audit-trail.service.ts
|
||||
@Injectable()
|
||||
export class AuditTrailService {
|
||||
constructor(
|
||||
@InjectRepository(AuditLog)
|
||||
private auditLogRepo: Repository<AuditLog>
|
||||
) {}
|
||||
|
||||
async logCreate(entity: BaseEntity, userId: number): Promise<void> {
|
||||
const auditLog = this.auditLogRepo.create({
|
||||
tableName: entity.constructor.name,
|
||||
recordId: entity.id,
|
||||
recordUuid: entity.uuid,
|
||||
action: 'CREATE',
|
||||
newValues: entity,
|
||||
userId
|
||||
});
|
||||
await this.auditLogRepo.save(auditLog);
|
||||
}
|
||||
|
||||
async logUpdate(
|
||||
entity: BaseEntity,
|
||||
oldValues: any,
|
||||
newValues: any,
|
||||
userId: number
|
||||
): Promise<void> {
|
||||
const changedFields = this.getChangedFields(oldValues, newValues);
|
||||
|
||||
const auditLog = this.auditLogRepo.create({
|
||||
tableName: entity.constructor.name,
|
||||
recordId: entity.id,
|
||||
recordUuid: entity.uuid,
|
||||
action: 'UPDATE',
|
||||
oldValues,
|
||||
newValues,
|
||||
changedFields,
|
||||
userId
|
||||
});
|
||||
await this.auditLogRepo.save(auditLog);
|
||||
}
|
||||
|
||||
private getChangedFields(old: any, new: any): string[] {
|
||||
const changes: string[] = [];
|
||||
for (const key in new) {
|
||||
if (old[key] !== new[key]) {
|
||||
changes.push(key);
|
||||
}
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Audit Interceptor
|
||||
|
||||
```typescript
|
||||
// File: backend/src/common/interceptors/audit.interceptor.ts
|
||||
@Injectable()
|
||||
export class AuditInterceptor implements NestInterceptor {
|
||||
constructor(private auditService: AuditTrailService) {}
|
||||
|
||||
async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const userId = request.user?.id;
|
||||
|
||||
return next.handle().pipe(
|
||||
tap(async (result) => {
|
||||
// Log successful operations
|
||||
if (result && typeof result === 'object') {
|
||||
await this.auditService.logCreate(result, userId);
|
||||
}
|
||||
}),
|
||||
catchError(async (error) => {
|
||||
// Log failed operations
|
||||
console.error('Operation failed:', error);
|
||||
throw error;
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Workflow State Management
|
||||
|
||||
```typescript
|
||||
// File: backend/src/modules/workflow/entities/workflow-instance.entity.ts
|
||||
@Entity('workflow_instances')
|
||||
export class WorkflowInstance extends BaseEntity {
|
||||
@Column({ type: 'varchar', length: 50 })
|
||||
workflowCode: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 50 })
|
||||
entityType: string; // 'correspondence', 'rfa', etc.
|
||||
|
||||
@Column({ type: 'int' })
|
||||
entityId: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 50 })
|
||||
currentState: string;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: ['ACTIVE', 'COMPLETED', 'CANCELLED'],
|
||||
default: 'ACTIVE'
|
||||
})
|
||||
status: string;
|
||||
|
||||
@Column({ type: 'json', nullable: true })
|
||||
context: any; // Workflow-specific context
|
||||
|
||||
// Relationships
|
||||
@ManyToOne(() => WorkflowState)
|
||||
@JoinColumn({
|
||||
name: 'workflow_code',
|
||||
referencedColumnName: 'workflow_code',
|
||||
referencedColumnName: 'state_name'
|
||||
})
|
||||
currentStateDefinition: WorkflowState;
|
||||
|
||||
@OneToMany(() => WorkflowHistory, history => history.instance)
|
||||
histories: WorkflowHistory[];
|
||||
}
|
||||
```
|
||||
|
||||
### Schema Migration Example
|
||||
|
||||
```sql
|
||||
-- File: backend/src/database/migrations/001_add_audit_trail.sql
|
||||
|
||||
-- Create audit log table
|
||||
CREATE TABLE audit_logs (
|
||||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||||
table_name VARCHAR(50) NOT NULL,
|
||||
record_id INT NOT NULL,
|
||||
record_uuid UUID NULL,
|
||||
action ENUM('CREATE', 'UPDATE', 'DELETE', 'SOFT_DELETE') NOT NULL,
|
||||
old_values JSON NULL,
|
||||
new_values JSON NULL,
|
||||
changed_fields JSON NULL,
|
||||
user_id INT NULL,
|
||||
ip_address VARCHAR(45) NULL,
|
||||
user_agent TEXT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
INDEX idx_audit_table_record (table_name, record_id),
|
||||
INDEX idx_audit_user (user_id, created_at),
|
||||
INDEX idx_audit_created (created_at),
|
||||
INDEX idx_audit_record_uuid (record_uuid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||||
COMMENT='Audit trail for all data changes';
|
||||
|
||||
-- Add base columns to existing tables (example)
|
||||
ALTER TABLE correspondences
|
||||
ADD COLUMN created_by INT NULL COMMENT 'User who created record',
|
||||
ADD COLUMN updated_by INT NULL COMMENT 'User who last updated record',
|
||||
ADD INDEX idx_correspondences_created_by (created_by),
|
||||
ADD INDEX idx_correspondences_updated_by (updated_by);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Success Criteria
|
||||
|
||||
### Functional Requirements
|
||||
- [ ] All entities extend from base classes
|
||||
- [ ] Audit trail captures all data changes
|
||||
- [ ] Workflow state management implemented
|
||||
- [ ] Database performance optimized
|
||||
- [ ] Migration strategy in place
|
||||
|
||||
### Non-Functional Requirements
|
||||
- [ ] Query response times < 200ms (p90)
|
||||
- [ ] Audit logging doesn't impact performance
|
||||
- [ ] Schema validation automated
|
||||
- [ ] Documentation auto-generated
|
||||
- [ ] Zero data loss during migrations
|
||||
|
||||
### Compliance Requirements
|
||||
- [ ] ADR-004 patterns followed consistently
|
||||
- [ ] ADR-009 migration strategy implemented
|
||||
- [ ] ADR-019 UUID strategy enforced
|
||||
- [ ] Audit trail meets compliance requirements
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Deployment & Rollout
|
||||
|
||||
### Phase 1: Base Pattern Implementation (Week 1)
|
||||
- Create base entity classes
|
||||
- Update core entities (users, organizations, projects)
|
||||
- Implement audit trail foundation
|
||||
|
||||
### Phase 2: Workflow & Audit Implementation (Week 2-3)
|
||||
- Implement workflow state management
|
||||
- Add audit logging to all modules
|
||||
- Create migration scripts
|
||||
|
||||
### Phase 3: Optimization & Documentation (Week 4)
|
||||
- Performance optimization and indexing
|
||||
- Schema validation automation
|
||||
- Documentation generation
|
||||
|
||||
---
|
||||
|
||||
## 📋 Dependencies & Prerequisites
|
||||
|
||||
### Must Have
|
||||
- ✅ ADR-004 Approved
|
||||
- ✅ ADR-009 Migration Strategy
|
||||
- ✅ ADR-019 UUID Strategy
|
||||
- ✅ TypeORM configuration
|
||||
- ✅ Database backup strategy
|
||||
|
||||
### Nice to Have
|
||||
- Database performance monitoring
|
||||
- Automated schema testing
|
||||
- Visual ER diagram generation
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Review & Acceptance
|
||||
|
||||
### Code Review Checklist
|
||||
- [ ] All entities follow base pattern
|
||||
- [ ] Audit logging comprehensive
|
||||
- [ ] Workflow state correct
|
||||
- [ ] Indexes optimized
|
||||
- [ ] Migration scripts safe
|
||||
- [ ] Documentation complete
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] All entities extend base classes
|
||||
- [ ] Audit trail captures 100% of changes
|
||||
- [ ] Performance benchmarks met
|
||||
- [ ] Migration scripts tested
|
||||
- [ ] Schema validation passes
|
||||
- [ ] Documentation generated
|
||||
|
||||
---
|
||||
|
||||
**Owner:** Backend Team Lead + DBA
|
||||
**Estimated Effort:** 4 weeks (1-2 developers)
|
||||
**Risk Level:** High (Database schema changes)
|
||||
**Rollback Plan:** Full database backups, migration rollback scripts
|
||||
@@ -0,0 +1,466 @@
|
||||
# Task BE-ERR-01: Error Handling & Recovery Strategy Implementation
|
||||
|
||||
**Phase:** Error Handling Standardization
|
||||
**ADR Compliance:** ADR-007 (Error Handling & Recovery), ADR-010 (Logging & Monitoring)
|
||||
**Priority:** 🟡 Important - User Experience & Debuggability
|
||||
|
||||
> **Context:** การ implement Layered Error Handling ตาม ADR-007 เพื่อให้ error messages สอดคล้องกันและมีประโยชน์ต่อ users
|
||||
|
||||
---
|
||||
|
||||
## 📋 Implementation Tasks
|
||||
|
||||
### **ERR-1.1: Exception Hierarchy Implementation**
|
||||
- [ ] **Create Base Exception Classes:**
|
||||
- File: `backend/src/common/exceptions/base.exception.ts`
|
||||
- Classes: `BaseException`, `ValidationException`, `BusinessException`
|
||||
- Features: Error classification, user messages, recovery actions
|
||||
- [ ] **Create Specific Exception Types:**
|
||||
- File: `backend/src/common/exceptions/`
|
||||
- Types: `PermissionException`, `SystemException`, `WorkflowException`
|
||||
- Properties: Error codes, severity levels, recovery guidance
|
||||
- [ ] **Implement Error Severity System:**
|
||||
- Enum: `ErrorSeverity` (LOW, MEDIUM, HIGH, CRITICAL)
|
||||
- Logic: Different handling based on severity
|
||||
- Integration: Logging levels and alerting
|
||||
|
||||
### **ERR-1.2: Global Exception Filter**
|
||||
- [ ] **Create Global Exception Filter:**
|
||||
- File: `backend/src/common/filters/global-exception.filter.ts`
|
||||
- Features: Layered error processing, information control
|
||||
- Integration: NestJS exception handling pipeline
|
||||
- [ ] **Implement Error Classification:**
|
||||
- Logic: Categorize errors by type and severity
|
||||
- Response format: Standardized error responses
|
||||
- Development vs Production: Different detail levels
|
||||
- [ ] **Add Error Logging Integration:**
|
||||
- Integration: ADR-010 logging strategy
|
||||
- Context: Request information, user details, stack traces
|
||||
- Performance: Async logging to avoid blocking
|
||||
|
||||
### **ERR-1.3: Service Layer Error Handling**
|
||||
- [ ] **Update Correspondence Service:**
|
||||
- File: `backend/src/modules/correspondence/correspondence.service.ts`
|
||||
- Methods: Use custom exceptions instead of generic errors
|
||||
- Validation: Business rule validation with proper errors
|
||||
- Recovery: Provide actionable error messages
|
||||
- [ ] **Update RFA Service:**
|
||||
- File: `backend/src/modules/rfa/rfa.service.ts`
|
||||
- Workflow errors: Invalid transitions, permission issues
|
||||
- Document numbering: Duplicate numbers, format errors
|
||||
- Integration: Workflow engine error handling
|
||||
- [ ] **Update All Other Services:**
|
||||
- Directory: `backend/src/modules/*/services/`
|
||||
- Pattern: Consistent error handling across all modules
|
||||
- Validation: Input validation with detailed errors
|
||||
|
||||
### **ERR-1.4: Frontend Error Handling**
|
||||
- [ ] **Create Error Display Component:**
|
||||
- File: `frontend/components/common/error-display.tsx`
|
||||
- Features: User-friendly error messages, recovery actions
|
||||
- Styling: Consistent with design system
|
||||
- Localization: Support for Thai/English messages
|
||||
- [ ] **Update API Client Error Handling:**
|
||||
- File: `frontend/lib/api/client.ts`
|
||||
- Logic: Parse structured error responses
|
||||
- Actions: Display appropriate recovery options
|
||||
- Integration: Global error state management
|
||||
- [ ] **Implement Error Recovery UI:**
|
||||
- Features: Retry buttons, alternative actions, help links
|
||||
- Context: Different recovery flows per error type
|
||||
- User Experience: Clear guidance and next steps
|
||||
|
||||
### **ERR-1.5: Error Catalog & Documentation**
|
||||
- [ ] **Create Error Catalog:**
|
||||
- File: `docs/error-catalog.md`
|
||||
- Content: All error codes, messages, recovery actions
|
||||
- Format: Structured table with examples
|
||||
- Maintenance: Process for adding new errors
|
||||
- [ ] **Implement Error Analytics:**
|
||||
- Tracking: Error rates, patterns, user impact
|
||||
- Dashboard: Error monitoring and alerting
|
||||
- Integration: ADR-010 monitoring strategy
|
||||
- [ ] **Create Error Handling Guidelines:**
|
||||
- Document: Developer guidelines for error handling
|
||||
- Examples: Common patterns and best practices
|
||||
- Review: Code review checklist for errors
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Technical Implementation Details
|
||||
|
||||
### Exception Hierarchy
|
||||
|
||||
```typescript
|
||||
// File: backend/src/common/exceptions/base.exception.ts
|
||||
export enum ErrorType {
|
||||
VALIDATION = 'VALIDATION',
|
||||
BUSINESS_RULE = 'BUSINESS_RULE',
|
||||
PERMISSION_DENIED = 'PERMISSION_DENIED',
|
||||
NOT_FOUND = 'NOT_FOUND',
|
||||
CONFLICT = 'CONFLICT',
|
||||
INTERNAL_ERROR = 'INTERNAL_ERROR',
|
||||
DATABASE_ERROR = 'DATABASE_ERROR',
|
||||
EXTERNAL_SERVICE = 'EXTERNAL_SERVICE',
|
||||
INFRASTRUCTURE = 'INFRASTRUCTURE'
|
||||
}
|
||||
|
||||
export enum ErrorSeverity {
|
||||
LOW = 'LOW', // User mistake, easy recovery
|
||||
MEDIUM = 'MEDIUM', // Business rule violation, needs action
|
||||
HIGH = 'HIGH', // System issue, may need support
|
||||
CRITICAL = 'CRITICAL' // System failure, immediate attention
|
||||
}
|
||||
|
||||
export abstract class BaseException extends HttpException {
|
||||
constructor(
|
||||
public readonly type: ErrorType,
|
||||
public readonly code: string,
|
||||
public readonly message: string,
|
||||
public readonly userMessage?: string,
|
||||
public readonly severity: ErrorSeverity = ErrorSeverity.MEDIUM,
|
||||
public readonly details?: any,
|
||||
public readonly recoveryActions?: string[]
|
||||
) {
|
||||
super(
|
||||
{
|
||||
error: {
|
||||
type,
|
||||
code,
|
||||
message: userMessage || message,
|
||||
severity,
|
||||
timestamp: new Date().toISOString(),
|
||||
...(recoveryActions && { recoveryActions }),
|
||||
...(process.env.NODE_ENV === 'development' && {
|
||||
technicalMessage: message,
|
||||
details
|
||||
})
|
||||
}
|
||||
},
|
||||
getStatusCode(type)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class ValidationException extends BaseException {
|
||||
constructor(message: string, details?: ValidationErrorDetail[]) {
|
||||
super(
|
||||
ErrorType.VALIDATION,
|
||||
'VALIDATION_ERROR',
|
||||
message,
|
||||
'ข้อมูลที่กรอกไม่ถูกต้อง กรุณาตรวจสอบและลองใหม่',
|
||||
ErrorSeverity.LOW,
|
||||
details,
|
||||
['ตรวจสอบข้อมูลที่กรอก', 'แก้ไขข้อมูลที่ผิดพลาด', 'ลองใหม่อีกครั้ง']
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Global Exception Filter
|
||||
|
||||
```typescript
|
||||
// File: backend/src/common/filters/global-exception.filter.ts
|
||||
@Injectable()
|
||||
export class GlobalExceptionFilter implements ExceptionFilter {
|
||||
private readonly logger = new Logger(GlobalExceptionFilter.name);
|
||||
|
||||
catch(exception: unknown, host: ArgumentsHost) {
|
||||
const ctx = host.switchToHttp();
|
||||
const response = ctx.getResponse();
|
||||
const request = ctx.getRequest();
|
||||
|
||||
let errorResponse: any;
|
||||
|
||||
if (exception instanceof BaseException) {
|
||||
// Handle our custom exceptions
|
||||
errorResponse = exception.getResponse();
|
||||
this.logError(exception, request, false);
|
||||
} else if (exception instanceof HttpException) {
|
||||
// Handle NestJS HTTP exceptions
|
||||
const status = exception.getStatus();
|
||||
const exceptionResponse = exception.getResponse();
|
||||
|
||||
errorResponse = {
|
||||
error: {
|
||||
type: this.getErrorType(status),
|
||||
code: 'HTTP_ERROR',
|
||||
message: this.getUserMessage(status),
|
||||
severity: ErrorSeverity.MEDIUM,
|
||||
timestamp: new Date().toISOString(),
|
||||
...(process.env.NODE_ENV === 'development' && {
|
||||
technicalMessage: exception.message,
|
||||
details: exceptionResponse
|
||||
})
|
||||
}
|
||||
};
|
||||
this.logError(exception, request, false);
|
||||
} else {
|
||||
// Handle unexpected errors
|
||||
errorResponse = {
|
||||
error: {
|
||||
type: ErrorType.INTERNAL_ERROR,
|
||||
code: 'UNEXPECTED_ERROR',
|
||||
message: 'เกิดข้อผิดพลาดที่ไม่คาดคิด กรุณาลองใหม่ภายหลัง',
|
||||
severity: ErrorSeverity.CRITICAL,
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
};
|
||||
this.logError(exception, request, true);
|
||||
}
|
||||
|
||||
response.status(errorResponse.error.statusCode || 500).json(errorResponse);
|
||||
}
|
||||
|
||||
private logError(exception: any, request: Request, isCritical: boolean) {
|
||||
const logData = {
|
||||
path: request.url,
|
||||
method: request.method,
|
||||
userId: request.user?.id,
|
||||
ip: request.ip,
|
||||
userAgent: request.headers['user-agent'],
|
||||
exception: {
|
||||
name: exception.name,
|
||||
message: exception.message,
|
||||
stack: exception.stack,
|
||||
details: exception.details
|
||||
}
|
||||
};
|
||||
|
||||
if (isCritical || exception.severity === ErrorSeverity.CRITICAL) {
|
||||
this.logger.error('Critical error occurred', logData);
|
||||
} else {
|
||||
this.logger.warn('Error occurred', logData);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Service Layer Example
|
||||
|
||||
```typescript
|
||||
// File: backend/src/modules/correspondence/correspondence.service.ts
|
||||
@Injectable()
|
||||
export class CorrespondenceService {
|
||||
async create(createDto: CreateCorrespondenceDto, userId: number): Promise<Correspondence> {
|
||||
try {
|
||||
// Business validation
|
||||
if (createDto.originatorId && !await this.canUserCreateForOrganization(userId, createDto.originatorId)) {
|
||||
throw new PermissionException('correspondence', 'create for organization');
|
||||
}
|
||||
|
||||
// Check for duplicate document number
|
||||
if (await this.isDuplicateDocumentNumber(createDto.documentNumber)) {
|
||||
throw new BusinessException(
|
||||
'DUPLICATE_DOCUMENT_NUMBER',
|
||||
`Document number ${createDto.documentNumber} already exists`,
|
||||
'เลขที่เอกสารนี้มีอยู่แล้ว กรุณาใช้เลขที่อื่น',
|
||||
['ตรวจสอบเลขที่เอกสารล่าสุด', 'ขอเลขที่เอกสารใหม่']
|
||||
);
|
||||
}
|
||||
|
||||
// Create correspondence
|
||||
const correspondence = this.correspondenceRepo.create({
|
||||
...createDto,
|
||||
createdBy: userId,
|
||||
createdAt: new Date()
|
||||
});
|
||||
|
||||
const saved = await this.correspondenceRepo.save(correspondence);
|
||||
|
||||
this.logger.log(`Correspondence created: ${saved.id}`);
|
||||
return saved;
|
||||
|
||||
} catch (error) {
|
||||
if (error instanceof BaseException) {
|
||||
throw error; // Re-throw our custom exceptions
|
||||
}
|
||||
|
||||
// Handle database errors
|
||||
if (error.code === 'ER_DUP_ENTRY') {
|
||||
throw new BusinessException(
|
||||
'DUPLICATE_ENTRY',
|
||||
'Database constraint violation',
|
||||
'ข้อมูลซ้ำกันในระบบ กรุณาตรวจสอบ'
|
||||
);
|
||||
}
|
||||
|
||||
// Handle unexpected errors
|
||||
this.logger.error('Unexpected error in CorrespondenceService.create', error);
|
||||
throw new SystemException('Failed to create correspondence', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Frontend Error Display Component
|
||||
|
||||
```typescript
|
||||
// File: frontend/components/common/error-display.tsx
|
||||
interface ErrorResponse {
|
||||
error: {
|
||||
type: string;
|
||||
code: string;
|
||||
message: string;
|
||||
severity: string;
|
||||
timestamp: string;
|
||||
recoveryActions?: string[];
|
||||
technicalMessage?: string;
|
||||
details?: any;
|
||||
};
|
||||
}
|
||||
|
||||
export function ErrorDisplay({ error, onRetry }: { error: ErrorResponse; onRetry?: () => void }) {
|
||||
const getSeverityColor = (severity: string) => {
|
||||
switch (severity) {
|
||||
case 'LOW': return 'text-yellow-600';
|
||||
case 'MEDIUM': return 'text-orange-600';
|
||||
case 'HIGH': return 'text-red-600';
|
||||
case 'CRITICAL': return 'text-red-800';
|
||||
default: return 'text-gray-600';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border border-red-200 bg-red-50 p-4">
|
||||
<div className="flex items-center">
|
||||
<div className="flex-shrink-0">
|
||||
<ExclamationTriangleIcon className="h-5 w-5 text-red-400" />
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<h3 className={`text-sm font-medium ${getSeverityColor(error.error.severity)}`}>
|
||||
{error.error.message}
|
||||
</h3>
|
||||
|
||||
{error.error.recoveryActions && (
|
||||
<div className="mt-2 text-sm text-gray-600">
|
||||
<p className="font-medium">วิธีแก้ไข:</p>
|
||||
<ul className="list-disc list-inside space-y-1">
|
||||
{error.error.recoveryActions.map((action, index) => (
|
||||
<li key={index}>{action}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-3 flex space-x-3">
|
||||
{onRetry && (
|
||||
<button
|
||||
onClick={onRetry}
|
||||
className="rounded bg-blue-600 px-3 py-1 text-sm text-white hover:bg-blue-700"
|
||||
>
|
||||
ลองใหม่
|
||||
</button>
|
||||
)}
|
||||
<button className="rounded bg-gray-600 px-3 py-1 text-sm text-white hover:bg-gray-700">
|
||||
ติดต่อผู้ดูแลระบบ
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Error Catalog Structure
|
||||
|
||||
```markdown
|
||||
# Error Catalog
|
||||
|
||||
| Error Code | Type | User Message | Recovery Actions | Severity | Module |
|
||||
|------------|------|--------------|------------------|----------|--------|
|
||||
| VALIDATION_ERROR | Validation | ข้อมูลที่กรอกไม่ถูกต้อง | ตรวจสอบข้อมูล, แก้ไข, ลองใหม่ | LOW | All |
|
||||
| DUPLICATE_DOCUMENT_NUMBER | Business | เลขที่เอกสารซ้ำกัน | ตรวจสอบเลขล่าสุด, ขอเลขใหม่ | MEDIUM | Correspondence |
|
||||
| CORRESPONDENCE_NOT_FOUND | Business | ไม่พบเอกสาร | ตรวจสอบ UUID, ค้นหาใหม่ | MEDIUM | Correspondence |
|
||||
| PERMISSION_DENIED | Permission | ไม่มีสิทธิ์ดำเนินการ | ติดต่อ admin, ใช้บัญชีอื่น | MEDIUM | All |
|
||||
| WORKFLOW_INVALID_TRANSITION | Business | ไม่สามารถดำเนินการได้ในสถานะปัจจุบัน | ตรวจสอบ workflow, ดำเนินการอื่น | MEDIUM | Workflow |
|
||||
| INTERNAL_ERROR | System | เกิดข้อผิดพลาดในระบบ | ลองใหม่, ติดต่อ admin | HIGH | All |
|
||||
| DATABASE_ERROR | System | ฐานข้อมูลมีปัญหา | ลองใหม่ภายหลัง, แจ้ง admin | HIGH | All |
|
||||
| EXTERNAL_SERVICE | System | บริการภายนอกมีปัญหา | ลองใหม่ภายหลัง | MEDIUM | Notification |
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Success Criteria
|
||||
|
||||
### Functional Requirements
|
||||
- [ ] Exception hierarchy implemented
|
||||
- [ ] Global exception filter working
|
||||
- [ ] All services use custom exceptions
|
||||
- [ ] Frontend displays user-friendly errors
|
||||
- [ ] Error catalog complete
|
||||
|
||||
### Non-Functional Requirements
|
||||
- [ ] Error responses consistent across all endpoints
|
||||
- [ ] Error logging integrated with ADR-010
|
||||
- [ ] User recovery guidance actionable
|
||||
- [ ] Technical details controlled in production
|
||||
- [ ] Error analytics and monitoring
|
||||
|
||||
### Compliance Requirements
|
||||
- [ ] ADR-007 patterns followed consistently
|
||||
- [ ] ADR-010 logging integrated
|
||||
- [ ] Security: No information leakage
|
||||
- [ ] Accessibility: Error messages understandable
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Deployment & Rollout
|
||||
|
||||
### Phase 1: Backend Implementation (Week 1)
|
||||
- Create exception hierarchy
|
||||
- Implement global filter
|
||||
- Update core services
|
||||
|
||||
### Phase 2: Service Updates (Week 2)
|
||||
- Update all service methods
|
||||
- Add comprehensive error handling
|
||||
- Create error catalog
|
||||
|
||||
### Phase 3: Frontend Integration (Week 3)
|
||||
- Update error display components
|
||||
- Implement recovery UI
|
||||
- Add error analytics
|
||||
|
||||
---
|
||||
|
||||
## 📋 Dependencies & Prerequisites
|
||||
|
||||
### Must Have
|
||||
- ✅ ADR-007 Approved
|
||||
- ✅ ADR-010 Logging Strategy
|
||||
- ✅ NestJS framework setup
|
||||
- ✅ Frontend error state management
|
||||
|
||||
### Nice to Have
|
||||
- Error monitoring dashboard
|
||||
- Automated error testing
|
||||
- Error localization framework
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Review & Acceptance
|
||||
|
||||
### Code Review Checklist
|
||||
- [ ] Exception hierarchy comprehensive
|
||||
- [ ] Global filter handles all cases
|
||||
- [ ] Services use proper exceptions
|
||||
- [ ] Frontend error handling user-friendly
|
||||
- [ ] Error catalog complete
|
||||
- [ ] No information leakage
|
||||
|
||||
### Acceptance Criteria
|
||||
- [ ] All errors use custom exceptions
|
||||
- [ ] Error responses standardized
|
||||
- [ ] User recovery guidance clear
|
||||
- [ ] Technical details controlled
|
||||
- [ ] Error logging comprehensive
|
||||
- [ ] Frontend error handling tested
|
||||
|
||||
---
|
||||
|
||||
**Owner:** Backend Team Lead + Frontend Team Lead
|
||||
**Estimated Effort:** 3 weeks (1-2 developers)
|
||||
**Risk Level:** Low (Improvement, no breaking changes)
|
||||
**Rollback Plan:** Revert to previous error handling
|
||||
Reference in New Issue
Block a user