Local Development & Demo Strategy
Purpose
This document defines the local development experience and "demo in a box" strategy for the Farmer1st platform, enabling developers and sales teams to run the complete stack locally.
Current State
Goals
| Audience | Goal |
|---|---|
| Developers | Full stack running locally with hot reload for rapid iteration |
| AI assistants | Complete context to make changes across apps, services, and infra |
| Sales team | One command to spin up a demo with realistic data |
| Product team | Prototype new features without deploying to cloud |
One-Command Experience
# Developer starting work
git clone git@github.com:farmer1st/farmer1st.git
cd farmer1st
make dev
# Sales preparing for demo
make demo scenario=cocoa-cooperative
# Both result in:
# - Full stack running in Docker
# - Hot reload for code changes
# - Seeded with appropriate data
Architecture: Local Stack
┌─────────────────────────────────────────────────────────────────────────────┐
│ LOCAL DEVELOPMENT STACK │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ BROWSER │
│ ┌─────────────────────────────┐ ┌─────────────────────────────┐ │
│ │ http://localhost:3000 │ │ http://localhost:3001 │ │
│ │ Farmer PWA │ │ Stakeholder Portal │ │
│ └─────────────────────────────┘ └─────────────────────────────┘ │
│ │
│ DOCKER COMPOSE │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ APPS (Hot Reload) │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ farmer-pwa │ │ stakeholder- │ │ │
│ │ │ :3000 │ │ portal :3001 │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ BFFs (Hot Reload) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────────┐│ │
│ │ │ user-bff │ │relationship- │ │ surveys-bff │ │payments-bff││ │
│ │ │ :4001 │ │ bff :4002 │ │ :4003 │ │ :4004 ││ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ └────────────┘│ │
│ │ │ │
│ │ SERVICES (Hot Reload) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ auth-service │ │profile-svc │ │ invite-svc │ ... │ │
│ │ │ :5001 │ │ :5002 │ │ :5003 │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ │ PLATFORM SERVICES │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌────────────┐│ │
│ │ │ SuperTokens │ │ OpenFGA │ │ Temporal │ │ Unleash ││ │
│ │ │ :3567 │ │ :8080 │ │ :7233 │ │ :4242 ││ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ └────────────┘│ │
│ │ │ │
│ │ DATA STORES │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ PostgreSQL │ │ Redis │ │ Kafka │ │ │
│ │ │ :5432 │ │ :6379 │ │ :9092 │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Docker Compose Configuration
Main docker-compose.yml
# docker-compose.yml
version: '3.8'
services:
# ============================================
# FRONTEND APPS
# ============================================
farmer-pwa:
build:
context: ./apps/farmer-pwa
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- ./apps/farmer-pwa/src:/app/src
- ./packages:/packages
environment:
- VITE_API_URL=http://localhost:4001/graphql
- VITE_AUTH_URL=http://localhost:3567
depends_on:
- user-bff
stakeholder-portal:
build:
context: ./apps/stakeholder-portal
dockerfile: Dockerfile.dev
ports:
- "3001:3001"
volumes:
- ./apps/stakeholder-portal/src:/app/src
- ./packages:/packages
environment:
- VITE_API_URL=http://localhost:4001/graphql
- VITE_AUTH_URL=http://localhost:3567
depends_on:
- user-bff
# ============================================
# BFFs (GraphQL)
# ============================================
user-bff:
build:
context: ./services/user-management/bff
dockerfile: Dockerfile.dev
ports:
- "4001:4001"
volumes:
- ./services/user-management/bff/src:/app/src
- ./packages:/packages
environment:
- AUTH_SERVICE_URL=http://auth-service:5001
- PROFILE_SERVICE_URL=http://profile-service:5002
- SUPERTOKENS_URL=http://supertokens:3567
depends_on:
- auth-service
- profile-service
- supertokens
# ... (other BFFs follow same pattern)
# ============================================
# MICROSERVICES
# ============================================
auth-service:
build:
context: ./services/user-management/auth-service
dockerfile: Dockerfile.dev
ports:
- "5001:5001"
volumes:
- ./services/user-management/auth-service/src:/app/src
environment:
- DATABASE_URL=postgresql://farmer1st:farmer1st@postgres:5432/farmer1st
- SUPERTOKENS_URL=http://supertokens:3567
depends_on:
- postgres
- supertokens
profile-service:
build:
context: ./services/user-management/profile-service
dockerfile: Dockerfile.dev
ports:
- "5002:5002"
volumes:
- ./services/user-management/profile-service/src:/app/src
environment:
- DATABASE_URL=postgresql://farmer1st:farmer1st@postgres:5432/farmer1st
- REDIS_URL=redis://redis:6379
depends_on:
- postgres
- redis
# ... (other services follow same pattern)
# ============================================
# PLATFORM SERVICES
# ============================================
supertokens:
image: supertokens/supertokens-postgresql:latest
ports:
- "3567:3567"
environment:
- POSTGRESQL_CONNECTION_URI=postgresql://farmer1st:farmer1st@postgres:5432/supertokens
depends_on:
- postgres
openfga:
image: openfga/openfga:latest
ports:
- "8080:8080"
- "8081:8081" # gRPC
command: run
environment:
- OPENFGA_DATASTORE_ENGINE=postgres
- OPENFGA_DATASTORE_URI=postgresql://farmer1st:farmer1st@postgres:5432/openfga
depends_on:
- postgres
temporal:
image: temporalio/auto-setup:latest
ports:
- "7233:7233"
environment:
- DB=postgresql
- DB_PORT=5432
- POSTGRES_USER=farmer1st
- POSTGRES_PWD=farmer1st
- POSTGRES_SEEDS=postgres
depends_on:
- postgres
temporal-ui:
image: temporalio/ui:latest
ports:
- "8088:8080"
environment:
- TEMPORAL_ADDRESS=temporal:7233
unleash:
image: unleashorg/unleash-server:latest
ports:
- "4242:4242"
environment:
- DATABASE_URL=postgresql://farmer1st:farmer1st@postgres:5432/unleash
- DATABASE_SSL=false
depends_on:
- postgres
# ============================================
# DATA STORES
# ============================================
postgres:
image: postgres:15
ports:
- "5432:5432"
environment:
- POSTGRES_USER=farmer1st
- POSTGRES_PASSWORD=farmer1st
- POSTGRES_DB=farmer1st
volumes:
- postgres_data:/var/lib/postgresql/data
- ./infra/docker/postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
kafka:
image: bitnami/kafka:latest
ports:
- "9092:9092"
environment:
- KAFKA_CFG_NODE_ID=0
- KAFKA_CFG_PROCESS_ROLES=controller,broker
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka:9093
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
volumes:
- kafka_data:/bitnami/kafka
volumes:
postgres_data:
redis_data:
kafka_data:
Demo Profile
# docker-compose.demo.yml
# Extends docker-compose.yml with demo-specific settings
version: '3.8'
services:
farmer-pwa:
environment:
- VITE_DEMO_MODE=true
- VITE_AUTO_LOGIN=true
stakeholder-portal:
environment:
- VITE_DEMO_MODE=true
- VITE_AUTO_LOGIN=true
# Seed service runs once to populate demo data
seeder:
build:
context: ./tools/seed-data
environment:
- DATABASE_URL=postgresql://farmer1st:farmer1st@postgres:5432/farmer1st
- OPENFGA_URL=http://openfga:8080
- SCENARIO=${DEMO_SCENARIO:-cocoa-cooperative}
depends_on:
- postgres
- openfga
- supertokens
Makefile Commands
# Makefile
.PHONY: dev demo test clean help
# Development with hot reload
dev:
docker compose up --build
# Development (detached)
dev-d:
docker compose up --build -d
# Stop all services
stop:
docker compose down
# Demo mode with seeded data
demo:
DEMO_SCENARIO=$(scenario) docker compose -f docker-compose.yml -f docker-compose.demo.yml up --build
# Available scenarios: cocoa-cooperative, coffee-brand, small-farm
demo-cocoa:
$(MAKE) demo scenario=cocoa-cooperative
demo-coffee:
$(MAKE) demo scenario=coffee-brand
demo-small:
$(MAKE) demo scenario=small-farm
# Reset all data
reset:
docker compose down -v
docker compose up --build
# Run tests
test:
docker compose run --rm test
# Run specific service tests
test-service:
docker compose run --rm $(service) pytest
# Generate API clients from OpenAPI specs
generate-clients:
./tools/scripts/generate-clients.sh
# Seed data (without full demo mode)
seed:
docker compose run --rm seeder python seed.py --scenario $(scenario)
# View logs
logs:
docker compose logs -f $(service)
# Shell into a service
shell:
docker compose exec $(service) /bin/sh
# Clean everything
clean:
docker compose down -v --rmi local
docker system prune -f
# Help
help:
@echo "Available commands:"
@echo " make dev - Start development environment"
@echo " make demo - Start demo with default scenario"
@echo " make demo-cocoa - Demo: West Africa cocoa cooperative"
@echo " make demo-coffee - Demo: Coffee brand (Nestlé scenario)"
@echo " make demo-small - Demo: Single small farm"
@echo " make stop - Stop all services"
@echo " make reset - Reset all data and restart"
@echo " make test - Run all tests"
@echo " make logs service=x - View logs for service x"
@echo " make shell service=x - Shell into service x"
@echo " make clean - Remove all containers and images"
Demo Scenarios
Scenario: Cocoa Cooperative
tools/seed-data/scenarios/cocoa-cooperative/
├── manifest.json # Scenario metadata
├── users.json # 50 farmers, 5 coop admins, 2 agents
├── farms.json # Farms with cocoa plots in Ghana/Côte d'Ivoire
├── cooperatives.json # "West Africa Cocoa Collective"
├── brands.json # Mars, Nestlé as buyers
├── relationships.json # OpenFGA tuples
├── surveys.json # Completed sustainability surveys
└── demo-credentials.json # Pre-configured login credentials
Demo credentials (cocoa-cooperative):
| Role | Email | Password | What they see |
|------|-------|----------|---------------|
| Farmer | demo-farmer@farmer1st.local | demo123 | Their farm, surveys, points |
| Coop Admin | demo-coop@farmer1st.local | demo123 | All coop farmers, aggregated data |
| Brand Employee | demo-brand@farmer1st.local | demo123 | Supply chain, sourcing data |
Scenario: Coffee Brand
Similar structure, but focused on coffee supply chain with different geography (Colombia, Ethiopia).
Scenario: Small Farm
Minimal setup for quick demos - single farmer, single farm, basic surveys.
Resource Requirements
Minimum (Lite Mode - TBD)
| Resource | Requirement |
|---|---|
| RAM | 8GB |
| CPU | 4 cores |
| Disk | 10GB |
Lite mode would mock heavy services (Kafka, Temporal) for resource-constrained laptops.
Recommended (Full Stack)
| Resource | Requirement |
|---|---|
| RAM | 16GB |
| CPU | 8 cores |
| Disk | 20GB |
What Each Service Uses
| Service | RAM (approx) | Notes |
|---|---|---|
| PostgreSQL | 512MB | Shared by all |
| Redis | 128MB | |
| Kafka | 1GB | Can be mocked for lite mode |
| Temporal | 512MB | Can be mocked for lite mode |
| SuperTokens | 256MB | |
| OpenFGA | 256MB | |
| Unleash | 256MB | |
| Each Python service | 128-256MB | ~10 services |
| Each React app | 256MB | 2 apps (dev server) |
Total: ~6-8GB for full stack
Hot Reload Setup
Frontend (Vite)
React apps use Vite with hot module replacement:
# apps/farmer-pwa/Dockerfile.dev
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
Backend (Python)
Python services use uvicorn with reload:
# services/user-management/auth-service/Dockerfile.dev
FROM python:3.12-slim
WORKDIR /app
COPY pyproject.toml ./
RUN pip install -e ".[dev]"
COPY . .
EXPOSE 5001
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "5001", "--reload"]
Troubleshooting
Common Issues
| Issue | Solution |
|---|---|
| Port already in use | make stop then make dev |
| Database connection refused | Wait for postgres to be ready, or make reset |
| Out of disk space | make clean to remove unused images |
| Service won't start | Check logs: make logs service=<name> |
| Stale data | make reset to clear volumes |
Health Checks
# Check all services are healthy
docker compose ps
# Check specific service logs
make logs service=user-bff
# Check database connectivity
docker compose exec postgres psql -U farmer1st -c "SELECT 1"
# Check OpenFGA
curl http://localhost:8080/healthz
Open Questions
- Lite mode implementation (mock Kafka/Temporal)?
- Pre-built demo images for faster startup?
- Demo data refresh strategy?
- Offline demo capability (airplane mode)?
- Windows/WSL2 compatibility testing?
Dependencies
- Docker & Docker Compose
- Make (or equivalent task runner)
- ~16GB RAM for full stack
- Git for cloning mono-repo
Last Updated: 2025-12-30