231 lines
9.3 KiB
Markdown
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. |