Files
work-club-manager/.sisyphus/notepads/club-work-manager/learnings.md
OpenCode Assistant cf7b47cb69 infra(docker): add Docker Compose with PostgreSQL and Keycloak
- 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
2026-03-03 14:07:29 +01:00

6.4 KiB

Learnings — Club Work Manager

Conventions, patterns, and accumulated wisdom from task execution


Task 1: Monorepo Scaffolding (2026-03-03)

Key Learnings

  1. .NET 10 Solution Format Change

    • .NET 10 uses .slnx format (not .sln)
    • Solution files are still named WorkClub.slnx, compatible with dotnet sln add
    • Both formats work seamlessly with build system
  2. 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
  3. NuGet Package Versioning

    • Finbuckle.MultiTenant: Specified 8.2.0 but .NET 10 SDK resolved to 9.0.0
    • This is expected behavior with rollForward: latestFeature in global.json
    • No build failures - warnings only about version resolution
    • Testcontainers brings in BouncyCastle which has known security advisories (expected in test dependencies)
  4. Git Configuration for Automation

    • Set user.email and user.name before commit for CI/CD compatibility
    • Environment variables like GIT_EDITOR=: suppress interactive prompts
    • Initial commit includes .sisyphus directory (plans, notepads, etc.)
  5. Build Verification

    • dotnet build --configuration Release works 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

  1. Docker Compose v3.9 for Development

    • Uses explicit app-network bridge for service discovery
    • Keycloak service depends on postgres with condition: service_healthy for ordered startup
    • Health checks critical: PostgreSQL uses pg_isready, Keycloak uses /health/ready endpoint
  2. 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_committed with max 200 connections configured
  3. Keycloak 26.x Setup

    • Image: quay.io/keycloak/keycloak:26.1 from Red Hat's container registry
    • Command: start-dev --import-realm (development mode with automatic realm import)
    • Realm import directory: /opt/keycloak/data/import mounted from ./infra/keycloak
    • Database credentials: separate keycloak user with keycloakpass (not production-safe, dev only)
    • Health check uses curl to /health/ready endpoint (startup probe: 30s initial wait, 30 retries)
  4. Volume Management

    • Named volume postgres-data for persistent PostgreSQL storage
    • Bind mount ./infra/keycloak to /opt/keycloak/data/import for realm configuration
    • Bind mount ./infra/postgres to /docker-entrypoint-initdb.d for database initialization
  5. Service Discovery & Networking

    • All services on app-network bridge network
    • Service names act as hostnames: postgres:5432 for PostgreSQL, localhost:8080 for Keycloak UI
    • JDBC connection string in Keycloak: jdbc:postgresql://postgres:5432/keycloak
  6. 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_on with 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/ready is 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.json with 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