This repository has no description
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```