9.3 KiB
9.3 KiB
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
// 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
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
// 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
-Fflag
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 /cfor .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 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.Propertywarnings - 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
- Start with Standard Naming: Use industry-standard property names from the beginning
- Plan Authentication Early: Decide on authentication strategy before implementation
- Test Continuously: Fix test issues as they arise, don't let them accumulate
- Document as You Go: Keep CLAUDE.md updated with decisions and patterns
- Consider the Database: Some features (like decimal ordering) are database-specific
- Mock External Dependencies: Always mock file system, email, etc. in tests
- Use DTOs Consistently: Don't expose entities directly through APIs
- Handle Errors Gracefully: Implement proper error handling at all layers
- Security First: Never have public endpoints if not required
- Client SDK Value: A well-designed client SDK greatly improves API usability
🔄 Refactoring Opportunities
Future Improvements
- Caching Layer: Add Redis for frequently accessed data
- Message Queue: Implement for order processing
- Event Sourcing: For order status changes
- API Versioning: Prepare for future API changes
- Health Checks: Add health check endpoints
- Metrics: Implement application metrics
- Rate Limiting: Add rate limiting to API endpoints
- Background Jobs: Use Hangfire or similar for async processing
📝 Development Workflow Tips
- Always Check CLAUDE.md: Project-specific instructions override defaults
- Use TodoWrite Tool: Track multi-step tasks systematically
- Build Frequently: Catch compilation errors early
- Commit Logically: Group related changes in commits
- Test After Major Changes: Run tests after significant refactoring
- Document Decisions: Record why, not just what
- Consider Side Effects: Model changes affect DTOs, services, views, and tests
🛠️ Tool-Specific Lessons
Visual Studio Code / WSL
- Use
cmd.exe /cwrapper 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
.gitattributesto 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.