feat: set up monorepo scaffold with Yarn workspaces, Django API, React/Vite web, and Docker Compose
- Root package.json with Yarn workspaces (web, api) - /web: React 19 + Vite 6 + @vitejs/plugin-react - index.html, src/main.jsx, src/App.jsx, vite.config.js - /api: Django 5.x managed with uv - pyproject.toml with Django + psycopg2-binary - project/settings.py with PostgreSQL config from DATABASE_URL - project/urls.py, project/wsgi.py, manage.py - docker-compose.yml with db (postgres:15), api, web services - Health check for db, live code volumes, dependency ordering - Dockerfiles for both web (node:20-alpine) and api (python:3.12-slim) - .gitignore updated for Python build artifacts and venv - docs/implementation-issue-1-monorepo-scaffold.md
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
# Monorepo Scaffold — Implementation Document
|
||||
|
||||
> **Issue:** [#1 — Set up monorepo scaffold with Yarn workspaces for Web and API](https://gitea-dev.codescripters.org/crisleo-hermes/job-tracker/issues/1)
|
||||
>
|
||||
> **Branch:** `feature/setup-monorepo-scaffold`
|
||||
>
|
||||
> **Status:** ✅ Implemented
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
job-tracker/
|
||||
├── package.json # Yarn workspaces root ("web", "api")
|
||||
├── docker-compose.yml # PostgreSQL 15 + Django API + React/Vite web
|
||||
├── README.md
|
||||
├── .gitignore
|
||||
│
|
||||
├── api/ # Django REST API (Python 3.12)
|
||||
│ ├── Dockerfile
|
||||
│ ├── pyproject.toml # uv-managed Python dependencies
|
||||
│ ├── uv.lock
|
||||
│ ├── manage.py # Django management CLI entrypoint
|
||||
│ └── project/
|
||||
│ ├── __init__.py
|
||||
│ ├── settings.py # PostgreSQL config via DATABASE_URL
|
||||
│ ├── urls.py # Root URL configuration
|
||||
│ └── wsgi.py # WSGI application
|
||||
│
|
||||
└── web/ # React 19 + Vite frontend
|
||||
├── Dockerfile
|
||||
├── package.json # React 19, Vite 6, @vitejs/plugin-react
|
||||
├── vite.config.js # Dev server on 0.0.0.0:3000, polling
|
||||
├── index.html # HTML entry point
|
||||
└── src/
|
||||
├── main.jsx # ReactDOM.createRoot mount
|
||||
└── App.jsx # Basic App component
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Root `package.json` — Yarn Workspaces
|
||||
|
||||
**File:** `package.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "job-tracker",
|
||||
"private": true,
|
||||
"workspaces": ["web", "api"]
|
||||
}
|
||||
```
|
||||
|
||||
- Declares `web` and `api` as Yarn workspace members.
|
||||
- Dependencies from each workspace are hoisted to the root `node_modules/` where possible.
|
||||
|
||||
> **Note:** `"api"` is listed as a workspace member purely for the monorepo structure. The actual Python/Django dependencies are managed via `uv` (see Section 3).
|
||||
|
||||
---
|
||||
|
||||
## 2. Docker Compose — Development Environment
|
||||
|
||||
**File:** `docker-compose.yml`
|
||||
|
||||
Three services orchestrated for local development:
|
||||
|
||||
### `db` (PostgreSQL 15)
|
||||
|
||||
| Setting | Value |
|
||||
|------------------|------------------------------------------|
|
||||
| Image | `postgres:15` |
|
||||
| DB Name | `jobtracker` |
|
||||
| User / Password | `jobtracker` / `jobtracker` |
|
||||
| Port | `5432` |
|
||||
| Volume | `postgres_data:/var/lib/postgresql/data` |
|
||||
| Health Check | `pg_isready -U jobtracker -d jobtracker` (5s interval, 10 retries) |
|
||||
|
||||
### `api` (Django)
|
||||
|
||||
| Setting | Value |
|
||||
|------------------|-----------------------------------------------|
|
||||
| Build Context | `./api` |
|
||||
| Port | `8000` |
|
||||
| Env Vars | `DATABASE_URL=postgres://jobtracker:...@db:5432/jobtracker` |
|
||||
| Volumes | `./api:/app` (live code reload) |
|
||||
| Command | `python manage.py migrate && python manage.py runserver 0.0.0.0:8000` |
|
||||
| Depends On | `db` → `condition: service_healthy` |
|
||||
|
||||
### `web` (React + Vite)
|
||||
|
||||
| Setting | Value |
|
||||
|------------------|-----------------------------------------------|
|
||||
| Build Context | `./web` |
|
||||
| Port | `3000` |
|
||||
| Env Vars | `REACT_APP_API_URL=http://localhost:8000` |
|
||||
| Volumes | `./web:/app` + `/app/node_modules` (live reload) |
|
||||
| Depends On | `db` → `condition: service_healthy` |
|
||||
|
||||
### How to start
|
||||
|
||||
```bash
|
||||
docker compose up
|
||||
```
|
||||
|
||||
- **Web:** http://localhost:3000
|
||||
- **API:** http://localhost:8000
|
||||
- **DB:** `postgres://jobtracker:jobtracker@localhost:5432/jobtracker`
|
||||
|
||||
---
|
||||
|
||||
## 3. Django API (`/api`)
|
||||
|
||||
### Dependency Management with `uv`
|
||||
|
||||
**File:** `api/pyproject.toml`
|
||||
|
||||
```toml
|
||||
[project]
|
||||
name = "api"
|
||||
version = "1.0.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"django>=5.1,<6.0",
|
||||
"psycopg2-binary>=2.9",
|
||||
]
|
||||
```
|
||||
|
||||
Install and verify:
|
||||
|
||||
```bash
|
||||
cd api
|
||||
uv sync
|
||||
uv run python manage.py check
|
||||
# → System check identified no issues (0 silenced).
|
||||
```
|
||||
|
||||
### Database Configuration (`settings.py`)
|
||||
|
||||
The `DATABASES` setting is built dynamically from the `DATABASE_URL` environment variable:
|
||||
|
||||
```python
|
||||
DATABASE_URL = "postgres://user:password@host:port/dbname"
|
||||
```
|
||||
|
||||
Parsed into the standard Django `DATABASES` dict with:
|
||||
- `ENGINE`: `django.db.backends.postgresql`
|
||||
- `NAME`, `USER`, `PASSWORD`, `HOST`, `PORT` — all extracted from the URL
|
||||
|
||||
Default value when `DATABASE_URL` is unset:
|
||||
`postgres://jobtracker:jobtracker@localhost:5432/jobtracker`
|
||||
|
||||
### Installed Apps (Minimal)
|
||||
|
||||
```python
|
||||
INSTALLED_APPS = [
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.auth",
|
||||
]
|
||||
```
|
||||
|
||||
### Files Created
|
||||
|
||||
| File | Purpose |
|
||||
|----------------------|--------------------------------------------|
|
||||
| `api/Dockerfile` | Python 3.12-slim + uv + libpq → Django dev |
|
||||
| `api/manage.py` | Django CLI entrypoint |
|
||||
| `api/pyproject.toml` | uv project config with Django + psycopg2 |
|
||||
| `api/project/__init__.py` | Python package marker |
|
||||
| `api/project/settings.py` | Django settings with PostgreSQL |
|
||||
| `api/project/urls.py` | Root URL configuration |
|
||||
| `api/project/wsgi.py` | WSGI application |
|
||||
|
||||
---
|
||||
|
||||
## 4. React Frontend (`/web`)
|
||||
|
||||
### Dependencies
|
||||
|
||||
| Package | Version | Type |
|
||||
|----------------------|---------|----------------|
|
||||
| `react` | ^19.0.0 | dependency |
|
||||
| `react-dom` | ^19.0.0 | dependency |
|
||||
| `vite` | ^6.0.0 | devDependency |
|
||||
| `@vitejs/plugin-react` | ^4.3.0 | devDependency |
|
||||
|
||||
### Vite Configuration
|
||||
|
||||
**File:** `web/vite.config.js`
|
||||
|
||||
```js
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
port: 3000,
|
||||
watch: { usePolling: true }, // required for Docker volume mounts
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Entry Point
|
||||
|
||||
**File:** `web/src/main.jsx` — mounts `<App />` inside `React.StrictMode` on the `#root` element.
|
||||
|
||||
### Component
|
||||
|
||||
**File:** `web/src/App.jsx` — minimal functional component:
|
||||
|
||||
```jsx
|
||||
function App() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Job Tracker</h1>
|
||||
<p>Welcome to the Job Tracker application.</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Files Created
|
||||
|
||||
| File | Purpose |
|
||||
|-----------------------|-----------------------------------------------|
|
||||
| `web/Dockerfile` | Node 20-alpine + yarn → dev server on :3000 |
|
||||
| `web/package.json` | React 19 + Vite 6 dependencies |
|
||||
| `web/vite.config.js` | Vite dev server config with polling |
|
||||
| `web/index.html` | HTML shell loading `/src/main.jsx` |
|
||||
| `web/src/main.jsx` | React entry point |
|
||||
| `web/src/App.jsx` | Basic App component |
|
||||
|
||||
---
|
||||
|
||||
## 5. Dockerfiles
|
||||
|
||||
### `api/Dockerfile`
|
||||
|
||||
```dockerfile
|
||||
FROM python:3.12-slim
|
||||
WORKDIR /app
|
||||
RUN apt-get update && apt-get install -y libpq-dev gcc && rm -rf /var/lib/apt/lists/*
|
||||
COPY pyproject.toml ./
|
||||
RUN pip install uv && uv sync --frozen
|
||||
COPY . .
|
||||
EXPOSE 8000
|
||||
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
|
||||
```
|
||||
|
||||
### `web/Dockerfile`
|
||||
|
||||
```dockerfile
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
COPY package.json ./
|
||||
RUN yarn install --frozen-lockfile
|
||||
COPY . .
|
||||
EXPOSE 3000
|
||||
CMD ["yarn", "dev"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
### Django (standalone)
|
||||
|
||||
```bash
|
||||
cd api
|
||||
uv sync
|
||||
uv run python manage.py check
|
||||
# → System check identified no issues (0 silenced).
|
||||
```
|
||||
|
||||
### Frontend (standalone)
|
||||
|
||||
```bash
|
||||
cd web
|
||||
yarn install
|
||||
yarn dev
|
||||
# → Vite dev server running on http://localhost:3000
|
||||
```
|
||||
|
||||
### Full stack (Docker)
|
||||
|
||||
```bash
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables Summary
|
||||
|
||||
| Variable | Default | Used By |
|
||||
|-------------------|------------------------------------------------------|---------|
|
||||
| `DATABASE_URL` | `postgres://jobtracker:jobtracker@localhost:5432/jobtracker` | API |
|
||||
| `REACT_APP_API_URL` | `http://localhost:8000` | Web |
|
||||
| `DJANGO_SECRET_KEY` | `django-insecure-change-me-in-production` | API |
|
||||
| `DJANGO_DEBUG` | `True` | API |
|
||||
Reference in New Issue
Block a user