|
| 1 | +# GitHub Copilot Instructions |
| 2 | + |
| 3 | +These instructions guide GitHub Copilot on how to assist meaningfully within this repository. |
| 4 | + |
| 5 | +## 🎯 Project Overview |
| 6 | + |
| 7 | +This project is a proof-of-concept Web API built using: |
| 8 | +- **.NET 8 (LTS)** |
| 9 | +- **ASP.NET Core** |
| 10 | +- **EF Core** with a **SQLite** database for simplicity |
| 11 | +- **Docker Compose** for basic containerization |
| 12 | + |
| 13 | +### Key Characteristics |
| 14 | +- **Purpose**: Learning-focused PoC demonstrating modern ASP.NET Core patterns |
| 15 | +- **Complexity**: Simple CRUD operations with a single `Player` entity |
| 16 | +- **Focus**: Clean architecture, best practices, and maintainable code |
| 17 | +- **Database**: SQLite for development; PostgreSQL may be introduced later |
| 18 | + |
| 19 | +## ✅ Coding Conventions |
| 20 | + |
| 21 | +Follow standard C# conventions: |
| 22 | +- Use `PascalCase` for class names, methods, and public properties |
| 23 | +- Use `camelCase` for local variables and private fields |
| 24 | +- Use `async/await` consistently for asynchronous code |
| 25 | +- Prefer `var` for local variable declarations where the type is obvious |
| 26 | +- Nullable reference types are **enabled** |
| 27 | +- Use `CSharpier` formatting standards (opinionated) |
| 28 | + |
| 29 | +## 🏗️ Architectural Patterns |
| 30 | + |
| 31 | +This project follows a **layered architecture** with clear separation of concerns: |
| 32 | + |
| 33 | +### Layer Structure |
| 34 | +- **Controllers** (`/Controllers`) - Handle HTTP requests and responses |
| 35 | +- **Services** (`/Services`) - Contain business logic and orchestration |
| 36 | +- **Repositories** (`/Repositories`) - Abstract data access layer |
| 37 | +- **Models** (`/Models`) - Domain entities and DTOs |
| 38 | + - `Player` - Core domain entity |
| 39 | + - `PlayerRequestModel` - Input validation model |
| 40 | + - `PlayerResponseModel` - API response model |
| 41 | +- **Validators** (`/Validators`) - FluentValidation rules |
| 42 | +- **Mappings** (`/Mappings`) - AutoMapper configurations |
| 43 | +- **Data** (`/Data`) - EF Core DbContext and database concerns |
| 44 | + |
| 45 | +### Design Principles |
| 46 | +- **Dependency Injection** for all services and repositories |
| 47 | +- **Repository Pattern** for data access abstraction |
| 48 | +- **Service Layer** for business logic encapsulation |
| 49 | +- **AutoMapper** for clean object transformations |
| 50 | +- **FluentValidation** for robust input validation |
| 51 | +- **Async/Await** throughout the application stack |
| 52 | + |
| 53 | +## ✅ Copilot Should Focus On |
| 54 | + |
| 55 | +- Generating idiomatic ASP.NET Core controller actions |
| 56 | +- Writing EF Core queries using LINQ |
| 57 | +- Following async programming practices |
| 58 | +- Producing unit tests using **xUnit** |
| 59 | +- Suggesting dependency-injected services |
| 60 | +- Adhering to RESTful naming and HTTP status codes |
| 61 | +- Using `ILogger<T>` for logging |
| 62 | +- Working with Docker-friendly patterns |
| 63 | +- Implementing proper error handling and validation |
| 64 | +- Using appropriate HTTP status codes (200, 201, 400, 404, 409, etc.) |
| 65 | +- Following the existing caching patterns with `IMemoryCache` |
| 66 | + |
| 67 | +## 🚫 Copilot Should Avoid |
| 68 | + |
| 69 | +- Generating raw SQL unless explicitly required |
| 70 | +- Using EF Core synchronous APIs (e.g., `FirstOrDefault` over `FirstOrDefaultAsync`) |
| 71 | +- Suggesting static service or repository classes |
| 72 | +- Including XML comments or doc stubs unless requested |
| 73 | +- Suggesting patterns that conflict with DI (e.g., `new Service()` instead of constructor injection) |
| 74 | +- Using `ConfigureAwait(false)` in ASP.NET Core contexts |
| 75 | +- Implementing complex inheritance hierarchies when composition is simpler |
| 76 | +- Adding unnecessary middleware or filters without clear purpose |
| 77 | + |
| 78 | +## 🧪 Testing |
| 79 | + |
| 80 | +- Use **xUnit** |
| 81 | +- Use **Moq** for mocking |
| 82 | +- Prefer testing **service logic** and **controller behavior** |
| 83 | +- Place unit tests under `test/` following structure already present (e.g., `Unit/PlayerServiceTests.cs`) |
| 84 | +- **Test Data**: Use faker patterns for consistent test data generation |
| 85 | +- **Assertions**: FluentAssertions for readable test assertions |
| 86 | + |
| 87 | +## ⚡ Performance & Best Practices |
| 88 | + |
| 89 | +- Use `AsNoTracking()` for read-only EF Core queries |
| 90 | +- Implement caching patterns with `IMemoryCache` for frequently accessed data |
| 91 | +- Use `DbContextPool` for better performance (already configured) |
| 92 | +- Follow async/await patterns consistently |
| 93 | +- Validate input using **FluentValidation** before processing |
| 94 | +- Use AutoMapper for object transformations |
| 95 | +- Implement proper logging with structured logging patterns |
| 96 | + |
| 97 | +## 🔧 Tooling & Environment |
| 98 | + |
| 99 | +- Format code with **CSharpier** |
| 100 | +- SQLite is used in development; **PostgreSQL** may be introduced in production later |
| 101 | +- Code runs in a **Docker Compose** environment |
| 102 | +- .NET 8 SDK is required |
| 103 | +- All configurations live in `appsettings.*.json` files |
| 104 | + |
| 105 | +## 🏷️ Technology Stack Deep Dive |
| 106 | + |
| 107 | +### Entity Framework Core |
| 108 | +- **DbContext**: `PlayerDbContext` with SQLite provider |
| 109 | +- **Migrations**: Used for schema management and data seeding |
| 110 | +- **Pooling**: `AddDbContextPool` for better performance |
| 111 | +- **Query Optimization**: `AsNoTracking()` for read-only operations |
| 112 | +- **Async Operations**: All database calls use async/await |
| 113 | + |
| 114 | +### AutoMapper |
| 115 | +- **Profile**: `PlayerMappingProfile` handles all object mappings |
| 116 | +- **Bidirectional**: Maps between request/response models and entities |
| 117 | +- **Integration**: Registered in DI container |
| 118 | + |
| 119 | +### FluentValidation |
| 120 | +- **Validators**: `PlayerRequestModelValidator` for input validation |
| 121 | +- **Integration**: Automatic validation in controllers before processing |
| 122 | +- **Error Messages**: Descriptive validation messages |
| 123 | + |
| 124 | +### Caching Strategy |
| 125 | +- **IMemoryCache**: Service-level caching for read operations |
| 126 | +- **Cache Keys**: Consistent naming with `nameof()` pattern |
| 127 | +- **Invalidation**: Cache cleared on data modifications |
| 128 | +- **TTL**: Sliding expiration (10 min) + absolute expiration (1 hour) |
| 129 | + |
| 130 | +### Logging with Serilog |
| 131 | +- **Structured Logging**: Consistent log message templates |
| 132 | +- **Log Levels**: Appropriate use of Information, Warning, Error |
| 133 | +- **Context**: Include relevant data in log messages |
| 134 | +- **Configuration**: File and console sinks configured |
| 135 | + |
| 136 | +## 🧩 Folder Conventions |
| 137 | + |
| 138 | +- `Controllers` for Web API endpoints |
| 139 | +- `Services` for business logic |
| 140 | +- `Repositories` for data access |
| 141 | +- `Models` for domain and DTO objects |
| 142 | +- `Mappings` for AutoMapper profiles |
| 143 | +- `Validators` for FluentValidation rules |
| 144 | +- `Utilities` for shared helper logic |
| 145 | + |
| 146 | +## 🧘 General Philosophy |
| 147 | + |
| 148 | +Keep things **simple, clear, and idiomatic**. This is a learning-focused PoC — clarity and maintainability win over overengineering. |
| 149 | + |
| 150 | +## 📋 Common Patterns in This Codebase |
| 151 | + |
| 152 | +### Repository Pattern |
| 153 | +```csharp |
| 154 | +// Generic repository base class |
| 155 | +public class Repository<T> : IRepository<T> where T : class |
| 156 | +{ |
| 157 | + protected readonly DbSet<T> _dbSet; |
| 158 | + // Standard CRUD operations with async/await |
| 159 | +} |
| 160 | + |
| 161 | +// Specific repository with custom queries |
| 162 | +public class PlayerRepository : Repository<Player>, IPlayerRepository |
| 163 | +{ |
| 164 | + public async Task<Player?> FindBySquadNumberAsync(int squadNumber) => |
| 165 | + await _dbSet.FirstOrDefaultAsync(p => p.SquadNumber == squadNumber); |
| 166 | +} |
| 167 | +``` |
| 168 | + |
| 169 | +### Service Layer Pattern |
| 170 | +```csharp |
| 171 | +public class PlayerService : IPlayerService |
| 172 | +{ |
| 173 | + // Dependencies injected via constructor |
| 174 | + // Business logic with caching |
| 175 | + // AutoMapper for transformations |
| 176 | + // Logging for observability |
| 177 | +} |
| 178 | +``` |
| 179 | + |
| 180 | +### Controller Pattern |
| 181 | +```csharp |
| 182 | +[ApiController] |
| 183 | +[Route("players")] |
| 184 | +public class PlayerController : ControllerBase |
| 185 | +{ |
| 186 | + // Minimal controllers - delegate to services |
| 187 | + // Proper HTTP status codes |
| 188 | + // FluentValidation integration |
| 189 | + // Structured logging |
| 190 | +} |
| 191 | +``` |
| 192 | + |
| 193 | +## 🎯 Domain Knowledge |
| 194 | + |
| 195 | +### Player Entity Context |
| 196 | +- **Squad Numbers**: Must be unique (1-99 typically) |
| 197 | +- **Positions**: Football/Soccer positions with 2-character abbreviations (GK, RB, LB, CB, DM, CM, RW, AM, CF, SS, LW) |
| 198 | +- **Starting11**: Boolean indicating if player is in starting lineup |
| 199 | +- **Team/League**: String fields for team and league information |
| 200 | + |
| 201 | +### Business Rules |
| 202 | +- Squad numbers cannot be duplicated |
| 203 | +- All operations should be logged for traceability |
| 204 | +- Cache invalidation on data modifications |
| 205 | +- Async operations throughout the stack |
| 206 | + |
| 207 | +## 🚨 Error Handling Patterns |
| 208 | + |
| 209 | +```csharp |
| 210 | +// Controller level - return appropriate HTTP status codes |
| 211 | +if (await playerService.RetrieveBySquadNumberAsync(squadNumber) != null) |
| 212 | +{ |
| 213 | + return TypedResults.Conflict($"Squad number {squadNumber} already exists"); |
| 214 | +} |
| 215 | + |
| 216 | +// Service level - structured logging |
| 217 | +logger.LogWarning("Player with squad number {SquadNumber} not found", squadNumber); |
| 218 | + |
| 219 | +// Repository level - null handling |
| 220 | +var player = await _dbSet.FirstOrDefaultAsync(p => p.Id == id); |
| 221 | +return player; // Let caller handle null |
| 222 | +``` |
| 223 | + |
| 224 | +## 🔄 Data Flow Patterns |
| 225 | + |
| 226 | +1. **Request Flow**: Controller → Validation → Service → Repository → Database |
| 227 | +2. **Response Flow**: Database → Repository → Service → AutoMapper → Controller → Client |
| 228 | +3. **Caching**: Service layer implements `IMemoryCache` for read operations |
| 229 | +4. **Logging**: Structured logging at each layer for observability |
| 230 | + |
| 231 | +## 🎯 When to Use Different Approaches |
| 232 | + |
| 233 | +### Choose EF Core When: |
| 234 | +- Simple CRUD operations (current use case) |
| 235 | +- Rapid development needed |
| 236 | +- Strong typing and compile-time checking preferred |
| 237 | +- Schema migrations are important |
| 238 | + |
| 239 | +### Consider Raw SQL/Dapper When: |
| 240 | +- Complex queries with performance requirements |
| 241 | +- Need fine-grained control over SQL |
| 242 | +- Working with existing stored procedures |
| 243 | +- Micro-service with minimal ORM overhead |
| 244 | + |
| 245 | +## 🌐 API Design Guidelines |
| 246 | + |
| 247 | +- Use proper HTTP verbs (GET, POST, PUT, DELETE) |
| 248 | +- Return appropriate status codes (200, 201, 400, 404, 409, 500) |
| 249 | +- Implement consistent error response formats |
| 250 | +- Use route parameters for resource identification |
| 251 | +- Apply validation before processing requests |
| 252 | + |
| 253 | +## 🚀 Future Evolution Considerations |
| 254 | + |
| 255 | +- **Database Migration**: SQLite → PostgreSQL transition path |
| 256 | +- **Authentication**: JWT Bearer token implementation ready |
| 257 | +- **API Versioning**: URL-based versioning strategy |
| 258 | +- **OpenAPI**: Comprehensive Swagger documentation |
| 259 | +- **Monitoring**: Health checks and metrics endpoints |
| 260 | +- **Containerization**: Docker multi-stage builds optimized |
0 commit comments