This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

semble / docs / features / GUIDE.md
12 kB 350 lines
1# Feature Implementation Guide 2 3This guide outlines the vertical slice approach for implementing features end-to-end in our DDD (Domain-Driven Design) and layered architecture system with Command Query Responsibility Segregation (CQRS). 4 5## Architecture Overview 6 7Our system follows a clean architecture pattern with the following layers: 8 9- **Domain Layer**: Core business logic, entities, value objects, and domain services 10- **Application Layer**: Use cases (commands and queries), DTOs, and application services 11- **Infrastructure Layer**: Repositories, external services, and technical implementations 12- **Presentation Layer**: HTTP controllers, routes, and API clients 13 14## Command vs Query Pattern 15 16We implement CQRS to separate read and write operations: 17 18### Commands 19 20- **Purpose**: Modify system state (Create, Update, Delete operations) 21- **Example**: `AddUrlToLibraryUseCase` 22- **Characteristics**: 23 - Return success/failure results 24 - May trigger domain events 25 - Often involve business rule validation 26 - Use domain services for complex operations 27 28### Queries 29 30- **Purpose**: Read data without side effects 31- **Example**: `GetCollectionPageUseCase` 32- **Characteristics**: 33 - Return data transfer objects (DTOs) 34 - Optimized for specific read scenarios 35 - May aggregate data from multiple sources 36 - Use query repositories for efficient data access 37 38## Vertical Slice Implementation Steps 39 40### 1. Domain Layer (if needed) 41 42#### Domain Entities & Value Objects 43 44- **Location**: `src/modules/{module}/domain/` 45- **Files**: Entities, Value Objects, Domain Services 46- **Example**: `Collection.ts`, `CardId.ts`, `URL.ts` 47 48#### Domain Services 49 50- **Location**: `src/modules/{module}/domain/services/` 51- **Purpose**: Complex business logic that doesn't belong to a single entity 52- **Example**: `CardLibraryService`, `CardCollectionService` 53 54#### Repository Interfaces 55 56- **Location**: `src/modules/{module}/domain/` 57- **Files**: 58 - `I{Entity}Repository.ts` - For command operations (write/modify state) 59 - `I{Entity}QueryRepository.ts` - For query operations (read-only, optimized for specific views) 60- **Example**: `ICardRepository.ts`, `ICardQueryRepository.ts`, `ICollectionRepository.ts`, `ICollectionQueryRepository.ts` 61 62### 2. Application Layer 63 64#### Use Cases 65 66- **Location**: `src/modules/{module}/application/useCases/` 67- **Structure**: 68 - `commands/` - For state-changing operations 69 - `queries/` - For read-only operations 70 71#### Command Use Case Pattern 72 73```typescript 74// Example: AddUrlToLibraryUseCase 75export interface AddUrlToLibraryDTO { 76 url: string; 77 note?: string; 78 collectionIds?: string[]; 79 curatorId: string; 80} 81 82export class AddUrlToLibraryUseCase 83 implements UseCase<AddUrlToLibraryDTO, Result<ResponseDTO>> 84{ 85 constructor( 86 private cardRepository: ICardRepository, 87 private metadataService: IMetadataService, 88 private cardLibraryService: CardLibraryService, 89 private cardCollectionService: CardCollectionService, 90 ) {} 91 92 async execute(request: AddUrlToLibraryDTO): Promise<Result<ResponseDTO>> { 93 // 1. Validate input 94 // 2. Create/fetch domain entities 95 // 3. Apply business rules via domain services 96 // 4. Persist changes 97 // 5. Return result 98 } 99} 100``` 101 102#### Query Use Case Pattern 103 104```typescript 105// Example: GetCollectionPageUseCase 106export interface GetCollectionPageQuery { 107 collectionId: string; 108 page?: number; 109 limit?: number; 110 sortBy?: CardSortField; 111 sortOrder?: SortOrder; 112} 113 114export class GetCollectionPageUseCase 115 implements UseCase<GetCollectionPageQuery, Result<GetCollectionPageResult>> 116{ 117 constructor( 118 private collectionRepo: ICollectionRepository, 119 private cardQueryRepo: ICardQueryRepository, 120 private profileService: IProfileService, 121 ) {} 122 123 async execute( 124 query: GetCollectionPageQuery, 125 ): Promise<Result<GetCollectionPageResult>> { 126 // 1. Validate query parameters 127 // 2. Fetch data from query repositories 128 // 3. Aggregate and transform data 129 // 4. Return structured result 130 } 131} 132``` 133 134### 3. Infrastructure Layer 135 136#### Repository Implementations 137 138- **Location**: `src/modules/{module}/infrastructure/repositories/` 139- **Files**: 140 - `Drizzle{Entity}Repository.ts` - Implements command repository interface 141 - `Drizzle{Entity}QueryRepository.ts` - Implements query repository interface with optimized read operations 142- **Purpose**: Implement domain repository interfaces with specific technology (Drizzle ORM) 143- **Pattern**: Query repositories often return DTOs optimized for specific views, while command repositories work with full domain entities 144 145#### HTTP Controllers 146 147- **Location**: `src/modules/{module}/infrastructure/http/controllers/` 148- **Pattern**: 149 150```typescript 151export class {Feature}Controller extends Controller { 152 constructor(private {feature}UseCase: {Feature}UseCase) { 153 super(); 154 } 155 156 async executeImpl(req: AuthenticatedRequest, res: Response): Promise<any> { 157 try { 158 // 1. Extract and validate request data 159 // 2. Call use case 160 // 3. Handle result and return appropriate HTTP response 161 } catch (error) { 162 return this.fail(res, error); 163 } 164 } 165} 166``` 167 168#### Routes 169 170- **Location**: `src/modules/{module}/infrastructure/http/routes/` 171- **Purpose**: Define HTTP endpoints and wire controllers 172- **Pattern**: Group related endpoints, apply middleware (auth, validation) 173 174### 4. Dependency Injection & Factories 175 176#### Factory Registration 177 178All new components must be registered in the appropriate factories: 179 1801. **RepositoryFactory** (`src/shared/infrastructure/http/factories/RepositoryFactory.ts`) 181 - Register both command and query repository implementations 182 1832. **ServiceFactory** (`src/shared/infrastructure/http/factories/ServiceFactory.ts`) 184 - Register domain services and external services 185 1863. **UseCaseFactory** (`src/shared/infrastructure/http/factories/UseCaseFactory.ts`) 187 - Register use cases with their dependencies 188 1894. **ControllerFactory** (`src/shared/infrastructure/http/factories/ControllerFactory.ts`) 190 - Register controllers with their use cases 191 192### 5. API Client Layer 193 194#### Client Structure 195 196- **Location**: `src/webapp/api-client/` 197- **Files**: 198 - `types/requests.ts` - Request DTOs 199 - `types/responses.ts` - Response DTOs 200 - `clients/{Module}Client.ts` - HTTP client implementations 201 - `ApiClient.ts` - Main client facade 202 203#### Client Pattern 204 205```typescript 206export class {Module}Client extends BaseClient { 207 async {operation}(request: {Operation}Request): Promise<{Operation}Response> { 208 return this.request<{Operation}Response>( 209 'POST', // or appropriate HTTP method 210 '/api/{endpoint}', 211 request, 212 ); 213 } 214} 215``` 216 217## Implementation Checklist 218 219When implementing a new feature, follow this checklist: 220 221### Domain Layer 222 223- [ ] Create/update domain entities if needed 224- [ ] Create/update value objects if needed 225- [ ] Define command repository interfaces (for write operations) 226- [ ] Define query repository interfaces (for read operations with specific DTOs) 227- [ ] Implement domain services for complex business logic 228 229### Application Layer 230 231- [ ] Create use case (command or query) 232- [ ] Define request/response DTOs 233- [ ] Implement business logic and validation 234- [ ] Handle error cases appropriately 235 236### Infrastructure Layer 237 238- [ ] Implement command repository (if new entity) 239- [ ] Implement query repository (if new entity) 240- [ ] Create HTTP controller 241- [ ] Define routes 242- [ ] Register in factories 243 244### API Client Layer 245 246- [ ] Define request/response types 247- [ ] Implement client methods 248- [ ] Update main ApiClient facade 249 250### Integration 251 252- [ ] Register all components in factories 253- [ ] Wire routes in main app 254- [ ] Test end-to-end flow 255 256### Repository Pattern (CQRS) 257 258- **Command Repositories**: Handle write operations, work with full domain entities, enforce business rules 259- **Query Repositories**: Handle read operations, return optimized DTOs, support pagination and sorting 260- **Separation**: Commands use `I{Entity}Repository`, queries use `I{Entity}QueryRepository` 261- **DTOs**: Query repositories return view-specific DTOs (e.g., `UrlCardView`, `CollectionQueryResultDTO`) 262 263## Key Patterns & Conventions 264 265### Error Handling 266 267- Use `Result<T, E>` pattern for use cases 268- Define specific error types that extend `UseCaseError` 269- Controllers handle errors and return appropriate HTTP status codes 270 271### Validation 272 273- Input validation in use cases using value objects 274- Domain validation in entities and value objects 275- HTTP validation in controllers 276 277### Authentication 278 279- Use `AuthenticatedRequest` for protected endpoints 280- Extract user identity (`did`) from request 281- Pass user context to use cases 282 283### Pagination & Sorting 284 285- Standardize pagination parameters (`page`, `limit`) 286- Use enums for sort fields and order 287- Return pagination metadata in responses 288 289### Testing Strategy 290 291- Unit tests for domain logic 292- Integration tests for use cases 293- In-memory implementations for testing 294- Mock external services 295 296## Common Pitfalls to Avoid 297 2981. **Mixing Concerns**: Keep domain logic in domain layer, not in controllers or repositories 2992. **Anemic Domain Model**: Ensure entities have behavior, not just data 3003. **Repository Leakage**: Don't expose ORM-specific types in domain interfaces 3014. **Missing Error Handling**: Always handle and propagate errors appropriately 3025. **Inconsistent Patterns**: Follow established patterns for similar operations 303 304## Example Files to Reference 305 306### Command Example 307 308- Use Case: `src/modules/cards/application/useCases/commands/AddUrlToLibraryUseCase.ts` 309- Controller: `src/modules/cards/infrastructure/http/controllers/AddUrlToLibraryController.ts` 310 311### Query Example 312 313- Use Case: `src/modules/cards/application/useCases/queries/GetCollectionPageUseCase.ts` 314- Controller: `src/modules/cards/infrastructure/http/controllers/GetCollectionPageController.ts` 315 316### Repository Examples 317 318- Command Repository Interface: `src/modules/cards/domain/ICardRepository.ts` 319- Query Repository Interface: `src/modules/cards/domain/ICardQueryRepository.ts` 320- Command Repository Implementation: `src/modules/cards/infrastructure/repositories/DrizzleCardRepository.ts` 321- Query Repository Implementation: `src/modules/cards/infrastructure/repositories/DrizzleCardQueryRepository.ts` 322 323### Factory Examples 324 325- All factories in `src/shared/infrastructure/http/factories/` 326 327This guide should be used as a reference when implementing new features to ensure consistency with the established architecture and patterns. 328 329### example context: 330 331``` 332docs/features/GUIDE.md 333src/modules/cards/application/useCases/commands/AddUrlToLibraryUseCase.ts 334src/modules/cards/domain/ICardQueryRepository.ts 335src/modules/cards/domain/ICardRepository.ts 336src/modules/cards/infrastructure/http/controllers/AddUrlToLibraryController.ts 337src/modules/cards/infrastructure/http/routes/cardRoutes.ts 338src/modules/cards/infrastructure/http/routes/collectionRoutes.ts 339src/modules/user/infrastructure/http/routes/userRoutes.ts 340src/shared/domain/AggregateRoot.ts 341src/shared/domain/events/IDomainEvent.ts 342src/shared/infrastructure/http/app.ts 343src/shared/infrastructure/http/factories/ControllerFactory.ts 344src/shared/infrastructure/http/factories/RepositoryFactory.ts 345src/shared/infrastructure/http/factories/ServiceFactory.ts 346src/shared/infrastructure/http/factories/UseCaseFactory.ts 347src/modules/cards/domain/Card.ts 348src/modules/cards/domain/events/CardAddedToLibraryEvent.ts 349src/modules/cards/infrastructure/http/routes/index.ts 350```