This repository has no description
0

Configure Feed

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

docs: add feature implementation guide for vertical slice architecture

Co-authored-by: aider (anthropic/claude-sonnet-4-20250514) <aider@aider.chat>

+202
+202
.agent/logs/20260121_feature_implementation_guide.md
··· 1 + # Feature Implementation Guide - Vertical Slice Pattern 2 + 3 + This guide outlines the standard pattern for implementing a complete feature in the Semble codebase, following a vertical slice architecture from API client to database. 4 + 5 + ## Overview 6 + 7 + Each feature follows a consistent layered architecture: 8 + - **API Client** - TypeScript types and client methods 9 + - **HTTP Layer** - Controllers and routes 10 + - **Application Layer** - Use cases (business logic) 11 + - **Domain Layer** - Entities, value objects, and domain services 12 + - **Infrastructure Layer** - Repositories, external services, and persistence 13 + 14 + ## Implementation Steps 15 + 16 + ### 1. Define Types (`src/types/src/api/`) 17 + 18 + Start by defining the request/response types that will be used across the API: 19 + 20 + **Request Types** (`requests.ts`): 21 + ```typescript 22 + export interface MyFeatureRequest { 23 + param1: string; 24 + param2?: number; 25 + } 26 + ``` 27 + 28 + **Response Types** (`responses.ts`): 29 + ```typescript 30 + export interface MyFeatureResponse { 31 + id: string; 32 + result: string; 33 + } 34 + ``` 35 + 36 + ### 2. Create Use Case (`src/modules/{module}/application/useCases/`) 37 + 38 + Implement the business logic in a use case class: 39 + 40 + ```typescript 41 + export class MyFeatureUseCase extends BaseUseCase< 42 + MyFeatureDTO, 43 + Result<MyFeatureResponseDTO, ValidationError | AppError.UnexpectedError> 44 + > { 45 + constructor( 46 + private repository: IMyRepository, 47 + private domainService: MyDomainService, 48 + eventPublisher: IEventPublisher, 49 + ) { 50 + super(eventPublisher); 51 + } 52 + 53 + async execute(request: MyFeatureDTO): Promise<Result<...>> { 54 + // Business logic implementation 55 + } 56 + } 57 + ``` 58 + 59 + ### 3. Create Controller (`src/modules/{module}/infrastructure/http/controllers/`) 60 + 61 + Handle HTTP requests and delegate to use cases: 62 + 63 + ```typescript 64 + export class MyFeatureController extends Controller { 65 + constructor(private useCase: MyFeatureUseCase) { 66 + super(); 67 + } 68 + 69 + async executeImpl(req: AuthenticatedRequest, res: Response): Promise<any> { 70 + const result = await this.useCase.execute(req.body); 71 + 72 + if (result.isErr()) { 73 + return this.fail(res, result.error); 74 + } 75 + 76 + return this.ok(res, result.value); 77 + } 78 + } 79 + ``` 80 + 81 + ### 4. Add Route (`src/modules/{module}/infrastructure/http/routes/`) 82 + 83 + Define the HTTP endpoint: 84 + 85 + ```typescript 86 + router.post('/my-feature', authMiddleware.ensureAuthenticated(), (req, res) => 87 + myFeatureController.execute(req, res), 88 + ); 89 + ``` 90 + 91 + ### 5. Wire Dependencies (`src/shared/infrastructure/http/factories/`) 92 + 93 + **UseCaseFactory.ts** - Add use case instantiation: 94 + ```typescript 95 + myFeatureUseCase: new MyFeatureUseCase( 96 + repositories.myRepository, 97 + services.myDomainService, 98 + services.eventPublisher, 99 + ), 100 + ``` 101 + 102 + **ControllerFactory.ts** - Add controller instantiation: 103 + ```typescript 104 + myFeatureController: new MyFeatureController( 105 + useCases.myFeatureUseCase, 106 + ), 107 + ``` 108 + 109 + ### 6. Add API Client Method (`src/webapp/api-client/`) 110 + 111 + **Client Class** (`clients/MyClient.ts`): 112 + ```typescript 113 + export class MyClient extends BaseClient { 114 + async myFeature(request: MyFeatureRequest): Promise<MyFeatureResponse> { 115 + return this.request<MyFeatureResponse>('POST', '/api/my-feature', request); 116 + } 117 + } 118 + ``` 119 + 120 + **Main API Client** (`ApiClient.ts`): 121 + ```typescript 122 + async myFeature(request: MyFeatureRequest): Promise<MyFeatureResponse> { 123 + return this.myClient.myFeature(request); 124 + } 125 + ``` 126 + 127 + ## Key Patterns 128 + 129 + ### Repository Pattern 130 + - Interface in domain layer (`src/modules/{module}/domain/`) 131 + - Implementation in infrastructure layer (`src/modules/{module}/infrastructure/repositories/`) 132 + - Both real (Drizzle) and in-memory test implementations 133 + 134 + ### Domain Services 135 + - Business logic that doesn't belong to a single entity 136 + - Located in `src/modules/{module}/domain/services/` 137 + - Injected into use cases 138 + 139 + ### Value Objects 140 + - Immutable objects representing domain concepts 141 + - Located in `src/modules/{module}/domain/value-objects/` 142 + - Include validation logic 143 + 144 + ### Error Handling 145 + - Use `Result<T, E>` pattern for error handling 146 + - Custom error types extend `UseCaseError` 147 + - Controllers handle different error types appropriately 148 + 149 + ### Authentication 150 + - Use `AuthMiddleware.ensureAuthenticated()` for protected routes 151 + - Use `AuthMiddleware.optionalAuth()` for routes that work with/without auth 152 + - Access user DID via `req.did` in controllers 153 + 154 + ### Event Publishing 155 + - Use cases extend `BaseUseCase` for event publishing 156 + - Call `this.publishEventsForAggregate(entity)` after operations 157 + - Events are handled by separate worker processes 158 + 159 + ## File Structure Example 160 + 161 + For a feature called "MyFeature" in the "cards" module: 162 + 163 + ``` 164 + src/ 165 + ├── types/src/api/ 166 + │ ├── requests.ts (add MyFeatureRequest) 167 + │ └── responses.ts (add MyFeatureResponse) 168 + ├── modules/cards/ 169 + │ ├── domain/ 170 + │ │ ├── IMyRepository.ts 171 + │ │ ├── services/MyDomainService.ts 172 + │ │ └── value-objects/MyValueObject.ts 173 + │ ├── application/useCases/ 174 + │ │ └── commands/MyFeatureUseCase.ts 175 + │ └── infrastructure/ 176 + │ ├── repositories/DrizzleMyRepository.ts 177 + │ └── http/ 178 + │ ├── controllers/MyFeatureController.ts 179 + │ └── routes/ (update existing route file) 180 + ├── shared/infrastructure/http/factories/ 181 + │ ├── UseCaseFactory.ts (add use case) 182 + │ ├── ControllerFactory.ts (add controller) 183 + │ └── RepositoryFactory.ts (add repository) 184 + └── webapp/api-client/ 185 + ├── clients/MyClient.ts 186 + └── ApiClient.ts (add method) 187 + ``` 188 + 189 + ## Testing Strategy 190 + 191 + - **Unit Tests**: Test use cases and domain services in isolation 192 + - **Integration Tests**: Test controllers with real dependencies 193 + - **In-Memory Implementations**: Use for testing and development 194 + - **Mock Services**: Use `Fake*` implementations for external dependencies 195 + 196 + ## Configuration 197 + 198 + - Use `EnvironmentConfigService` for environment-specific settings 199 + - Support both mock and real implementations via config flags 200 + - Factory pattern allows switching implementations based on environment 201 + 202 + This pattern ensures consistency, testability, and maintainability across all features in the codebase.