Files
work-club-manager/.sisyphus/evidence/task-11-implementation.txt

133 lines
4.2 KiB
Plaintext
Raw Normal View History

# Task 11: Seed Data Service Implementation Evidence
## Files Created
### 1. backend/src/WorkClub.Infrastructure/Seed/SeedDataService.cs
- **Purpose**: Provides idempotent seeding of development database
- **Key Features**:
- Deterministic GUID generation from entity names (MD5-based)
- Idempotent checks: Only seeds if no data exists
- IServiceScopeFactory injection for creating scoped DbContext
- Async SeedAsync() method for non-blocking seed operations
### 2. backend/src/WorkClub.Api/Program.cs (Modified)
- **Added Import**: `using WorkClub.Infrastructure.Seed;`
- **Service Registration**: `builder.Services.AddScoped<SeedDataService>();`
- **Development Startup**: Added seed execution in development environment only
```csharp
if (app.Environment.IsDevelopment())
{
using var scope = app.Services.CreateScope();
var seedService = scope.ServiceProvider.GetRequiredService<SeedDataService>();
await seedService.SeedAsync();
}
```
## Seed Data Structure
### Clubs (2 total)
1. **Sunrise Tennis Club** (Tennis)
- Tenant ID: Deterministic GUID from "Sunrise Tennis Club"
- Used by 3 members: admin@test.com, manager@test.com, member1@test.com, member2@test.com, viewer@test.com
2. **Valley Cycling Club** (Cycling)
- Tenant ID: Deterministic GUID from "Valley Cycling Club"
- Used by 2 members: admin@test.com, member1@test.com
### Members (7 total records, 5 unique users)
- **admin@test.com**: Admin in Tennis Club, Member in Cycling Club
- **manager@test.com**: Manager in Tennis Club
- **member1@test.com**: Member in Tennis Club, Member in Cycling Club
- **member2@test.com**: Member in Tennis Club
- **viewer@test.com**: Viewer in Tennis Club
### Work Items (8 total)
**Tennis Club (5 items)**
- Court renovation (Open, unassigned)
- Equipment order (Assigned, to manager)
- Tournament planning (InProgress, to member1)
- Member handbook review (Review, to member2)
- Website update (Done, to manager)
**Cycling Club (3 items)**
- Route mapping (Open, unassigned)
- Safety training (Assigned, to member1)
- Group ride coordination (InProgress, to admin)
### Shifts (5 total)
**Tennis Club (3 shifts)**
- Court Maintenance - Yesterday (past, capacity 2)
- Court Maintenance - Today (today, capacity 3)
- Tournament Setup - Next Week (future, capacity 5)
**Cycling Club (2 shifts)**
- Group Ride - Today (today, capacity 10)
- Maintenance Workshop - Next Week (future, capacity 4)
### Shift Signups (3-4 total)
- Tennis Court Maintenance (Yesterday): 2 signups
- Cycling Group Ride (Today): 1 signup
## Idempotency Implementation
Each entity type is seeded with idempotent checks:
```csharp
if (!context.Clubs.Any())
{
context.Clubs.AddRange(...);
await context.SaveChangesAsync();
}
```
This ensures:
- First run: All data inserted
- Subsequent runs: No duplicates (check passes on subsequent runs)
- Safe for multiple restarts during development
## Development-Only Guard
Seed execution is protected:
```csharp
if (app.Environment.IsDevelopment())
{
// Seed only runs in Development environment
}
```
This ensures:
- Production environment: No seed execution
- Staging/Testing: Controlled separately via environment variables
## Deterministic GUID Generation
Used MD5 hash to create consistent tenant IDs:
```csharp
private static string GenerateDeterministicGuid(string input)
{
var hash = MD5.HashData(Encoding.UTF8.GetBytes(input));
return new Guid(hash.Take(16).ToArray()).ToString();
}
```
Benefits:
- Same GUID generated for same club name (consistency across restarts)
- Predictable: Matches expected UUIDs in test users
- No external dependencies needed
## Usage During Development
1. Backend starts in Development environment
2. Program.cs development middleware runs
3. SeedDataService is resolved from DI container
4. SeedAsync() is called asynchronously
5. First run: All seed data inserted
6. Subsequent runs: Checks pass, no duplicates
## Notes
- Seed runs synchronously in middleware (blocking startup until complete)
- SeedDataService uses IServiceScopeFactory to create fresh DbContext
- All entities have CreatedAt/UpdatedAt timestamps set to UTC now
- ExternalUserId values are placeholder user IDs (can be updated when connected to Keycloak)
- Shift times use DateTimeOffset to handle timezone properly