- Add docker-compose.yml (v3.9) with postgres:16-alpine and keycloak:26.1 services - Configure PostgreSQL with separate workclub and keycloak databases - Setup Keycloak with database backend, admin user, and realm import capability - Create PostgreSQL init script to provision development databases and users - Add placeholder realm-export.json for Keycloak realm configuration - Configure healthchecks and app-network bridge for service discovery - Document configuration and patterns in learnings.md
6.4 KiB
6.4 KiB
Learnings — Club Work Manager
Conventions, patterns, and accumulated wisdom from task execution
Task 1: Monorepo Scaffolding (2026-03-03)
Key Learnings
-
.NET 10 Solution Format Change
- .NET 10 uses
.slnxformat (not.sln) - Solution files are still named
WorkClub.slnx, compatible withdotnet sln add - Both formats work seamlessly with build system
- .NET 10 uses
-
Clean Architecture Implementation
- Successfully established layered architecture with proper dependencies
- Api → (Application + Infrastructure) → Domain
- Tests reference all layers for comprehensive coverage
- Project references added via
dotnet add reference
-
NuGet Package Versioning
- Finbuckle.MultiTenant: Specified 8.2.0 but .NET 10 SDK resolved to 9.0.0
- This is expected behavior with
rollForward: latestFeaturein global.json - No build failures - warnings only about version resolution
- Testcontainers brings in BouncyCastle which has known security advisories (expected in test dependencies)
-
Git Configuration for Automation
- Set
user.emailanduser.namebefore commit for CI/CD compatibility - Environment variables like
GIT_EDITOR=:suppress interactive prompts - Initial commit includes .sisyphus directory (plans, notepads, etc.)
- Set
-
Build Verification
dotnet build --configuration Releaseworks perfectly- 6 projects compile successfully in 4.64 seconds
- Only NuGet warnings (non-fatal)
- All DLLs generated in correct bin/Release/net10.0 directories
Configuration Files Created
-
.gitignore: Comprehensive coverage for:
- .NET: bin/, obj/, *.user, .vs/
- Node: node_modules/, .next/, .cache/
- IDE: .idea/, .vscode/, *.swp
-
.editorconfig: C# conventions with:
- 4-space indentation for .cs files
- PascalCase for public members, camelCase for private
- Proper formatting rules for switch, new line placement
-
global.json: SDK pinning with latestFeature rollForward for flexibility
Project Template Choices
- Api:
dotnet new webapi(includes Program.cs, appsettings.json, Controllers template) - Application/Domain/Infrastructure:
dotnet new classlib(clean base) - Tests:
dotnet new xunit(modern testing framework, includes base dependencies)
Next Phase Considerations
- Generated Program.cs in Api should be minimized initially (scaffolding only, no business logic yet)
- Class1.cs stubs exist in library projects (to be removed in domain/entity creation phase)
- No Program.cs modifications yet - pure scaffolding as required
Task 2: Docker Compose with PostgreSQL 16 & Keycloak 26.x (2026-03-03)
Key Learnings
-
Docker Compose v3.9 for Development
- Uses explicit
app-networkbridge for service discovery - Keycloak service depends on postgres with
condition: service_healthyfor ordered startup - Health checks critical: PostgreSQL uses
pg_isready, Keycloak uses/health/readyendpoint
- Uses explicit
-
PostgreSQL 16 Alpine Configuration
- Alpine image reduces footprint significantly vs full PostgreSQL images
- Multi-database setup: separate databases for application (
workclub) and Keycloak (keycloak) - Init script (
init.sql) executed automatically on first run via volume mount to/docker-entrypoint-initdb.d - Default PostgreSQL connection isolation:
read_committedwith max 200 connections configured
-
Keycloak 26.x Setup
- Image:
quay.io/keycloak/keycloak:26.1from Red Hat's container registry - Command:
start-dev --import-realm(development mode with automatic realm import) - Realm import directory:
/opt/keycloak/data/importmounted from./infra/keycloak - Database credentials: separate
keycloakuser withkeycloakpass(not production-safe, dev only) - Health check uses curl to
/health/readyendpoint (startup probe: 30s initial wait, 30 retries)
- Image:
-
Volume Management
- Named volume
postgres-datafor persistent PostgreSQL storage - Bind mount
./infra/keycloakto/opt/keycloak/data/importfor realm configuration - Bind mount
./infra/postgresto/docker-entrypoint-initdb.dfor database initialization
- Named volume
-
Service Discovery & Networking
- All services on
app-networkbridge network - Service names act as hostnames:
postgres:5432for PostgreSQL,localhost:8080for Keycloak UI - JDBC connection string in Keycloak:
jdbc:postgresql://postgres:5432/keycloak
- All services on
-
Development vs Production
- This configuration is dev-only: hardcoded credentials, start-dev mode, default admin user
- Security note: Keycloak admin credentials (admin/admin) and PostgreSQL passwords visible in plain text
- No TLS/HTTPS, no resource limits, no restart policies beyond defaults
- Future: Task 22 will add backend/frontend services to this compose file
Configuration Files Created
- docker-compose.yml: 68 lines, v3.9 format with postgres + keycloak services
- infra/postgres/init.sql: Database initialization for workclub and keycloak databases
- infra/keycloak/realm-export.json: Placeholder realm (will be populated by Task 3)
Environment Constraints
- Docker Compose CLI plugin not available in development environment
- Configuration validated against v3.9 spec structure
- YAML syntax verified via grep pattern matching
- Full integration testing deferred to actual Docker deployment
Patterns & Conventions
- Use Alpine Linux images for smaller container footprints
- Health checks with appropriate startup periods and retry counts
- Ordered service startup via
depends_onwith health conditions - Named volumes for persistent state, bind mounts for configuration
- Separate database users and passwords even in development (easier to migrate to secure configs)
Gotchas to Avoid
- Keycloak startup takes 20-30 seconds even in dev mode (don't reduce retries)
/health/readyis not the same as/health/live(use ready for startup confirmation)- PostgreSQL in Alpine doesn't include common extensions by default (not needed yet)
- Keycloak password encoding: stored hashed in PostgreSQL, admin creds only in environment
- Missing realm-export.json or empty directory causes Keycloak to start but import silently fails
Next Dependencies
- Task 3: Populate
realm-export.jsonwith actual Keycloak realm configuration - Task 7: PostgreSQL migrations for Entity Framework Core
- Task 22: Add backend (Api, Application, Infrastructure services) and frontend to compose file