|
| 1 | +# Design Document |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +This design document outlines the migration strategy for transitioning the notifyer-cron serverless application from AWS SDK for JavaScript v2 to v3. The migration focuses on maintaining functional equivalence while leveraging v3's performance improvements, reduced bundle sizes, and modular architecture. |
| 6 | + |
| 7 | +The primary changes involve replacing the v2 `AWS.DynamoDB.DocumentClient` with v3's modular `@aws-sdk/client-dynamodb` and `@aws-sdk/lib-dynamodb` packages, updating import statements, client instantiation patterns, and error handling to align with v3's architecture. |
| 8 | + |
| 9 | +## Architecture |
| 10 | + |
| 11 | +### Current Architecture (v2) |
| 12 | +``` |
| 13 | +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ |
| 14 | +│ Lambda │ │ AWS SDK v2 │ │ DynamoDB │ |
| 15 | +│ Functions │───▶│ DocumentClient │───▶│ Table │ |
| 16 | +│ │ │ (93.6MB) │ │ │ |
| 17 | +└─────────────────┘ └──────────────────┘ └─────────────────┘ |
| 18 | +``` |
| 19 | + |
| 20 | +### Target Architecture (v3) |
| 21 | +``` |
| 22 | +┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ |
| 23 | +│ Lambda │ │ AWS SDK v3 │ │ DynamoDB │ |
| 24 | +│ Functions │───▶│ Modular Client │───▶│ Table │ |
| 25 | +│ │ │ (17MB) │ │ │ |
| 26 | +└─────────────────┘ └──────────────────┘ └─────────────────┘ |
| 27 | + │ |
| 28 | + ├─ @aws-sdk/client-dynamodb |
| 29 | + └─ @aws-sdk/lib-dynamodb |
| 30 | +``` |
| 31 | + |
| 32 | +### Migration Strategy |
| 33 | +The migration will follow a **direct replacement approach** rather than gradual migration, ensuring: |
| 34 | +- Single deployment with complete v3 implementation |
| 35 | +- Minimal code changes through strategic abstraction |
| 36 | +- Preservation of existing functionality and error handling patterns |
| 37 | +- Optimization for serverless/Lambda environment |
| 38 | + |
| 39 | +## Components and Interfaces |
| 40 | + |
| 41 | +### 1. Database Persistence Layer (`db/persist.js`) |
| 42 | + |
| 43 | +**Current Implementation:** |
| 44 | +```javascript |
| 45 | +const AWS = require('aws-sdk') |
| 46 | +const documentClient = new AWS.DynamoDB.DocumentClient({ |
| 47 | + apiVersion: '2012-10-08' |
| 48 | +}) |
| 49 | +``` |
| 50 | + |
| 51 | +**Target Implementation:** |
| 52 | +```javascript |
| 53 | +const { DynamoDBClient } = require('@aws-sdk/client-dynamodb') |
| 54 | +const { DynamoDBDocumentClient, GetCommand, UpdateCommand } = require('@aws-sdk/lib-dynamodb') |
| 55 | + |
| 56 | +const client = new DynamoDBClient({ |
| 57 | + region: process.env.REGION |
| 58 | +}) |
| 59 | + |
| 60 | +const documentClient = DynamoDBDocumentClient.from(client, { |
| 61 | + marshallOptions: { |
| 62 | + removeUndefinedValues: true // Maintain v2 behavior |
| 63 | + } |
| 64 | +}) |
| 65 | +``` |
| 66 | + |
| 67 | +**Interface Changes:** |
| 68 | +- Replace `.promise()` calls with direct `await` on `send()` method |
| 69 | +- Update error handling to account for v3's error structure (`error.$metadata`) |
| 70 | +- Maintain existing `getItem()` and `setItem()` function signatures |
| 71 | + |
| 72 | +### 2. Package Dependencies |
| 73 | + |
| 74 | +**Removals:** |
| 75 | +- `aws-sdk` (v2) from devDependencies |
| 76 | + |
| 77 | +**Additions:** |
| 78 | +- `@aws-sdk/client-dynamodb` (v3) |
| 79 | +- `@aws-sdk/lib-dynamodb` (v3) |
| 80 | + |
| 81 | +**Bundle Size Impact:** |
| 82 | +- Current: ~93.6MB install size |
| 83 | +- Target: ~17MB install size |
| 84 | +- Runtime bundle reduction: ~3.4MB → ~234KB |
| 85 | + |
| 86 | +### 3. Serverless Plugin Compatibility |
| 87 | + |
| 88 | +**Current Plugins:** |
| 89 | +- `serverless-webpack` (v5.11.0) |
| 90 | +- `serverless-offline` (v12.0.4) |
| 91 | +- `serverless-dynamodb-local` (v0.2.40) - currently commented out |
| 92 | + |
| 93 | +**Compatibility Assessment:** |
| 94 | +- **serverless-webpack**: Generally compatible with v3, may need version verification |
| 95 | +- **serverless-offline**: Should work with v3 SDK operations, requires testing |
| 96 | +- **serverless-dynamodb-local**: May need updates for v3 compatibility if re-enabled |
| 97 | + |
| 98 | +**Webpack Configuration:** |
| 99 | +```javascript |
| 100 | +// webpack.config.js - current configuration maintained |
| 101 | +module.exports = { |
| 102 | + externals: { |
| 103 | + // AWS SDK v3 modules will be bundled for optimal tree-shaking |
| 104 | + // No externals needed as v3 is designed for bundling |
| 105 | + } |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +**Serverless Framework Integration:** |
| 110 | +- Maintain existing `serverless-webpack` plugin configuration |
| 111 | +- Leverage v3's improved tree-shaking for smaller bundles |
| 112 | +- No changes required to `serverless.yml` |
| 113 | +- Verify plugin compatibility and upgrade if necessary |
| 114 | + |
| 115 | +**Plugin Compatibility Research Required:** |
| 116 | +- Verify serverless-webpack v5.11.0 works with AWS SDK v3 modular imports |
| 117 | +- Test serverless-offline v12.0.4 compatibility with v3 SDK operations |
| 118 | +- Check if serverless-dynamodb-local v0.2.40 needs updates for v3 (if re-enabled) |
| 119 | +- Document any required plugin version upgrades or configuration changes |
| 120 | + |
| 121 | +## Data Models |
| 122 | + |
| 123 | +### DynamoDB Operations Mapping |
| 124 | + |
| 125 | +| Operation | v2 Implementation | v3 Implementation | |
| 126 | +|-----------|-------------------|-------------------| |
| 127 | +| Get Item | `documentClient.get(params).promise()` | `documentClient.send(new GetCommand(params))` | |
| 128 | +| Update Item | `documentClient.update(params).promise()` | `documentClient.send(new UpdateCommand(params))` | |
| 129 | + |
| 130 | +### Error Structure Changes |
| 131 | + |
| 132 | +**v2 Error Structure:** |
| 133 | +```javascript |
| 134 | +{ |
| 135 | + code: 'ResourceNotFoundException', |
| 136 | + statusCode: 400, |
| 137 | + // ... other top-level properties |
| 138 | +} |
| 139 | +``` |
| 140 | + |
| 141 | +**v3 Error Structure:** |
| 142 | +```javascript |
| 143 | +{ |
| 144 | + name: 'ResourceNotFoundException', |
| 145 | + $metadata: { |
| 146 | + httpStatusCode: 400, |
| 147 | + // ... metadata in subfield |
| 148 | + } |
| 149 | +} |
| 150 | +``` |
| 151 | + |
| 152 | +### Data Marshalling Considerations |
| 153 | + |
| 154 | +**Key Configuration:** |
| 155 | +```javascript |
| 156 | +const documentClient = DynamoDBDocumentClient.from(client, { |
| 157 | + marshallOptions: { |
| 158 | + removeUndefinedValues: true, // Replicate v2 behavior |
| 159 | + convertEmptyValues: false // Maintain current handling |
| 160 | + } |
| 161 | +}) |
| 162 | +``` |
| 163 | + |
| 164 | +## Error Handling |
| 165 | + |
| 166 | +### Error Handling Strategy |
| 167 | + |
| 168 | +1. **Preserve Existing Patterns:** Maintain current try/catch blocks and error propagation |
| 169 | +2. **Adapt Error Structure:** Update error property access to use v3's structure |
| 170 | +3. **Logging Consistency:** Ensure error logs maintain current format |
| 171 | + |
| 172 | +### Implementation Approach |
| 173 | + |
| 174 | +**Current Error Handling:** |
| 175 | +```javascript |
| 176 | +try { |
| 177 | + const data = await documentClient.get(params).promise() |
| 178 | + return parse ? JSON.parse(data.Item[itemName]) : data.Item[itemName] |
| 179 | +} catch (err) { |
| 180 | + console.error(`Error getting db item: '${itemName}'`) |
| 181 | + console.error(err) |
| 182 | + throw err |
| 183 | +} |
| 184 | +``` |
| 185 | + |
| 186 | +**Target Error Handling:** |
| 187 | +```javascript |
| 188 | +try { |
| 189 | + const data = await documentClient.send(new GetCommand(params)) |
| 190 | + return parse ? JSON.parse(data.Item[itemName]) : data.Item[itemName] |
| 191 | +} catch (err) { |
| 192 | + console.error(`Error getting db item: '${itemName}'`) |
| 193 | + console.error(err) |
| 194 | + throw err // Error structure will be v3 format but handling remains the same |
| 195 | +} |
| 196 | +``` |
| 197 | + |
| 198 | +### Error Compatibility Layer |
| 199 | + |
| 200 | +No compatibility layer needed as: |
| 201 | +- Error throwing/catching patterns remain unchanged |
| 202 | +- Console logging will work with v3 error objects |
| 203 | +- Application-level error handling doesn't depend on specific error properties |
| 204 | + |
| 205 | +## Testing Strategy |
| 206 | + |
| 207 | +### Unit Testing Approach |
| 208 | + |
| 209 | +1. **Functional Equivalence Testing:** |
| 210 | + - Test all DynamoDB operations return identical results |
| 211 | + - Verify error scenarios produce equivalent behavior |
| 212 | + - Validate data marshalling/unmarshalling consistency |
| 213 | + |
| 214 | +2. **Integration Testing:** |
| 215 | + - Test complete Lambda function execution |
| 216 | + - Verify serverless deployment process |
| 217 | + - Test local development with serverless-offline |
| 218 | + - Validate AWS IAM permissions compatibility |
| 219 | + - Test serverless plugin functionality |
| 220 | + |
| 221 | +3. **Performance Testing:** |
| 222 | + - Measure cold start time improvements |
| 223 | + - Validate bundle size reductions |
| 224 | + - Monitor memory usage changes |
| 225 | + |
| 226 | +### Test Implementation Strategy |
| 227 | + |
| 228 | +**Mock Testing:** |
| 229 | +```javascript |
| 230 | +// Use aws-sdk-client-mock for v3 testing |
| 231 | +const { mockClient } = require('aws-sdk-client-mock') |
| 232 | +const { DynamoDBDocumentClient, GetCommand } = require('@aws-sdk/lib-dynamodb') |
| 233 | + |
| 234 | +const ddbMock = mockClient(DynamoDBDocumentClient) |
| 235 | +ddbMock.on(GetCommand).resolves({ Item: { test: 'data' } }) |
| 236 | +``` |
| 237 | + |
| 238 | +**Deployment Testing:** |
| 239 | +1. Deploy to development environment |
| 240 | +2. Execute full application workflow |
| 241 | +3. Verify data persistence and retrieval |
| 242 | +4. Confirm monitoring and logging functionality |
| 243 | + |
| 244 | +### Rollback Strategy |
| 245 | + |
| 246 | +**Immediate Rollback Capability:** |
| 247 | +- Maintain v2 implementation in version control |
| 248 | +- Prepare rollback deployment package |
| 249 | +- Document rollback procedure |
| 250 | + |
| 251 | +**Rollback Triggers:** |
| 252 | +- Functional regression detected |
| 253 | +- Performance degradation beyond acceptable thresholds |
| 254 | +- Deployment failures in production environment |
| 255 | + |
| 256 | +## Performance Considerations |
| 257 | + |
| 258 | +### Bundle Size Optimization |
| 259 | + |
| 260 | +**Tree-Shaking Benefits:** |
| 261 | +- v3's modular architecture enables automatic dead code elimination |
| 262 | +- Only required DynamoDB operations will be included in bundle |
| 263 | +- Estimated 85% reduction in AWS SDK bundle size |
| 264 | + |
| 265 | +**Cold Start Improvements:** |
| 266 | +- Smaller bundle size reduces Lambda initialization time |
| 267 | +- Modular imports reduce memory footprint |
| 268 | +- Estimated 10-30% improvement in cold start performance |
| 269 | + |
| 270 | +### Memory Usage |
| 271 | + |
| 272 | +**v2 vs v3 Memory Profile:** |
| 273 | +- v2: Loads entire AWS SDK into memory |
| 274 | +- v3: Loads only required service clients |
| 275 | +- Expected memory usage reduction: 20-40% |
| 276 | + |
| 277 | +### Runtime Performance |
| 278 | + |
| 279 | +**Operation Performance:** |
| 280 | +- DynamoDB operations: Equivalent performance expected |
| 281 | +- Client initialization: Slightly faster due to reduced overhead |
| 282 | +- Error handling: Minimal performance impact |
| 283 | + |
| 284 | +## Deployment Strategy |
| 285 | + |
| 286 | +### Deployment Approach |
| 287 | + |
| 288 | +**Single-Phase Deployment:** |
| 289 | +1. Research and verify serverless plugin compatibility |
| 290 | +2. Update package.json dependencies (including plugin upgrades if needed) |
| 291 | +3. Modify db/persist.js implementation |
| 292 | +4. Test local development workflow with plugins |
| 293 | +5. Deploy complete application |
| 294 | +6. Monitor for issues |
| 295 | +7. Rollback if necessary |
| 296 | + |
| 297 | +### Deployment Validation |
| 298 | + |
| 299 | +**Pre-Deployment Checklist:** |
| 300 | +- [ ] Serverless plugin compatibility verified |
| 301 | +- [ ] Unit tests pass |
| 302 | +- [ ] Integration tests pass |
| 303 | +- [ ] Local development workflow tested (serverless-offline, etc.) |
| 304 | +- [ ] Bundle size verification |
| 305 | +- [ ] Serverless configuration validation |
| 306 | + |
| 307 | +**Post-Deployment Verification:** |
| 308 | +- [ ] Lambda functions execute successfully |
| 309 | +- [ ] DynamoDB operations function correctly |
| 310 | +- [ ] Error handling works as expected |
| 311 | +- [ ] Performance metrics within acceptable ranges |
| 312 | +- [ ] Monitoring and logging operational |
| 313 | + |
| 314 | +### Risk Mitigation |
| 315 | + |
| 316 | +**Low-Risk Factors:** |
| 317 | +- Minimal code changes required |
| 318 | +- Well-documented migration path |
| 319 | +- Extensive AWS documentation and community support |
| 320 | +- Backward-compatible error handling approach |
| 321 | + |
| 322 | +**Risk Mitigation Measures:** |
| 323 | +- Comprehensive testing in development environment |
| 324 | +- Gradual rollout capability (if needed) |
| 325 | +- Immediate rollback procedure |
| 326 | +- Monitoring alerts for performance degradation |
| 327 | + |
| 328 | +## Monitoring and Observability |
| 329 | + |
| 330 | +### Metrics to Monitor |
| 331 | + |
| 332 | +**Performance Metrics:** |
| 333 | +- Lambda cold start duration |
| 334 | +- Lambda execution duration |
| 335 | +- Memory utilization |
| 336 | +- DynamoDB operation latency |
| 337 | + |
| 338 | +**Functional Metrics:** |
| 339 | +- Error rates |
| 340 | +- Success rates for DynamoDB operations |
| 341 | +- Application workflow completion rates |
| 342 | + |
| 343 | +**Cost Metrics:** |
| 344 | +- Lambda execution costs |
| 345 | +- DynamoDB request costs |
| 346 | +- Data transfer costs |
| 347 | + |
| 348 | +### Logging Strategy |
| 349 | + |
| 350 | +**Maintain Current Logging:** |
| 351 | +- Preserve existing console.log statements |
| 352 | +- Maintain error logging format |
| 353 | +- Ensure CloudWatch integration continues |
| 354 | + |
| 355 | +**Enhanced Logging (Optional):** |
| 356 | +- Add v3-specific performance metrics |
| 357 | +- Include bundle size information |
| 358 | +- Log client initialization details |
| 359 | + |
| 360 | +This design provides a comprehensive migration strategy that minimizes risk while maximizing the benefits of AWS SDK v3's improved architecture and performance characteristics. |
0 commit comments