littleshop/DEVELOPMENT_LESSONS.md
2025-08-27 18:02:39 +01:00

231 lines
9.3 KiB
Markdown

# LittleShop Development - Technical Lessons Learned
## 🔑 Critical Discoveries & Solutions
### 1. **ASP.NET Core 9.0 Authentication Architecture**
- **Dual Authentication Schemes**: Successfully implemented both Cookie (for MVC Admin Panel) and JWT Bearer (for API) authentication in the same application
- **Key Learning**: Must specify authentication scheme explicitly when using multiple schemes
- **Implementation**: Cookie auth uses `[Authorize(AuthenticationSchemes = "Cookies")]`, JWT uses `[Authorize(AuthenticationSchemes = "Bearer")]`
### 2. **Entity Framework Core with SQLite**
- **Decimal Ordering Issue**: SQLite cannot order by decimal columns directly
- **Solution**: Load data into memory first, then apply ordering
```csharp
// Won't work in SQLite:
.OrderBy(sr => sr.MinWeight)
// Solution:
var rates = await _context.ShippingRates.ToListAsync();
return rates.OrderBy(sr => sr.MinWeight);
```
### 3. **Service Constructor Dependencies**
- **ProductService Evolution**: Initially had IMapper dependency, later changed to IWebHostEnvironment for file handling
- **CategoryService**: Simplified to only require DbContext, no mapper needed
- **Lesson**: Services evolved based on actual needs rather than anticipated patterns
### 4. **Model Property Naming Conventions**
- **Initial Design**: Used non-standard names (BasePrice, ProductWeight, ProductWeightUnit)
- **Refactored To**: Standard e-commerce names (Price, Weight, WeightUnit)
- **Impact**: Required updating all DTOs, services, views, and tests
- **Lesson**: Follow industry-standard naming conventions from the start
### 5. **Test Project Configuration**
- **Mock Dependencies**: Use Moq for IWebHostEnvironment in unit tests
- **In-Memory Database**: Works well for testing with `UseInMemoryDatabase()`
- **Authentication in Tests**: Create JWT tokens manually for integration tests
```csharp
var token = JwtTokenHelper.GenerateJwtToken();
_client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
```
### 6. **File Upload Implementation**
- **LINQ Translation Issues**: Complex LINQ queries with DefaultIfEmpty() don't translate to SQL
- **Solution**: Use nullable casts and null coalescing
```csharp
// Problematic:
.Select(pp => pp.SortOrder).DefaultIfEmpty(0).Max()
// Solution:
.Select(pp => (int?)pp.SortOrder).MaxAsync() ?? 0
```
### 7. **Git Commit in Windows/WSL**
- **Issue**: Complex multi-line commit messages fail with standard quotes
- **Solution**: Write commit message to file, use `-F` flag
```bash
git commit -F commit_msg.txt
```
### 8. **API Security Design Decision**
- **Critical Requirement**: NO public endpoints - all require authentication
- **Implementation**: Every API endpoint requires JWT token
- **Rationale**: Client applications handle public presentation after authentication
- **Impact**: Simplified security model, consistent authorization
### 9. **WSL2 Development Environment**
- **Command Execution**: Must use `cmd.exe /c` for .NET commands in WSL
- **File Locking**: Application must be stopped before rebuilding (common WSL issue)
- **Path Handling**: Use `/mnt/c/` for Windows paths in WSL
### 10. **Client SDK Design Patterns**
- **Retry Policies**: Polly integration for transient failure handling
- **Error Handling**: Middleware pattern for consistent error responses
- **DI Integration**: Extension methods for easy service registration
- **Response Wrapping**: ApiResponse<T> pattern for consistent error handling
## 📊 Database Design Decisions
### Shipping Information
- **Added to Orders**: Full shipping details (Name, Address, City, PostCode, Country)
- **Separate ShippingRates Table**: Weight-based calculation system
- **Lesson**: E-commerce requires comprehensive shipping information upfront
### Payment Status Evolution
- **Original Enum Values**: Pending, Confirmed, Failed
- **Changed To**: Pending, Paid, Failed, Expired, Cancelled
- **Reason**: Better alignment with cryptocurrency payment lifecycle
### Order Status Values
- **Original**: Pending, Processing, Shipped
- **Updated**: PendingPayment, PaymentReceived, Processing, Shipped, Delivered, Cancelled
- **Benefit**: More granular order tracking
## 🏗️ Architecture Patterns That Worked
### 1. **Service Layer Pattern**
- Clean separation between controllers and business logic
- Each service has its interface
- Easy to mock for testing
### 2. **DTO Pattern**
- Separate DTOs for Create, Update, and View operations
- Prevents over-posting attacks
- Clear API contracts
### 3. **Repository Pattern (via EF Core)**
- DbContext acts as Unit of Work
- No need for additional repository layer with EF Core
- Simplified data access
### 4. **Areas for Admin Panel**
- `/Areas/Admin/` structure keeps admin code separate
- Own controllers, views, and routing
- Clear separation of concerns
## 🐛 Common Issues & Fixes
### View Compilation Issues
- **Problem**: Runtime changes to views not reflected
- **Solution**: Restart application in production mode
- **Better Solution**: Use development mode for active development
### ModelState Validation
- **Issue**: Empty validation summaries appearing
- **Cause**: ModelState checking even for GET requests
- **Fix**: Only display validation summary on POST
### Nullable Reference Warnings
- **Common in Views**: `Model.Property` warnings
- **Solution**: Use null-conditional operator `Model?.Property`
- **Alternative**: Use `new()` initialization in view model
## 🚀 Performance Optimizations
### 1. **Query Optimization**
- Use `.Include()` for eager loading
- Use `.AsNoTracking()` for read-only queries
- Project to DTOs in queries to reduce data transfer
### 2. **Pagination Implementation**
- Always implement pagination for list endpoints
- Return metadata (total count, page info)
- Client-side should handle pagination UI
### 3. **File Upload Strategy**
- Store files on disk, not in database
- Save only file paths in database
- Implement file size limits
## 🔒 Security Best Practices Implemented
### 1. **Authentication**
- JWT tokens expire after 60 minutes
- Refresh token mechanism available
- No sensitive data in JWT claims
### 2. **Password Security**
- PBKDF2 with 100,000 iterations
- Unique salt per password
- Never store plain text passwords
### 3. **Input Validation**
- FluentValidation for complex validation
- Data annotations for simple validation
- Server-side validation always enforced
### 4. **CORS Configuration**
- Configured for specific domains in production
- AllowAll only in development
- Credentials handled properly
## 🎯 Key Takeaways
1. **Start with Standard Naming**: Use industry-standard property names from the beginning
2. **Plan Authentication Early**: Decide on authentication strategy before implementation
3. **Test Continuously**: Fix test issues as they arise, don't let them accumulate
4. **Document as You Go**: Keep CLAUDE.md updated with decisions and patterns
5. **Consider the Database**: Some features (like decimal ordering) are database-specific
6. **Mock External Dependencies**: Always mock file system, email, etc. in tests
7. **Use DTOs Consistently**: Don't expose entities directly through APIs
8. **Handle Errors Gracefully**: Implement proper error handling at all layers
9. **Security First**: Never have public endpoints if not required
10. **Client SDK Value**: A well-designed client SDK greatly improves API usability
## 🔄 Refactoring Opportunities
### Future Improvements
1. **Caching Layer**: Add Redis for frequently accessed data
2. **Message Queue**: Implement for order processing
3. **Event Sourcing**: For order status changes
4. **API Versioning**: Prepare for future API changes
5. **Health Checks**: Add health check endpoints
6. **Metrics**: Implement application metrics
7. **Rate Limiting**: Add rate limiting to API endpoints
8. **Background Jobs**: Use Hangfire or similar for async processing
## 📝 Development Workflow Tips
1. **Always Check CLAUDE.md**: Project-specific instructions override defaults
2. **Use TodoWrite Tool**: Track multi-step tasks systematically
3. **Build Frequently**: Catch compilation errors early
4. **Commit Logically**: Group related changes in commits
5. **Test After Major Changes**: Run tests after significant refactoring
6. **Document Decisions**: Record why, not just what
7. **Consider Side Effects**: Model changes affect DTOs, services, views, and tests
## 🛠️ Tool-Specific Lessons
### Visual Studio Code / WSL
- Use `cmd.exe /c` wrapper for Windows commands
- File watchers don't always work in WSL
- Hot reload is unreliable with WSL
### Entity Framework Core
- Migrations not always needed for development
- `EnsureCreated()` sufficient for prototyping
- Use migrations for production deployments
### Git in Mixed Environments
- Line ending issues (CRLF vs LF)
- Use `.gitattributes` to standardize
- Commit from consistent environment
## Final Wisdom
**"All endpoints must be authenticated"** - This single decision simplified the entire security model and eliminated a class of vulnerabilities. Sometimes the most restrictive choice is the best choice.
**"Standard names matter"** - Using `Price` instead of `BasePrice` seems trivial, but non-standard names cascade through the entire codebase, tests, and documentation.
**"Test the workflow, not just the code"** - Creating sample data that demonstrates the complete order workflow (pending → paid → shipped → delivered) helps validate the business logic, not just the technical implementation.