# Inquiries Module - Chỉ Mục File Đầy Đủ

**Tạo lúc:** Ngày 11 tháng 4 năm 2026  
**Module:** `apps/api/src/modules/inquiries/`  
**Tổng số file:** 25  

---

## 📋 DANH SÁCH FILE ĐẦY ĐỦ THEO ĐƯỜNG DẪN

### **1. TẦNG ỨNG DỤNG (APPLICATION LAYER)**

#### Commands (4 files)

```
📄 apps/api/src/modules/inquiries/application/commands/create-inquiry/create-inquiry.command.ts
   Type: Command (CQRS)
   Purpose: Input DTO cho use case tạo yêu cầu tư vấn
   Exports: CreateInquiryCommand class
   Properties: userId, listingId, message, phone
   Lines: ~8
```

```
📄 apps/api/src/modules/inquiries/application/commands/create-inquiry/create-inquiry.handler.ts
   Type: Command Handler (CQRS)
   Purpose: Điều phối quá trình tạo yêu cầu tư vấn
   Implements: ICommandHandler<CreateInquiryCommand>
   Returns: CreateInquiryResult { id, listingId, createdAt }
   Key Logic:
     • Xác thực tin đăng tồn tại (Prisma.listing.findUnique)
     • Tạo InquiryEntity qua phương thức factory
     • Lưu vào repository
     • Phát sự kiện InquiryCreatedEvent qua EventBus
   Lines: ~60
   Dependencies: 
     • INQUIRY_REPOSITORY (injected)
     • EventBus (injected)
     • PrismaService (injected)
     • LoggerService (injected)
```

```
📄 apps/api/src/modules/inquiries/application/commands/mark-inquiry-read/mark-inquiry-read.command.ts
   Type: Command (CQRS)
   Purpose: Input DTO để đánh dấu yêu cầu tư vấn đã đọc
   Exports: MarkInquiryReadCommand class
   Properties: inquiryId, agentUserId
   Lines: ~6
```

```
📄 apps/api/src/modules/inquiries/application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts
   Type: Command Handler (CQRS)
   Purpose: Điều phối thao tác đánh dấu đã đọc
   Implements: ICommandHandler<MarkInquiryReadCommand>
   Returns: void (không có giá trị trả về)
   Key Logic:
     • Tải entity yêu cầu tư vấn từ repository
     • Tải tin đăng và xác minh quyền sở hữu của môi giới
     • Gọi inquiry.markAsRead() để cập nhật trạng thái
     • Lưu qua repository.markAsRead()
     • Phát sự kiện InquiryReadEvent qua EventBus
   Authorization Checks:
     • Yêu cầu tư vấn tồn tại
     • Tin đăng tồn tại
     • Người dùng đã đăng ký làm môi giới
     • ID môi giới khớp với listing.agentId
   Lines: ~50
   Dependencies:
     • INQUIRY_REPOSITORY (injected)
     • EventBus (injected)
     • PrismaService (injected)
     • LoggerService (injected)
```

#### Queries (4 files)

```
📄 apps/api/src/modules/inquiries/application/queries/get-inquiries-by-agent/get-inquiries-by-agent.query.ts
   Type: Query (CQRS)
   Purpose: Input DTO để lấy danh sách yêu cầu tư vấn của môi giới
   Exports: GetInquiriesByAgentQuery class
   Properties: agentUserId, page, limit
   Lines: ~6
```

```
📄 apps/api/src/modules/inquiries/application/queries/get-inquiries-by-agent/get-inquiries-by-agent.handler.ts
   Type: Query Handler (CQRS)
   Purpose: Phân giải danh sách yêu cầu tư vấn phân trang cho một môi giới
   Implements: IQueryHandler<GetInquiriesByAgentQuery>
   Returns: PaginatedResult<InquiryReadDto>
   Key Logic:
     • Phân giải ID môi giới từ userId qua Prisma
     • Ném NotFoundException nếu người dùng không phải môi giới
     • Ủy quyền cho repository.findByAgent()
   Lines: ~30
   Dependencies:
     • INQUIRY_REPOSITORY (injected)
     • PrismaService (injected)
```

```
📄 apps/api/src/modules/inquiries/application/queries/get-inquiries-by-listing/get-inquiries-by-listing.query.ts
   Type: Query (CQRS)
   Purpose: Input DTO để lấy danh sách yêu cầu tư vấn của tin đăng
   Exports: GetInquiriesByListingQuery class
   Properties: listingId, page, limit
   Lines: ~6
```

```
📄 apps/api/src/modules/inquiries/application/queries/get-inquiries-by-listing/get-inquiries-by-listing.handler.ts
   Type: Query Handler (CQRS)
   Purpose: Phân giải danh sách yêu cầu tư vấn phân trang cho một tin đăng
   Implements: IQueryHandler<GetInquiriesByListingQuery>
   Returns: PaginatedResult<InquiryReadDto>
   Key Logic:
     • Ủy quyền trực tiếp cho repository.findByListing()
   Lines: ~20
   Dependencies:
     • INQUIRY_REPOSITORY (injected)
```

#### Application Tests (4 files)

```
📄 apps/api/src/modules/inquiries/application/__tests__/create-inquiry.handler.spec.ts
   Type: Unit Tests (Jest/Vitest)
   Test Framework: Vitest với hàm Mock (vi.fn)
   Test Count: 4
   Tests:
     ✓ tạo yêu cầu tư vấn thành công
     ✓ ném NotFoundException khi không tìm thấy tin đăng
     ✓ phát sự kiện domain sau khi lưu
   Mocks:
     • mockInquiryRepo (IInquiryRepository implementation)
     • mockEventBus
     • mockPrisma.listing.findUnique
   Lines: ~98
```

```
📄 apps/api/src/modules/inquiries/application/__tests__/mark-inquiry-read.handler.spec.ts
   Type: Unit Tests (Jest/Vitest)
   Test Count: 5
   Tests:
     ✓ đánh dấu yêu cầu tư vấn là đã đọc thành công
     ✓ ném NotFoundException khi không tìm thấy yêu cầu tư vấn
     ✓ ném NotFoundException khi không tìm thấy tin đăng
     ✓ ném ForbiddenException khi người dùng không phải môi giới của tin đăng
     ✓ ném ForbiddenException khi không tìm thấy môi giới cho người dùng
   Mocks:
     • mockInquiryRepo
     • mockEventBus
     • mockPrisma.listing.findUnique
     • mockPrisma.agent.findUnique
   Lines: ~130
```

```
📄 apps/api/src/modules/inquiries/application/__tests__/get-inquiries-by-listing.handler.spec.ts
   Type: Unit Tests (Jest/Vitest)
   Test Count: 2
   Tests:
     ✓ trả về kết quả phân trang
     ✓ trả về dữ liệu rỗng khi không tìm thấy yêu cầu tư vấn nào
   Mocks:
     • mockInquiryRepo.findByListing
   Lines: ~69
```

```
📄 apps/api/src/modules/inquiries/application/__tests__/get-inquiries-by-agent.handler.spec.ts
   Type: Unit Tests (Jest/Vitest)
   Test Count: 2
   Tests:
     ✓ trả về kết quả phân trang
     ✓ ném NotFoundException khi không tìm thấy môi giới cho người dùng
   Mocks:
     • mockInquiryRepo.findByAgent
     • mockPrisma.agent.findUnique
   Lines: ~77
```

---

### **2. TẦNG DOMAIN (DOMAIN LAYER)**

#### Entities (1 file)

```
📄 apps/api/src/modules/inquiries/domain/entities/inquiry.entity.ts
   Type: Aggregate Root (DDD)
   Extends: AggregateRoot<string>
   Purpose: Logic nghiệp vụ cốt lõi cho yêu cầu tư vấn
   
   Properties (Private):
     • _listingId: string
     • _userId: string
     • _message: string
     • _phone: string | null
     • _isRead: boolean
   
   Getters (Read-only):
     • listingId: string
     • userId: string
     • message: string
     • phone: string | null
     • isRead: boolean
   
   Factory Method:
     • static createNew(id, listingId, userId, message, phone): InquiryEntity
       - Tạo yêu cầu tư vấn mới với isRead=false
       - Phát InquiryCreatedEvent
       - Trả về instance của entity
   
   Domain Methods:
     • markAsRead(): void
       - Đặt _isRead thành true
       - Phát InquiryReadEvent
   
   Lines: ~63
   Dependencies:
     • AggregateRoot from @modules/shared
     • InquiryCreatedEvent
     • InquiryReadEvent
```

#### Events (2 files)

```
📄 apps/api/src/modules/inquiries/domain/events/inquiry-created.event.ts
   Type: Domain Event (DDD)
   Implements: DomainEvent interface
   Purpose: Báo hiệu rằng một yêu cầu tư vấn đã được tạo
   
   Properties:
     • eventName: string = 'inquiry.created'
     • occurredAt: Date = new Date()
     • aggregateId: string
     • listingId: string
     • userId: string
   
   Lines: ~13
```

```
📄 apps/api/src/modules/inquiries/domain/events/inquiry-read.event.ts
   Type: Domain Event (DDD)
   Implements: DomainEvent interface
   Purpose: Báo hiệu rằng một yêu cầu tư vấn đã được đánh dấu là đã đọc
   
   Properties:
     • eventName: string = 'inquiry.read'
     • occurredAt: Date = new Date()
     • aggregateId: string
     • listingId: string
     • userId: string
   
   Lines: ~13
```

#### Repositories (2 files)

```
📄 apps/api/src/modules/inquiries/domain/repositories/inquiry.repository.ts
   Type: Repository Interface + Symbol (DDD)
   Purpose: Định nghĩa hợp đồng persistence
   
   Exports:
     • INQUIRY_REPOSITORY: Symbol (DI token)
     • IInquiryRepository: Interface
     • PaginatedResult<T>: Interface
   
   IInquiryRepository Methods:
     • findById(id: string): Promise<InquiryEntity | null>
     • save(inquiry: InquiryEntity): Promise<void>
     • markAsRead(id: string): Promise<void>
     • findByListing(listingId, page, limit): Promise<PaginatedResult<InquiryReadDto>>
     • findByAgent(agentId, page, limit): Promise<PaginatedResult<InquiryReadDto>>
     • countUnreadByAgent(agentId): Promise<number>
   
   PaginatedResult<T> Interface:
     • data: T[]
     • total: number
     • page: number
     • limit: number
     • totalPages: number
   
   Lines: ~22
```

```
📄 apps/api/src/modules/inquiries/domain/repositories/inquiry-read.dto.ts
   Type: Data Transfer Object (Read Model)
   Purpose: DTO cho kết quả truy vấn
   Exports: InquiryReadDto interface
   
   InquiryReadDto Properties:
     • id: string
     • listingId: string
     • listingTitle: string
     • userId: string
     • userName: string
     • userPhone: string
     • message: string
     • phone: string | null
     • isRead: boolean
     • createdAt: string
   
   Lines: ~13
```

#### Domain Tests (1 file)

```
📄 apps/api/src/modules/inquiries/domain/__tests__/inquiry-domain.spec.ts
   Type: Unit Tests (Jest/Vitest)
   Test Count: 5
   Tests:
     ✓ createNew() tạo yêu cầu tư vấn với các thuộc tính đúng
     ✓ createNew() tạo yêu cầu tư vấn với phone là null
     ✓ createNew() phát InquiryCreatedEvent
     ✓ markAsRead() đặt isRead thành true
     ✓ markAsRead() phát InquiryReadEvent
   Focus: Hành vi entity và việc phát sự kiện domain
   Lines: ~96
```

---

### **3. TẦNG HẠ TẦNG (INFRASTRUCTURE LAYER)**

#### Repository Implementation (1 file)

```
📄 apps/api/src/modules/inquiries/infrastructure/repositories/prisma-inquiry.repository.ts
   Type: Repository Implementation (Service)
   Implements: IInquiryRepository
   Decorator: @Injectable()
   Purpose: Persistence dựa trên Prisma
   
   Methods:
     1. findById(id: string): Promise<InquiryEntity | null>
        - Query: prisma.inquiry.findUnique({ where: { id } })
        - Returns: InquiryEntity | null
     
     2. save(entity: InquiryEntity): Promise<void>
        - Query: prisma.inquiry.create({ data: {...} })
        - Ánh xạ thuộc tính entity sang Prisma model
     
     3. markAsRead(id: string): Promise<void>
        - Query: prisma.inquiry.update({ where: { id }, data: { isRead: true } })
     
     4. findByListing(listingId, page, limit): Promise<PaginatedResult<InquiryReadDto>>
        - Query: prisma.inquiry.findMany() với joins
        - Includes: listing.property.title, user.fullName, user.phone
        - Pagination: mô hình skip/take
        - Sorting: orderBy: { createdAt: 'desc' }
        - Returns: Mảng InquiryReadDto đã ánh xạ kèm thông tin phân trang
     
     5. findByAgent(agentId, page, limit): Promise<PaginatedResult<InquiryReadDto>>
        - Query: prisma.inquiry.findMany() lọc theo listing.agentId
        - Cùng includes và phân trang như findByListing
        - Filter: { listing: { agentId } }
     
     6. countUnreadByAgent(agentId): Promise<number>
        - Query: prisma.inquiry.count()
        - Filter: { isRead: false, listing: { agentId } }
     
     Helper Method:
     • toDomain(raw: PrismaInquiry): InquiryEntity
       - Ánh xạ bản ghi cơ sở dữ liệu sang domain entity
   
   Lines: ~146
   Dependencies:
     • PrismaService (injected)
     • InquiryEntity
     • IInquiryRepository (implements)
```

---

### **4. TẦNG TRÌNH BÀY (PRESENTATION LAYER)**

#### Controller (1 file)

```
📄 apps/api/src/modules/inquiries/presentation/controllers/inquiries.controller.ts
   Type: NestJS Controller
   Decorator: @Controller('inquiries')
   Decorator: @ApiTags('inquiries')
   Purpose: Các endpoint HTTP API
   
   Endpoints:
   
   1. POST /inquiries
      Decorator: @Post()
      Auth: @UseGuards(JwtAuthGuard)
      Method: createInquiry(dto: CreateInquiryDto, user: JwtPayload)
      Body: CreateInquiryDto { listingId, message, phone? }
      Returns: CreateInquiryResult { id, listingId, createdAt }
      Status Codes: 201 Created, 400 Bad Request, 401 Unauthorized, 404 Not Found
      Logic: 
        • Điều phối CreateInquiryCommand qua CommandBus
        • Truyền user.sub làm userId
   
   2. GET /inquiries/listing/:listingId
      Decorator: @Get('listing/:listingId')
      Auth: @UseGuards(JwtAuthGuard)
      Method: getByListing(listingId: string, dto: ListInquiriesDto)
      Query: page?, limit?
      Returns: PaginatedResult<InquiryReadDto>
      Status Codes: 200 OK, 401 Unauthorized
      Logic:
        • Điều phối GetInquiriesByListingQuery qua QueryBus
        • Mặc định page=1, limit=20
   
   3. GET /inquiries/agent/me
      Decorator: @Get('agent/me')
      Auth: @UseGuards(JwtAuthGuard, RolesGuard)
      Decorator: @Roles('AGENT')
      Method: getMyInquiries(user: JwtPayload, dto: ListInquiriesDto)
      Query: page?, limit?
      Returns: PaginatedResult<InquiryReadDto>
      Status Codes: 200 OK, 401 Unauthorized, 403 Forbidden
      Logic:
        • Điều phối GetInquiriesByAgentQuery qua QueryBus
        • Truyền user.sub làm agentUserId
        • Mặc định page=1, limit=20
   
   4. PATCH /inquiries/:id/read
      Decorator: @Patch(':id/read')
      Auth: @UseGuards(JwtAuthGuard, RolesGuard)
      Decorator: @Roles('AGENT')
      Method: markAsRead(id: string, user: JwtPayload)
      Returns: { success: boolean }
      Status Codes: 200 OK, 401 Unauthorized, 403 Forbidden, 404 Not Found
      Logic:
        • Điều phối MarkInquiryReadCommand qua CommandBus
        • Truyền user.sub làm agentUserId
        • Trả về { success: true }
   
   Lines: ~121
   Dependencies:
     • CommandBus (injected)
     • QueryBus (injected)
     • @nestjs/swagger decorators
     • @modules/auth guards and decorators
```

#### DTOs (2 files)

```
📄 apps/api/src/modules/inquiries/presentation/dto/create-inquiry.dto.ts
   Type: Data Transfer Object (Validation)
   Purpose: Xác thực body của request POST /inquiries
   Decorators: @ApiProperty, @ApiPropertyOptional
   
   Properties:
     • listingId: string (required)
       Validators: @IsString(), @IsNotEmpty()
       API Doc: "ID of the listing"
     
     • message: string (required)
       Validators: @IsString(), @IsNotEmpty(), @MaxLength(2000)
       API Doc: "Tin nhắn yêu cầu tư vấn" (Consultation request message)
       Max: 2000 ký tự
     
     • phone?: string (optional)
       Validators: @IsOptional(), @IsString()
       API Doc: "Số điện thoại liên hệ" (Contact phone number)
   
   Lines: ~21
   Dependencies: class-validator, @nestjs/swagger
```

```
📄 apps/api/src/modules/inquiries/presentation/dto/list-inquiries.dto.ts
   Type: Data Transfer Object (Validation)
   Purpose: Xác thực query parameter cho các endpoint danh sách
   Decorators: @ApiPropertyOptional, @Type
   
   Properties:
     • page?: number (optional)
       Validators: @IsOptional(), @IsInt(), @Min(1), @Type(() => Number)
       API Doc: "Số trang", mặc định: 1
       Example: 1
     
     • limit?: number (optional)
       Validators: @IsOptional(), @IsInt(), @Min(1), @Max(100), @Type(() => Number)
       API Doc: "Số mục mỗi trang", mặc định: 20
       Example: 20
       Max: 100
   
   Lines: ~21
   Dependencies: class-validator, @nestjs/swagger, class-transformer
```

#### Presentation Tests (1 file)

```
📄 apps/api/src/modules/inquiries/presentation/__tests__/inquiries.controller.spec.ts
   Type: Unit Tests (Jest/Vitest)
   Test Count: 6
   
   Tests:
     ✓ POST /inquiries điều phối CreateInquiryCommand với tham số đúng
     ✓ POST /inquiries truyền phone là null khi không được cung cấp
     ✓ GET /listing/:id điều phối GetInquiriesByListingQuery với giá trị mặc định
     ✓ GET /listing/:id truyền phân trang tùy chỉnh
     ✓ GET /agent/me điều phối GetInquiriesByAgentQuery với người dùng hiện tại
     ✓ PATCH /:id/read điều phối MarkInquiryReadCommand và trả về thành công
   
   Mocks:
     • mockCommandBus với hàm mock execute()
     • mockQueryBus với hàm mock execute()
     • mockBuyer user object (sub='buyer-1', role='BUYER')
     • mockAgent user object (sub='agent-1', role='AGENT')
   
   Lines: ~105
```

---

### **5. TẦNG MODULE (MODULE LAYER)**

```
📄 apps/api/src/modules/inquiries/inquiries.module.ts
   Type: NestJS Module
   Decorator: @Module()
   Purpose: Cấu hình module, khai báo phụ thuộc, xuất khẩu
   
   Imports: [CqrsModule]
   Controllers: [InquiriesController]
   
   Providers:
     • { provide: INQUIRY_REPOSITORY, useClass: PrismaInquiryRepository }
       - Ánh xạ token dependency injection
     • CommandHandlers array: [CreateInquiryHandler, MarkInquiryReadHandler]
     • QueryHandlers array: [GetInquiriesByListingHandler, GetInquiriesByAgentHandler]
   
   Exports: [INQUIRY_REPOSITORY]
     - Cung cấp repository cho các module khác
   
   Lines: ~29
   Dependencies:
     • @nestjs/common
     • @nestjs/cqrs
     • All handlers, repository, controller
```

```
📄 apps/api/src/modules/inquiries/index.ts
   Type: Barrel Export (Public API)
   Purpose: Định nghĩa giao diện công khai của module
   
   Exports:
     • InquiriesModule (default export for app imports)
     • INQUIRY_REPOSITORY (DI token)
     • IInquiryRepository (interface type)
     • InquiryEntity (for external access)
   
   Lines: ~4
```

---

## 📊 THỐNG KÊ FILE

### Theo Tầng

| Tầng | Files | Loại | Mục đích |
|------|-------|------|----------|
| **Presentation** | 5 | Controller + DTOs + Tests | Endpoint HTTP và xác thực đầu vào |
| **Application** | 8 | Commands/Queries + Handlers + Tests | Điều phối use case |
| **Domain** | 6 | Entities + Events + Repository Interface + Tests | Logic nghiệp vụ và hợp đồng |
| **Infrastructure** | 1 | Repository Implementation | Lưu trữ cơ sở dữ liệu |
| **Module** | 2 | Module + Exports | Cấu hình và API công khai |
| **TỔNG** | 25 | - | - |

### Theo Loại

| Loại | Số lượng |
|------|----------|
| File nguồn (.ts, không phải test) | 19 |
| File test (.spec.ts) | 6 |
| **Tổng** | **25** |

### Theo Mục Đích

| Mục đích | Số lượng | Files |
|----------|----------|-------|
| Controllers | 1 | inquiries.controller.ts |
| DTOs | 2 | create-inquiry.dto.ts, list-inquiries.dto.ts |
| Commands | 2 | create-inquiry.command.ts, mark-inquiry-read.command.ts |
| Queries | 2 | get-inquiries-by-*.query.ts (2 files) |
| Handlers | 4 | 2 command handlers + 2 query handlers |
| Entities | 1 | inquiry.entity.ts |
| Events | 2 | inquiry-created.event.ts, inquiry-read.event.ts |
| Repositories | 3 | interface + 2 DTOs + 1 implementation |
| Tests | 6 | 5 bộ test trải rộng các tầng |
| Module | 2 | inquiries.module.ts, index.ts |

---

## 🔍 HƯỚNG DẪN TÌM KIẾM FILE

### Tìm kiếm Logic Nghiệp Vụ?
→ **`domain/entities/inquiry.entity.ts`** - Quy tắc nghiệp vụ cốt lõi
→ **`domain/events/*.event.ts`** - Sự kiện nghiệp vụ

### Tìm kiếm Use Case?
→ **`application/commands/*/`** - Thao tác ghi
→ **`application/queries/*/`** - Thao tác đọc
→ **`application/[type]/__tests__/`** - Các test case

### Tìm kiếm HTTP Endpoint?
→ **`presentation/controllers/inquiries.controller.ts`** - Toàn bộ 4 endpoint

### Tìm kiếm Xác Thực Đầu Vào?
→ **`presentation/dto/`** - Tất cả DTOs kèm validator

### Tìm kiếm Logic Cơ Sở Dữ Liệu?
→ **`infrastructure/repositories/prisma-inquiry.repository.ts`** - Tất cả Prisma query

### Tìm kiếm Tests?
→ **`domain/__tests__/`** - Hành vi domain
→ **`application/__tests__/`** - Test handler
→ **`presentation/__tests__/`** - Test controller

---

## 🔗 ĐỒ THỊ PHỤ THUỘC (DEPENDENCY GRAPH)

```
presentation/controllers/inquiries.controller.ts
  ├── application/commands/create-inquiry/create-inquiry.handler.ts
  ├── application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts
  ├── application/queries/get-inquiries-by-*/[name].handler.ts
  └── CommandBus/QueryBus (NestJS CQRS)

application/commands/*/[name].handler.ts
  ├── domain/entities/inquiry.entity.ts
  ├── domain/repositories/inquiry.repository.ts (interface)
  ├── domain/events/*.event.ts
  └── @modules/shared (AggregateRoot, exceptions)

application/queries/*/[name].handler.ts
  ├── domain/repositories/inquiry.repository.ts
  └── domain/repositories/inquiry-read.dto.ts

domain/entities/inquiry.entity.ts
  ├── @modules/shared (AggregateRoot)
  └── domain/events/*.event.ts

infrastructure/repositories/prisma-inquiry.repository.ts
  ├── domain/entities/inquiry.entity.ts
  ├── domain/repositories/inquiry.repository.ts (implements)
  ├── domain/repositories/inquiry-read.dto.ts
  └── @prisma/client (Prisma)

inquiries.module.ts
  ├── All handlers
  ├── All repositories
  ├── InquiriesController
  └── CqrsModule (NestJS)
```

---

## 📝 THAM CHIẾU CHÉO FILE

### Các File Sử Dụng Symbol INQUIRY_REPOSITORY
- `inquiries.module.ts` - Cung cấp implementation
- `application/commands/create-inquiry/create-inquiry.handler.ts` - Injects
- `application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts` - Injects
- `application/queries/get-inquiries-by-agent/get-inquiries-by-agent.handler.ts` - Injects
- `application/queries/get-inquiries-by-listing/get-inquiries-by-listing.handler.ts` - Injects

### Các File Tạo/Chỉnh Sửa InquiryEntity
- `domain/entities/inquiry.entity.ts` - Định nghĩa
- `application/commands/create-inquiry/create-inquiry.handler.ts` - Tạo mới
- `application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts` - Chỉnh sửa
- `infrastructure/repositories/prisma-inquiry.repository.ts` - Ánh xạ đến/từ

### Các File Phát Sự Kiện
- `domain/entities/inquiry.entity.ts` - Phát ra (qua addDomainEvent)
- `application/commands/create-inquiry/create-inquiry.handler.ts` - Xuất bản
- `application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts` - Xuất bản

### Các File Có Tests
- `domain/entities/inquiry.entity.ts` → `domain/__tests__/inquiry-domain.spec.ts`
- `application/commands/create-inquiry/create-inquiry.handler.ts` → `application/__tests__/create-inquiry.handler.spec.ts`
- `application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts` → `application/__tests__/mark-inquiry-read.handler.spec.ts`
- `application/queries/get-inquiries-by-listing/` → `application/__tests__/get-inquiries-by-listing.handler.spec.ts`
- `application/queries/get-inquiries-by-agent/` → `application/__tests__/get-inquiries-by-agent.handler.spec.ts`
- `presentation/controllers/inquiries.controller.ts` → `presentation/__tests__/inquiries.controller.spec.ts`

---

## 🎯 ĐIỂM VÀO CHO VIỆC CHỈNH SỬA

### Thêm Endpoint Mới
1. Thêm phương thức vào `presentation/controllers/inquiries.controller.ts`
2. Tạo command/query trong `application/[type]/[name]/`
3. Tạo handler: `[name].handler.ts`
4. Thêm tests trong `application/__tests__/`
5. Tuỳ chọn: Cập nhật domain nếu cần logic nghiệp vụ mới

### Thêm Command Mới
1. Tạo `application/commands/[name]/[name].command.ts` (DTO)
2. Tạo `application/commands/[name]/[name].handler.ts` (@CommandHandler)
3. Đăng ký trong mảng CommandHandlers của `inquiries.module.ts`
4. Thêm tests trong `application/__tests__/[name].handler.spec.ts`
5. Tuỳ chọn: Định nghĩa sự kiện domain mới trong `domain/events/`

### Thêm Logic Domain
1. Chỉnh sửa `domain/entities/inquiry.entity.ts`
2. Thêm phương thức hoặc thuộc tính công khai mới
3. Phát sự kiện mới nếu cần: `domain/events/[name].event.ts`
4. Cập nhật tests trong `domain/__tests__/inquiry-domain.spec.ts`
5. Cập nhật các handler sử dụng entity

### Thêm Thao Tác Cơ Sở Dữ Liệu
1. Thêm phương thức vào `infrastructure/repositories/prisma-inquiry.repository.ts`
2. Cập nhật interface trong `domain/repositories/inquiry.repository.ts`
3. Gọi từ các handler liên quan

---

**Tổng số dòng code:** 1.212  
**Cập nhật lần cuối:** Ngày 11 tháng 4 năm 2026
