Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 23: Development Environment Setup

Overview

This chapter provides complete, step-by-step instructions for setting up your development environment for the POS platform. By the end, you will have a fully functional local development stack.


Prerequisites

Required Software

SoftwareVersionPurpose
.NET SDK8.0+Backend development
PostgreSQL16+Primary database
Docker24.0+Containerization
Docker Compose2.20+Multi-container orchestration
Node.js20 LTSFrontend tooling
Git2.40+Version control

Hardware Requirements

ComponentMinimumRecommended
RAM8 GB16 GB
Storage20 GB free50 GB SSD
CPU4 cores8 cores

Project Structure

/volume1/docker/pos-platform/
├── CLAUDE.md                          # AI assistant guidance
├── README.md                          # Quick start guide
├── .gitignore                         # Git ignore patterns
├── .env.example                       # Environment template
├── pos-platform.sln                   # .NET solution file
│
├── docker/
│   ├── docker-compose.yml             # Development stack
│   ├── docker-compose.prod.yml        # Production overrides
│   ├── Dockerfile                     # API container build
│   ├── Dockerfile.web                 # Web container build
│   └── .env                           # Docker environment (gitignored)
│
├── src/
│   ├── PosPlatform.Core/              # Domain layer
│   │   ├── Entities/                  # Domain entities
│   │   ├── ValueObjects/              # Immutable value objects
│   │   ├── Events/                    # Domain events
│   │   ├── Exceptions/                # Domain exceptions
│   │   ├── Interfaces/                # Repository interfaces
│   │   └── Services/                  # Domain services
│   │
│   ├── PosPlatform.Infrastructure/    # Infrastructure layer
│   │   ├── Data/                      # EF Core contexts
│   │   ├── Repositories/              # Repository implementations
│   │   ├── Services/                  # External service integrations
│   │   ├── Messaging/                 # Event bus, queues
│   │   └── MultiTenant/               # Tenant resolution
│   │
│   ├── PosPlatform.Api/               # API layer
│   │   ├── Controllers/               # REST endpoints
│   │   ├── Middleware/                # Request pipeline
│   │   ├── Filters/                   # Action filters
│   │   ├── DTOs/                      # Data transfer objects
│   │   └── Program.cs                 # Application entry
│   │
│   └── PosPlatform.Web/               # Blazor frontend
│       ├── Components/                # Blazor components
│       ├── Pages/                     # Routable pages
│       ├── Services/                  # Frontend services
│       └── wwwroot/                   # Static assets
│
├── tests/
│   ├── PosPlatform.Core.Tests/        # Unit tests
│   ├── PosPlatform.Api.Tests/         # API integration tests
│   └── PosPlatform.E2E.Tests/         # End-to-end tests
│
└── database/
    ├── migrations/                    # EF Core migrations
    ├── seed/                          # Seed data scripts
    └── init.sql                       # Database initialization

Step 1: Install Prerequisites

Linux (Ubuntu/Debian)

# Update package manager
sudo apt update && sudo apt upgrade -y

# Install .NET 8 SDK
wget https://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt update
sudo apt install -y dotnet-sdk-8.0

# Verify .NET installation
dotnet --version

# Install Docker
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Log out and back in for group changes

# Verify Docker
docker --version
docker compose version

# Install Node.js 20 LTS
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

# Verify Node.js
node --version
npm --version

# Install Git
sudo apt install -y git
git --version

macOS

# Install Homebrew (if not installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Install .NET 8 SDK
brew install dotnet-sdk

# Install Docker Desktop
brew install --cask docker

# Install Node.js
brew install node@20

# Install Git
brew install git

Windows

# Install with winget (Windows Package Manager)
winget install Microsoft.DotNet.SDK.8
winget install Docker.DockerDesktop
winget install OpenJS.NodeJS.LTS
winget install Git.Git

# Alternatively, download installers from:
# - https://dotnet.microsoft.com/download
# - https://docker.com/products/docker-desktop
# - https://nodejs.org/
# - https://git-scm.com/

Step 2: Create Project Structure

Initialize Repository

# Create project directory
mkdir -p /volume1/docker/pos-platform
cd /volume1/docker/pos-platform

# Initialize Git repository
git init
git branch -M main

# Create initial structure
mkdir -p docker src tests database/migrations database/seed

Create .gitignore

cat > .gitignore << 'EOF'
# Build outputs
bin/
obj/
publish/

# IDE
.vs/
.vscode/
.idea/
*.user
*.suo

# Environment
.env
*.env.local
appsettings.*.json
!appsettings.json
!appsettings.Development.json

# Logs
logs/
*.log

# Docker
docker/.env

# Node
node_modules/
dist/

# Database
*.db
*.sqlite

# OS
.DS_Store
Thumbs.db

# Secrets
*.pem
*.key
secrets/
EOF

Create Solution File

# Create .NET solution
dotnet new sln -n pos-platform

# Create projects
dotnet new classlib -n PosPlatform.Core -o src/PosPlatform.Core
dotnet new classlib -n PosPlatform.Infrastructure -o src/PosPlatform.Infrastructure
dotnet new webapi -n PosPlatform.Api -o src/PosPlatform.Api
dotnet new blazorserver -n PosPlatform.Web -o src/PosPlatform.Web

# Create test projects
dotnet new xunit -n PosPlatform.Core.Tests -o tests/PosPlatform.Core.Tests
dotnet new xunit -n PosPlatform.Api.Tests -o tests/PosPlatform.Api.Tests

# Add projects to solution
dotnet sln add src/PosPlatform.Core/PosPlatform.Core.csproj
dotnet sln add src/PosPlatform.Infrastructure/PosPlatform.Infrastructure.csproj
dotnet sln add src/PosPlatform.Api/PosPlatform.Api.csproj
dotnet sln add src/PosPlatform.Web/PosPlatform.Web.csproj
dotnet sln add tests/PosPlatform.Core.Tests/PosPlatform.Core.Tests.csproj
dotnet sln add tests/PosPlatform.Api.Tests/PosPlatform.Api.Tests.csproj

# Add project references
dotnet add src/PosPlatform.Infrastructure/PosPlatform.Infrastructure.csproj reference src/PosPlatform.Core/PosPlatform.Core.csproj
dotnet add src/PosPlatform.Api/PosPlatform.Api.csproj reference src/PosPlatform.Infrastructure/PosPlatform.Infrastructure.csproj
dotnet add src/PosPlatform.Api/PosPlatform.Api.csproj reference src/PosPlatform.Core/PosPlatform.Core.csproj
dotnet add src/PosPlatform.Web/PosPlatform.Web.csproj reference src/PosPlatform.Core/PosPlatform.Core.csproj
dotnet add tests/PosPlatform.Core.Tests/PosPlatform.Core.Tests.csproj reference src/PosPlatform.Core/PosPlatform.Core.csproj
dotnet add tests/PosPlatform.Api.Tests/PosPlatform.Api.Tests.csproj reference src/PosPlatform.Api/PosPlatform.Api.csproj

Step 3: Docker Configuration

docker-compose.yml

# /volume1/docker/pos-platform/docker/docker-compose.yml
version: '3.8'

services:
  # PostgreSQL Database
  postgres:
    image: postgres:16-alpine
    container_name: pos-postgres
    environment:
      POSTGRES_USER: ${DB_USER:-pos_admin}
      POSTGRES_PASSWORD: ${DB_PASSWORD:-PosDevPass2025!}
      POSTGRES_DB: ${DB_NAME:-pos_platform}
    ports:
      - "5434:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ../database/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-pos_admin} -d ${DB_NAME:-pos_platform}"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - pos-network

  # Redis for Caching and Sessions
  redis:
    image: redis:7-alpine
    container_name: pos-redis
    ports:
      - "6380:6379"
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - pos-network

  # RabbitMQ for Event Bus
  rabbitmq:
    image: rabbitmq:3-management-alpine
    container_name: pos-rabbitmq
    environment:
      RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-pos_user}
      RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASS:-PosRabbit2025!}
    ports:
      - "5673:5672"   # AMQP
      - "15673:15672" # Management UI
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq
    healthcheck:
      test: ["CMD", "rabbitmq-diagnostics", "check_running"]
      interval: 30s
      timeout: 10s
      retries: 5
    networks:
      - pos-network

  # POS API (Development)
  api:
    build:
      context: ..
      dockerfile: docker/Dockerfile
    container_name: pos-api
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://+:8080
      - ConnectionStrings__DefaultConnection=Host=postgres;Port=5432;Database=${DB_NAME:-pos_platform};Username=${DB_USER:-pos_admin};Password=${DB_PASSWORD:-PosDevPass2025!}
      - Redis__ConnectionString=redis:6379
      - RabbitMQ__Host=rabbitmq
      - RabbitMQ__Username=${RABBITMQ_USER:-pos_user}
      - RabbitMQ__Password=${RABBITMQ_PASS:-PosRabbit2025!}
    ports:
      - "5100:8080"
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      rabbitmq:
        condition: service_healthy
    volumes:
      - ../src:/app/src:ro
      - api_logs:/app/logs
    networks:
      - pos-network

  # POS Web (Development)
  web:
    build:
      context: ..
      dockerfile: docker/Dockerfile.web
    container_name: pos-web
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://+:8080
      - ApiBaseUrl=http://api:8080
    ports:
      - "5101:8080"
    depends_on:
      - api
    networks:
      - pos-network

volumes:
  postgres_data:
  redis_data:
  rabbitmq_data:
  api_logs:

networks:
  pos-network:
    driver: bridge

Dockerfile for API

# /volume1/docker/pos-platform/docker/Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src

# Copy solution and project files
COPY *.sln ./
COPY src/PosPlatform.Core/*.csproj ./src/PosPlatform.Core/
COPY src/PosPlatform.Infrastructure/*.csproj ./src/PosPlatform.Infrastructure/
COPY src/PosPlatform.Api/*.csproj ./src/PosPlatform.Api/

# Restore dependencies
RUN dotnet restore src/PosPlatform.Api/PosPlatform.Api.csproj

# Copy source code
COPY src/ ./src/

# Build and publish
WORKDIR /src/src/PosPlatform.Api
RUN dotnet publish -c Release -o /app/publish --no-restore

# Runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS runtime
WORKDIR /app

# Install culture support
RUN apk add --no-cache icu-libs
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false

# Copy published app
COPY --from=build /app/publish .

# Create non-root user
RUN adduser -D -u 1000 appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 8080
ENTRYPOINT ["dotnet", "PosPlatform.Api.dll"]

Dockerfile for Web

# /volume1/docker/pos-platform/docker/Dockerfile.web
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src

# Copy solution and project files
COPY *.sln ./
COPY src/PosPlatform.Core/*.csproj ./src/PosPlatform.Core/
COPY src/PosPlatform.Web/*.csproj ./src/PosPlatform.Web/

# Restore dependencies
RUN dotnet restore src/PosPlatform.Web/PosPlatform.Web.csproj

# Copy source code
COPY src/ ./src/

# Build and publish
WORKDIR /src/src/PosPlatform.Web
RUN dotnet publish -c Release -o /app/publish --no-restore

# Runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS runtime
WORKDIR /app

RUN apk add --no-cache icu-libs
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false

COPY --from=build /app/publish .

RUN adduser -D -u 1000 appuser && chown -R appuser:appuser /app
USER appuser

EXPOSE 8080
ENTRYPOINT ["dotnet", "PosPlatform.Web.dll"]

Environment Template

# /volume1/docker/pos-platform/docker/.env.example
# Database
DB_USER=pos_admin
DB_PASSWORD=PosDevPass2025!
DB_NAME=pos_platform

# RabbitMQ
RABBITMQ_USER=pos_user
RABBITMQ_PASS=PosRabbit2025!

# API Keys (development)
JWT_SECRET=dev-jwt-secret-key-min-32-characters-long
ENCRYPTION_KEY=dev-encryption-key-32-chars-long

Step 4: Database Initialization

init.sql

-- /volume1/docker/pos-platform/database/init.sql

-- Create extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_trgm";

-- Create shared schema for platform-wide data
CREATE SCHEMA IF NOT EXISTS shared;

-- Tenants table (platform-wide)
CREATE TABLE shared.tenants (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    code VARCHAR(10) NOT NULL UNIQUE,
    name VARCHAR(100) NOT NULL,
    domain VARCHAR(255),
    status VARCHAR(20) NOT NULL DEFAULT 'active',
    settings JSONB NOT NULL DEFAULT '{}',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ
);

-- Platform users (super admins)
CREATE TABLE shared.platform_users (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    email VARCHAR(255) NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    full_name VARCHAR(100) NOT NULL,
    role VARCHAR(50) NOT NULL DEFAULT 'admin',
    is_active BOOLEAN NOT NULL DEFAULT true,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Function to create tenant schema
CREATE OR REPLACE FUNCTION shared.create_tenant_schema(tenant_code VARCHAR)
RETURNS VOID AS $$
BEGIN
    EXECUTE format('CREATE SCHEMA IF NOT EXISTS tenant_%s', tenant_code);

    -- Create tenant-specific tables
    EXECUTE format('
        CREATE TABLE tenant_%s.locations (
            id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
            code VARCHAR(10) NOT NULL UNIQUE,
            name VARCHAR(100) NOT NULL,
            address JSONB,
            is_active BOOLEAN DEFAULT true,
            created_at TIMESTAMPTZ DEFAULT NOW()
        )', tenant_code);

    EXECUTE format('
        CREATE TABLE tenant_%s.users (
            id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
            employee_id VARCHAR(20) UNIQUE,
            full_name VARCHAR(100) NOT NULL,
            email VARCHAR(255),
            pin_hash VARCHAR(255),
            role VARCHAR(50) NOT NULL,
            location_id UUID REFERENCES tenant_%s.locations(id),
            is_active BOOLEAN DEFAULT true,
            created_at TIMESTAMPTZ DEFAULT NOW()
        )', tenant_code, tenant_code);

    EXECUTE format('
        CREATE TABLE tenant_%s.products (
            id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
            sku VARCHAR(50) NOT NULL UNIQUE,
            name VARCHAR(255) NOT NULL,
            description TEXT,
            category_id UUID,
            base_price DECIMAL(10,2) NOT NULL,
            cost DECIMAL(10,2),
            is_active BOOLEAN DEFAULT true,
            created_at TIMESTAMPTZ DEFAULT NOW(),
            updated_at TIMESTAMPTZ
        )', tenant_code);
END;
$$ LANGUAGE plpgsql;

-- Insert default platform admin
INSERT INTO shared.platform_users (email, password_hash, full_name, role)
VALUES (
    'admin@posplatform.local',
    '$2a$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/X4.vttYqBZq.kxVQ6', -- "admin123"
    'Platform Administrator',
    'super_admin'
);

-- Insert demo tenant
INSERT INTO shared.tenants (code, name, domain, status, settings)
VALUES (
    'DEMO',
    'Demo Retail Store',
    'demo.posplatform.local',
    'active',
    '{"timezone": "America/New_York", "currency": "USD", "taxRate": 0.07}'
);

-- Create demo tenant schema
SELECT shared.create_tenant_schema('demo');

COMMENT ON SCHEMA shared IS 'Platform-wide shared data';

Step 5: IDE Setup

VS Code Configuration

// /volume1/docker/pos-platform/.vscode/settings.json
{
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "ms-dotnettools.csharp",
    "omnisharp.enableRoslynAnalyzers": true,
    "omnisharp.enableEditorConfigSupport": true,
    "dotnet.defaultSolution": "pos-platform.sln",
    "files.exclude": {
        "**/bin": true,
        "**/obj": true,
        "**/node_modules": true
    },
    "[csharp]": {
        "editor.defaultFormatter": "ms-dotnettools.csharp"
    }
}
// /volume1/docker/pos-platform/.vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Launch API",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "build-api",
            "program": "${workspaceFolder}/src/PosPlatform.Api/bin/Debug/net8.0/PosPlatform.Api.dll",
            "args": [],
            "cwd": "${workspaceFolder}/src/PosPlatform.Api",
            "console": "internalConsole",
            "stopAtEntry": false,
            "env": {
                "ASPNETCORE_ENVIRONMENT": "Development"
            }
        },
        {
            "name": "Launch Web",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "build-web",
            "program": "${workspaceFolder}/src/PosPlatform.Web/bin/Debug/net8.0/PosPlatform.Web.dll",
            "args": [],
            "cwd": "${workspaceFolder}/src/PosPlatform.Web",
            "console": "internalConsole",
            "stopAtEntry": false
        }
    ]
}
// /volume1/docker/pos-platform/.vscode/extensions.json
{
    "recommendations": [
        "ms-dotnettools.csharp",
        "ms-dotnettools.csdevkit",
        "ms-azuretools.vscode-docker",
        "eamodio.gitlens",
        "streetsidesoftware.code-spell-checker",
        "editorconfig.editorconfig",
        "humao.rest-client",
        "mtxr.sqltools",
        "mtxr.sqltools-driver-pg"
    ]
}

Step 6: Git Workflow

Branch Strategy

main                    # Production-ready code
  |
  +-- develop           # Integration branch
       |
       +-- feature/*    # New features
       +-- bugfix/*     # Bug fixes
       +-- hotfix/*     # Urgent production fixes

Initial Commit

cd /volume1/docker/pos-platform

# Stage all files
git add .

# Initial commit
git commit -m "Initial project structure with Docker development stack

- Created .NET 8 solution with 4 projects (Core, Infrastructure, Api, Web)
- Added docker-compose with PostgreSQL 16, Redis, RabbitMQ
- Configured multi-tenant database initialization
- Set up VS Code development environment

Generated with Claude Code"

# Create develop branch
git checkout -b develop

Quick Reference Commands

Start Development Stack

cd /volume1/docker/pos-platform/docker

# Copy environment file
cp .env.example .env

# Start all services
docker compose up -d

# View logs
docker compose logs -f

# Check status
docker compose ps

Database Access

# Connect to PostgreSQL
docker exec -it pos-postgres psql -U pos_admin -d pos_platform

# List schemas
\dn

# List tables in shared schema
\dt shared.*

# List tables in tenant schema
\dt tenant_demo.*

Build and Run Locally

cd /volume1/docker/pos-platform

# Restore dependencies
dotnet restore

# Build solution
dotnet build

# Run API (from project directory)
cd src/PosPlatform.Api
dotnet run

# Run tests
cd /volume1/docker/pos-platform
dotnet test

Stop and Clean

cd /volume1/docker/pos-platform/docker

# Stop services
docker compose down

# Stop and remove volumes (WARNING: deletes data)
docker compose down -v

# Remove unused images
docker image prune -f

Verification Checklist

After completing setup, verify each component:

  • dotnet --version shows 8.0.x
  • docker compose ps shows all containers healthy
  • PostgreSQL accepts connections on port 5434
  • Redis responds to ping on port 6380
  • RabbitMQ management UI accessible at http://localhost:15673
  • Solution builds without errors: dotnet build
  • All tests pass: dotnet test

Next Steps

With your development environment ready:

  1. Proceed to Chapter 24: Implementation Roadmap for the full build plan
  2. Begin Phase 1: Foundation in Chapter 25
  3. Reference Chapter 23 when adding new developers to the project

Chapter 23 Complete - Development Environment Setup