Move 36 root-level audit/analysis documents and 7 web app audit documents into docs/audits/ directory to declutter the project root. Remove stale EXPLORATION_SUMMARY.txt. Co-Authored-By: Paperclip <noreply@paperclip.ing>
736 lines
23 KiB
Markdown
736 lines
23 KiB
Markdown
# Inquiries Module - Complete File Index
|
|
|
|
**Generated:** April 11, 2026
|
|
**Module:** `apps/api/src/modules/inquiries/`
|
|
**Total Files:** 25
|
|
|
|
---
|
|
|
|
## 📋 COMPLETE FILE LISTING BY PATH
|
|
|
|
### **1. APPLICATION LAYER**
|
|
|
|
#### Commands (4 files)
|
|
|
|
```
|
|
📄 apps/api/src/modules/inquiries/application/commands/create-inquiry/create-inquiry.command.ts
|
|
Type: Command (CQRS)
|
|
Purpose: Input DTO for create inquiry use case
|
|
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: Orchestrates inquiry creation
|
|
Implements: ICommandHandler<CreateInquiryCommand>
|
|
Returns: CreateInquiryResult { id, listingId, createdAt }
|
|
Key Logic:
|
|
• Validate listing exists (Prisma.listing.findUnique)
|
|
• Create InquiryEntity via factory method
|
|
• Save to repository
|
|
• Publish InquiryCreatedEvent via 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 for mark inquiry as read
|
|
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: Orchestrates mark as read operation
|
|
Implements: ICommandHandler<MarkInquiryReadCommand>
|
|
Returns: void (no return value)
|
|
Key Logic:
|
|
• Load inquiry entity from repository
|
|
• Load listing and verify agent ownership
|
|
• Call inquiry.markAsRead() to update state
|
|
• Persist via repository.markAsRead()
|
|
• Publish InquiryReadEvent via EventBus
|
|
Authorization Checks:
|
|
• Inquiry exists
|
|
• Listing exists
|
|
• User is registered as agent
|
|
• Agent ID matches 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 for listing agent's inquiries
|
|
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: Resolves paginated inquiries for an agent
|
|
Implements: IQueryHandler<GetInquiriesByAgentQuery>
|
|
Returns: PaginatedResult<InquiryReadDto>
|
|
Key Logic:
|
|
• Resolve agent ID from userId via Prisma
|
|
• Throw NotFoundException if user not an agent
|
|
• Delegate to 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 for listing inquiries
|
|
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: Resolves paginated inquiries for a listing
|
|
Implements: IQueryHandler<GetInquiriesByListingQuery>
|
|
Returns: PaginatedResult<InquiryReadDto>
|
|
Key Logic:
|
|
• Direct delegation to 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 with Mock functions (vi.fn)
|
|
Test Count: 4
|
|
Tests:
|
|
✓ creates an inquiry successfully
|
|
✓ throws NotFoundException when listing not found
|
|
✓ publishes domain events after saving
|
|
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:
|
|
✓ marks an inquiry as read successfully
|
|
✓ throws NotFoundException when inquiry not found
|
|
✓ throws NotFoundException when listing not found
|
|
✓ throws ForbiddenException when user is not the listing agent
|
|
✓ throws ForbiddenException when agent not found for user
|
|
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:
|
|
✓ returns paginated results
|
|
✓ returns empty data when no inquiries found
|
|
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:
|
|
✓ returns paginated results
|
|
✓ throws NotFoundException when agent not found for user
|
|
Mocks:
|
|
• mockInquiryRepo.findByAgent
|
|
• mockPrisma.agent.findUnique
|
|
Lines: ~77
|
|
```
|
|
|
|
---
|
|
|
|
### **2. DOMAIN LAYER**
|
|
|
|
#### Entities (1 file)
|
|
|
|
```
|
|
📄 apps/api/src/modules/inquiries/domain/entities/inquiry.entity.ts
|
|
Type: Aggregate Root (DDD)
|
|
Extends: AggregateRoot<string>
|
|
Purpose: Core business logic for inquiries
|
|
|
|
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
|
|
- Creates new inquiry with isRead=false
|
|
- Emits InquiryCreatedEvent
|
|
- Returns entity instance
|
|
|
|
Domain Methods:
|
|
• markAsRead(): void
|
|
- Sets _isRead to true
|
|
- Emits 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: Signals that an inquiry was created
|
|
|
|
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: Signals that an inquiry was marked as read
|
|
|
|
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: Defines persistence contract
|
|
|
|
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 for query results
|
|
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() creates an inquiry with correct properties
|
|
✓ createNew() creates an inquiry with null phone
|
|
✓ createNew() emits InquiryCreatedEvent
|
|
✓ markAsRead() sets isRead to true
|
|
✓ markAsRead() emits InquiryReadEvent
|
|
Focus: Entity behavior and domain event emission
|
|
Lines: ~96
|
|
```
|
|
|
|
---
|
|
|
|
### **3. 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: Prisma-based persistence
|
|
|
|
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: {...} })
|
|
- Maps entity properties to 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() with joins
|
|
- Includes: listing.property.title, user.fullName, user.phone
|
|
- Pagination: skip/take pattern
|
|
- Sorting: orderBy: { createdAt: 'desc' }
|
|
- Returns: Mapped InquiryReadDto array with pagination info
|
|
|
|
5. findByAgent(agentId, page, limit): Promise<PaginatedResult<InquiryReadDto>>
|
|
- Query: prisma.inquiry.findMany() filtered by listing.agentId
|
|
- Same includes and pagination as findByListing
|
|
- Filter: { listing: { agentId } }
|
|
|
|
6. countUnreadByAgent(agentId): Promise<number>
|
|
- Query: prisma.inquiry.count()
|
|
- Filter: { isRead: false, listing: { agentId } }
|
|
|
|
Helper Method:
|
|
• toDomain(raw: PrismaInquiry): InquiryEntity
|
|
- Maps database record to domain entity
|
|
|
|
Lines: ~146
|
|
Dependencies:
|
|
• PrismaService (injected)
|
|
• InquiryEntity
|
|
• IInquiryRepository (implements)
|
|
```
|
|
|
|
---
|
|
|
|
### **4. 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: HTTP API endpoints
|
|
|
|
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:
|
|
• Dispatch CreateInquiryCommand via CommandBus
|
|
• Pass user.sub as 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:
|
|
• Dispatch GetInquiriesByListingQuery via QueryBus
|
|
• Default 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:
|
|
• Dispatch GetInquiriesByAgentQuery via QueryBus
|
|
• Pass user.sub as agentUserId
|
|
• Default 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:
|
|
• Dispatch MarkInquiryReadCommand via CommandBus
|
|
• Pass user.sub as agentUserId
|
|
• Return { 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: Validate POST /inquiries request body
|
|
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 characters
|
|
|
|
• 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: Validate query parameters for list endpoints
|
|
Decorators: @ApiPropertyOptional, @Type
|
|
|
|
Properties:
|
|
• page?: number (optional)
|
|
Validators: @IsOptional(), @IsInt(), @Min(1), @Type(() => Number)
|
|
API Doc: "Page number", default: 1
|
|
Example: 1
|
|
|
|
• limit?: number (optional)
|
|
Validators: @IsOptional(), @IsInt(), @Min(1), @Max(100), @Type(() => Number)
|
|
API Doc: "Items per page", default: 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 dispatches CreateInquiryCommand with correct parameters
|
|
✓ POST /inquiries passes null phone when not provided
|
|
✓ GET /listing/:id dispatches GetInquiriesByListingQuery with defaults
|
|
✓ GET /listing/:id passes custom pagination
|
|
✓ GET /agent/me dispatches GetInquiriesByAgentQuery with current user
|
|
✓ PATCH /:id/read dispatches MarkInquiryReadCommand and returns success
|
|
|
|
Mocks:
|
|
• mockCommandBus with execute() mock function
|
|
• mockQueryBus with execute() mock function
|
|
• mockBuyer user object (sub='buyer-1', role='BUYER')
|
|
• mockAgent user object (sub='agent-1', role='AGENT')
|
|
|
|
Lines: ~105
|
|
```
|
|
|
|
---
|
|
|
|
### **5. MODULE LAYER**
|
|
|
|
```
|
|
📄 apps/api/src/modules/inquiries/inquiries.module.ts
|
|
Type: NestJS Module
|
|
Decorator: @Module()
|
|
Purpose: Configure module, declare dependencies, exports
|
|
|
|
Imports: [CqrsModule]
|
|
Controllers: [InquiriesController]
|
|
|
|
Providers:
|
|
• { provide: INQUIRY_REPOSITORY, useClass: PrismaInquiryRepository }
|
|
- Dependency injection token mapping
|
|
• CommandHandlers array: [CreateInquiryHandler, MarkInquiryReadHandler]
|
|
• QueryHandlers array: [GetInquiriesByListingHandler, GetInquiriesByAgentHandler]
|
|
|
|
Exports: [INQUIRY_REPOSITORY]
|
|
- Makes repository available to other modules
|
|
|
|
Lines: ~29
|
|
Dependencies:
|
|
• @nestjs/common
|
|
• @nestjs/cqrs
|
|
• All handlers, repository, controller
|
|
```
|
|
|
|
```
|
|
📄 apps/api/src/modules/inquiries/index.ts
|
|
Type: Barrel Export (Public API)
|
|
Purpose: Define public module interface
|
|
|
|
Exports:
|
|
• InquiriesModule (default export for app imports)
|
|
• INQUIRY_REPOSITORY (DI token)
|
|
• IInquiryRepository (interface type)
|
|
• InquiryEntity (for external access)
|
|
|
|
Lines: ~4
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 FILE STATISTICS
|
|
|
|
### By Layer
|
|
|
|
| Layer | Files | Type | Purpose |
|
|
|-------|-------|------|---------|
|
|
| **Presentation** | 5 | Controller + DTOs + Tests | HTTP endpoints and input validation |
|
|
| **Application** | 8 | Commands/Queries + Handlers + Tests | Use case orchestration |
|
|
| **Domain** | 6 | Entities + Events + Repository Interface + Tests | Business logic and contracts |
|
|
| **Infrastructure** | 1 | Repository Implementation | Database persistence |
|
|
| **Module** | 2 | Module + Exports | Configuration and public API |
|
|
| **TOTAL** | 25 | - | - |
|
|
|
|
### By Type
|
|
|
|
| Type | Count |
|
|
|------|-------|
|
|
| Source Files (.ts, no tests) | 19 |
|
|
| Test Files (.spec.ts) | 6 |
|
|
| **Total** | **25** |
|
|
|
|
### By Purpose
|
|
|
|
| Purpose | Count | 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 test suites across layers |
|
|
| Module | 2 | inquiries.module.ts, index.ts |
|
|
|
|
---
|
|
|
|
## 🔍 FILE DISCOVERY GUIDE
|
|
|
|
### Looking for Business Logic?
|
|
→ **`domain/entities/inquiry.entity.ts`** - Core business rules
|
|
→ **`domain/events/*.event.ts`** - Business events
|
|
|
|
### Looking for Use Cases?
|
|
→ **`application/commands/*/`** - Write operations
|
|
→ **`application/queries/*/`** - Read operations
|
|
→ **`application/[type]/__tests__/`** - Test cases
|
|
|
|
### Looking for HTTP Endpoints?
|
|
→ **`presentation/controllers/inquiries.controller.ts`** - All 4 endpoints
|
|
|
|
### Looking for Input Validation?
|
|
→ **`presentation/dto/`** - All DTOs with validators
|
|
|
|
### Looking for Database Logic?
|
|
→ **`infrastructure/repositories/prisma-inquiry.repository.ts`** - All Prisma queries
|
|
|
|
### Looking for Tests?
|
|
→ **`domain/__tests__/`** - Domain behavior
|
|
→ **`application/__tests__/`** - Handler tests
|
|
→ **`presentation/__tests__/`** - Controller tests
|
|
|
|
---
|
|
|
|
## 🔗 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)
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 FILE CROSS-REFERENCES
|
|
|
|
### Files Using INQUIRY_REPOSITORY Symbol
|
|
- `inquiries.module.ts` - Provides 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
|
|
|
|
### Files Creating/Modifying InquiryEntity
|
|
- `domain/entities/inquiry.entity.ts` - Defines
|
|
- `application/commands/create-inquiry/create-inquiry.handler.ts` - Creates
|
|
- `application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts` - Modifies
|
|
- `infrastructure/repositories/prisma-inquiry.repository.ts` - Maps to/from
|
|
|
|
### Files Publishing Events
|
|
- `domain/entities/inquiry.entity.ts` - Emits (via addDomainEvent)
|
|
- `application/commands/create-inquiry/create-inquiry.handler.ts` - Publishes
|
|
- `application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts` - Publishes
|
|
|
|
### Files with 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`
|
|
|
|
---
|
|
|
|
## 🎯 ENTRY POINTS FOR MODIFICATIONS
|
|
|
|
### To Add a New Endpoint
|
|
1. Add method to `presentation/controllers/inquiries.controller.ts`
|
|
2. Create command/query in `application/[type]/[name]/`
|
|
3. Create handler: `[name].handler.ts`
|
|
4. Add tests in `application/__tests__/`
|
|
5. Optional: Update domain if new business logic needed
|
|
|
|
### To Add a New Command
|
|
1. Create `application/commands/[name]/[name].command.ts` (DTO)
|
|
2. Create `application/commands/[name]/[name].handler.ts` (@CommandHandler)
|
|
3. Register in `inquiries.module.ts` CommandHandlers array
|
|
4. Add tests in `application/__tests__/[name].handler.spec.ts`
|
|
5. Optional: Define new domain events in `domain/events/`
|
|
|
|
### To Add Domain Logic
|
|
1. Modify `domain/entities/inquiry.entity.ts`
|
|
2. Add new public methods or properties
|
|
3. Emit new events if needed: `domain/events/[name].event.ts`
|
|
4. Update tests in `domain/__tests__/inquiry-domain.spec.ts`
|
|
5. Update handlers that use the entity
|
|
|
|
### To Add Database Operations
|
|
1. Add method to `infrastructure/repositories/prisma-inquiry.repository.ts`
|
|
2. Update interface in `domain/repositories/inquiry.repository.ts`
|
|
3. Call from relevant handlers
|
|
|
|
---
|
|
|
|
**Total Lines of Code:** 1,212
|
|
**Last Updated:** April 11, 2026
|