Skip to content

ERP Unlocked Monorepo Guide

This repository has been refactored into a monorepo structure with clear separation of concerns:

/
├── apps/ # Applications
│ ├── webapp/ # Main Astro customer application (port 4321)
│ ├── marketing/ # Static marketing website (port 3001)
│ ├── internal/ # Internal admin portal (port 4322)
│ ├── crm/ # CRM Astro application
│ ├── pdf-api/ # FastAPI PDF processing service (port 8000)
│ └── pdf-worker/ # Celery worker service
├── packages/ # Shared packages
│ ├── ui/ # Shared UI components (Astro + React)
│ ├── db/ # Database schema & migrations (Drizzle ORM)
│ ├── auth/ # Authentication utilities (Clerk)
│ ├── utils/ # TypeScript utilities
│ ├── ai/ # AI/ML utilities
│ ├── crm/ # CRM-specific utilities
│ ├── trigger/ # Background job orchestration (Trigger.dev)
│ ├── observability/ # OpenTelemetry tracing, logging & metrics
│ ├── pdf-shared/ # Python shared library
│ └── mockoon/ # Mockoon TUI – run/export/import mock APIs
├── docs/ # Project documentation
├── debt/ # Technical debt planning
└── scripts/ # Project-level scripts

The monorepo uses Clerk for authentication and organization management:

  • Package: @repo/auth - Centralized authentication utilities
  • Features: User authentication, organization management, role-based access control
  • Organization Context: Multi-tenant support with organization isolation
  • Middleware: Automatic organization context resolution for protected routes

Organizations provide multi-tenant isolation across all applications:

  • Database Schema: @repo/db/schema/organizations.ts - Organization and member tables
  • API Routes: /api/organizations/* - Organization CRUD operations
  • Member Management: Role-based access control (admin, member)
  • Context Resolution: Automatic organization context in middleware

Billing functionality is integrated throughout the system:

  • Database Schema:
    • @repo/db/schema/billing.ts - Organization billing records
    • @repo/db/schema/subscriptions.ts - Subscription management
    • @repo/db/schema/usage-events.ts - Usage tracking for billing
    • @repo/db/schema/invoices.ts - Invoice records
  • API Routes:
    • /api/billing/* - Checkout, portal, and billing management
    • /api/subscriptions/* - Subscription status and management
    • /api/webhooks/stripe - Stripe webhook handling
  • Features: Usage-based billing, subscription management, payment processing
  • Organization Scoped: All billing is tied to specific organizations
  • Usage Tracking: Automatic usage tracking for order processing
  • Webhook Processing: Real-time Stripe event handling
  • Multi-tenant: Complete isolation between organizations

The monorepo implements a layered logging and observability system to balance functionality and bundle size:

  • Base Logger (@repo/utils/logger): Pure Pino logger with no OpenTelemetry dependencies. Use in shared packages and apps that don’t require distributed tracing.
  • Enhanced Logger (@repo/observability): OTEL-integrated logger with tracing, metrics, and instrumentation. Use in main applications for comprehensive observability.
  • webapp: Uses @repo/observability (full tracing, metrics, and context propagation)
  • crm: Uses @repo/utils/logger (base logger only - avoids OTEL bundling issues in Astro v5)
  • marketing: Uses @repo/utils/logger (base logger only - static site with minimal logging)
  • internal: Uses @repo/utils/logger (base logger only - internal administrative portal)
  • pdf-api: Uses @repo/utils/logger (base logger with Python-specific configuration)
  • pdf-worker: Uses @repo/utils/logger (base logger with Celery integration)

The OTEL NodeSDK is initialized in apps/webapp to avoid bundling issues in shared packages:

  • Initialization: apps/webapp/otel-init.cjs - Runs before app startup
  • Configuration: packages/observability/src/otel-config.ts - Configures SDK, exporters, and instrumentations
  • Environment Variables: See root .env configuration above (OTEL_SERVICE_NAME, OTEL_EXPORTER_OTLP_ENDPOINT, etc.)
// In webapp (with tracing)
import { logger } from '@repo/observability';
logger.info('Message with trace context', { userId: '123' });
// In shared packages or other apps (no OTEL)
import { logger } from '@repo/utils';
logger.info('Basic message', { context: 'value' });

For detailed setup and configuration, see docs/observability-architecture.md.

Terminal window
# Install all dependencies
pnpm install
# Build all applications
pnpm -r build
Terminal window
# Start webapp development server
pnpm --filter webapp dev
# Start internal admin development server
pnpm --filter internal dev
# Build specific app
pnpm --filter webapp build
# Run linting across all packages
pnpm -r lint

Each service uses a layered environment approach:

  1. Root .env - Shared infrastructure (database, Redis, observability)
  2. App-specific .env - Service-specific configuration
Terminal window
# Database
DATABASE_URL=postgresql://...
DB_POOL_MAX=20
# Redis
REDIS_URL=redis://localhost:6379/0
# Observability
TRIGGER_API_KEY=...
LOG_LEVEL=info
OTEL_SERVICE_NAME=erp-unlocked
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_HEADERS=...
# Storage
R2_ACCESS_KEY_ID=...
R2_SECRET_ACCESS_KEY=...
Terminal window
# Clerk Authentication
PUBLIC_CLERK_PUBLISHABLE_KEY=...
CLERK_SECRET_KEY=...
# Stripe Billing (Usage-Based)
STRIPE_SECRET_KEY=...
STRIPE_PUBLISHABLE_KEY=...
STRIPE_WEBHOOK_SECRET=...
STRIPE_BASE_PRICE_ID=... # Base subscription price
STRIPE_USAGE_PRICE_ID=... # Metered usage price
# Feature Flags (Flagsmith)
FLAGSMITH_ENVIRONMENT_KEY=...
FLAGSMITH_API_URL=...
# Application URLs
PUBLIC_WEBAPP_URL=http://localhost:4321
Terminal window
# PDF Processing
PDF_PROCESSOR_TOKEN=...
GEMINI_API_KEY=...
# Storage
R2_BUCKET_NAME=...
R2_ENDPOINT=...
Terminal window
# Copy example files
cp .env.example .env
cp apps/webapp/.env.example apps/webapp/.env
cp apps/pdf-api/.env.example apps/pdf-api/.env
cp apps/pdf-worker/.env.example apps/pdf-worker/.env
services:
web:
env_file:
- .env # Shared variables
- apps/webapp/.env # App-specific variables
Terminal window
# Install dependency in specific app
pnpm --filter webapp add react-query
# Install dev dependency in root
pnpm add -D -w prettier
# Run script in all packages
pnpm -r test
# Run script in specific package
pnpm --filter pdf-api start
Terminal window
# Add shared package to app
pnpm --filter webapp add @repo/ui@workspace:*
pnpm --filter webapp add @repo/db@workspace:*
pnpm --filter webapp add @repo/auth@workspace:*

The database is organized around organizations for multi-tenant support:

  • Core Tables: organizations, organization_members, users
  • Business Logic: orders, pdf_documents, erp_products, erp_customers
  • Billing: organization_billing, subscriptions, usage_events, invoices
  • Connections: erp_connections for ERP system integrations

All data is automatically scoped to organizations:

  • Middleware: Automatic organization context injection
  • Queries: All database queries include organization filtering
  • API Routes: Organization context validation on all protected endpoints

Background job processing using Trigger.dev:

  • Package: @repo/trigger - Job orchestration and scheduling
  • Features: Delayed jobs, recurring tasks, webhook processing
  • Use Cases: PDF processing, ERP synchronization, billing operations

Python-based background processing:

  • Service: apps/pdf-worker - PDF processing and AI extraction
  • Features: Async task processing, Redis-based queuing
  • Integration: Seamless integration with FastAPI services
  • Unit Tests: Individual package testing with Vitest (TS) and pytest (Python)
  • Integration Tests: End-to-end testing for critical workflows
  • Database Tests: Schema validation and migration testing
  • Linting: ESLint + Prettier for TypeScript/JavaScript
  • Python: Ruff for linting and formatting
  • Type Safety: Comprehensive TypeScript types across all packages
Terminal window
# Build all applications
pnpm build
# Build specific app
pnpm --filter webapp build
# Build Python packages
cd apps/pdf-api && uv run build
cd apps/pdf-worker && uv run build
  • Multi-stage builds for optimized production images
  • Layer caching for faster builds
  • Environment-specific configurations
  • Web Applications: Vercel, Netlify, or similar static hosting
  • API Services: Railway, Render, or container platforms
  • Background Workers: Container platforms with Redis support
  1. Create/Update Schemas: Add database tables in @repo/db
  2. Implement Business Logic: Add functions in appropriate packages
  3. Create API Routes: Add endpoints in relevant applications
  4. Update Types: Ensure TypeScript types are comprehensive
  5. Add Tests: Include unit and integration tests
  1. Create Package Structure: Follow existing package patterns
  2. Define Exports: Update package.json exports
  3. Add Dependencies: Use workspace references where possible
  4. Update Documentation: Include usage examples and API docs
  1. Schema Updates: Modify schema files in @repo/db
  2. Generate Migrations: Use Drizzle migration tools
  3. Test Migrations: Verify in development environment
  4. Deploy Safely: Use proper migration strategies
  • Package Resolution: Ensure workspace dependencies are properly configured
  • Environment Variables: Check both root and app-specific .env files
  • Database Connections: Verify organization context is properly set
  • Build Failures: Check for missing dependencies or type errors
Terminal window
# Check workspace status
pnpm list --depth=0
# Verify package builds
pnpm -r build
# Run tests with coverage
pnpm -r test --coverage
# Check database migrations
pnpm --filter @repo/db db:generate
  • Architecture Overview: See docs/architecture.md for system design details
  • API Documentation: Check individual app README files for API specifics
  • Deployment Guide: See docs/deployment-guide.md for production setup
  • Authentication Guide: See docs/authentication-architecture.md for auth details
  • Observability Guide: See docs/observability-architecture.md for tracing, logging & metrics setup