ddd_domain/README.md

317 lines
8.3 KiB
Markdown

# ddd_domain
Database-driven application using [half-orm](https://github.com/half-orm/half-orm) with [half-orm-dev](https://github.com/half-orm/half-orm-dev) workflow.
**Database:** `ddd_domain`
**Generated with:** half-orm-dev==1.0.0-a32
---
## 🚀 Quick Start
### Prerequisites
```bash
# Install half-orm-dev (includes half-orm)
# IMPORTANT: Install the version specified in .hop/config.
# Replace <version> by the value of hop_version.
pip install half-orm-dev==<version>
```
**Version requirement:** This project requires `half-orm-dev >= 1.0.0-a32`
### Clone This Project
```bash
# Clone project with automatic database setup (use git_origin in .hop/config)
half_orm dev clone <git_origin>
```
This will:
- Clone the repository and checkout `ho-prod` branch
- Configure database connection in `/etc/half_orm/ddd_domain` or `${HALFORM_CONF_DIR}/ddd_domain`
- Restore production schema from `.hop/model/schema.sql`
- Generate Python ORM classes in `ddd_domain/` directory
---
## 📖 Development Workflow
This project uses **half-orm-dev** for database versioning and patch management.
### 1. Create a Release
```bash
# Create new minor release (e.g., 0.17.0)
half_orm dev release create minor
# Creates ho-release/0.17.0 branch
# Creates .hop/releases/0.17.0-patches.toml
```
### 2. Create a Patch
# Create patch (auto-added as candidate)
half_orm dev patch create <number>-<description>
# Example: Create user authentication patch
half_orm dev patch create 456-user-auth
# Creates ho-patch/456-user-auth branch
# Creates Patches/456-user-auth/ directory
```
### 3. Develop Your Changes
```bash
# Add SQL migration
echo "CREATE TABLE users (id SERIAL PRIMARY KEY, username TEXT);" > Patches/456-user-auth/1-create-users.sql
# Add Python migration (optional)
cat > Patches/456-user-auth/2-seed-data.py << 'EOF'
from ddd_domain.public.users import Users
def apply(model):
Users(username='admin').ho_insert()
EOF
# Apply patch to test database
half_orm dev patch apply
# Generated ORM classes available in ddd_domain/
from ddd_domain.public.users import Users
user = Users(username='john').ho_insert()
```
### 4. Test Your Changes
```bash
# Run tests (pytest must be configured)
pytest tests/
# half-orm-dev validates:
# - Patch SQL syntax
# - Patch idempotency (can be applied twice)
# - Test suite passes with full release context
```
### 5. Merge Patch into Release
```bash
# Merge patch (automatic validation + tests)
half_orm dev patch merge
# What happens:
# 1. Creates temp validation branch
# 2. Applies ALL staged patches + your patch
# 3. Runs pytest tests/
# 4. If tests pass → merges into ho-release/X.Y.Z
# 5. Changes patch status to "staged" in TOML
# 6. Deletes patch branch
```
### 6. Promote Release
```bash
# Stage → RC (first release candidate)
half_orm dev release promote rc
# Creates .hop/releases/0.17.0-rc1.txt snapshot
# Tags ho-release-0.17.0-rc1
# RC → Production (after validation)
half_orm dev release promote prod
# Merges into ho-prod branch
# Creates .hop/releases/0.17.0.txt
# Tags production-0.17.0
# Generates .hop/model/schema-0.17.0.sql
```
---
## 🔧 Common Commands
### Status & Information
```bash
half_orm dev check # Show repository state
```
### Patch Management
```bash
half_orm dev patch create # create a new patch for release
half_orm dev patch apply # Apply patch to dev database
half_orm dev patch merge # Integrate patch into release
```
### Release Management
```bash
half_orm dev release create <level> # Create release (patch/minor/major)
half_orm dev release promote rc # Stage → RC
half_orm dev release promote prod # RC → Production
```
### Production Deployment
```bash
# On production server
half_orm dev update # Check available releases
half_orm dev upgrade <version> # Deploy specific version
```
---
## 📁 Project Structure
```
ddd_domain/
├── .hop/ # half-orm-dev metadata
│ ├── config # Repository configuration
│ ├── model/ # Database schemas and data
│ │ ├── schema.sql # Current production schema (symlink)
│ │ ├── schema-X.Y.Z.sql # Versioned schemas
│ │ ├── metadata-X.Y.Z.sql # half_orm_meta data dumps
│ │ └── data-X.Y.Z.sql # Reference data from @HOP:data patches
│ └── releases/ # Release tracking files
│ ├── X.Y.Z-patches.toml # Development releases (mutable)
│ ├── X.Y.Z-rcN.txt # Release candidates (immutable)
│ └── X.Y.Z.txt # Production releases (immutable)
├── Patches/ # Patch directories
│ └── <id>-<description>/ # Individual patch files
│ ├── *.sql # SQL migrations (numbered)
│ └── *.py # Python migrations (optional)
├── ddd_domain/ # Generated ORM package
│ └── <schema>/ # schema classes
└── tests/ # Test suite (pytest)
```
---
## 🌿 Git Branch Structure
- **`ho-prod`**: Production branch (stable, source of truth)
- **`ho-release/X.Y.Z`**: Release integration branches (temporary)
- **`ho-patch/ID`**: Patch development branches (temporary)
All development happens on patch branches, merged into release branches, then promoted to production.
---
## 💾 Data Persistence (@HOP:data)
For reference data that must be loaded with every database installation (lookup tables, default roles, etc.), use the `@HOP:data` annotation:
```sql
-- @HOP:data
-- This file will be included in model/data-X.Y.Z.sql
INSERT INTO roles (name, description)
VALUES ('admin', 'Administrator')
ON CONFLICT (name) DO NOTHING;
INSERT INTO permissions (name)
VALUES ('read'), ('write'), ('delete')
ON CONFLICT DO NOTHING;
```
### How it works
1. **In patches**: SQL files starting with `-- @HOP:data` contain reference data
2. **Production promote**: All `@HOP:data` files are consolidated into `model/data-X.Y.Z.sql`
3. **Clone/Restore**: Data files are loaded automatically after schema restoration
4. **Production upgrade**: Data is inserted via normal patch application (no special handling)
### Best practices
- Use `ON CONFLICT DO NOTHING` or `ON CONFLICT DO UPDATE` for idempotency
- Keep data files small and focused (one concern per file)
- Number your SQL files to control execution order: `01_roles.sql`, `02_permissions.sql`
- Only use for **reference data**, not user-generated data
---
## ⚡ Async Usage
The generated package supports both synchronous and asynchronous access to the database.
### Sync
```python
from ddd_domain.public.users import Users
user = Users(username='john').ho_get()
users = list(Users(is_active=True).ho_select())
```
### Async
```python
import asyncio
from ddd_domain import aconnect, adisconnect
from ddd_domain.public.users import Users
async def main():
await aconnect()
try:
user = await Users(username='john').ho_aget()
users = await Users(is_active=True).ho_aselect()
finally:
await adisconnect()
asyncio.run(main())
```
### REST API (FastAPI)
```python
from contextlib import asynccontextmanager
from fastapi import FastAPI
import ddd_domain
@asynccontextmanager
async def lifespan(app: FastAPI):
await ddd_domain.aconnect()
yield
await ddd_domain.adisconnect()
app = FastAPI(lifespan=lifespan)
```
The async connection pool is established automatically during tests via the `_async_pool`
fixture in `tests/conftest.py`.
---
## 📚 Documentation
- **half-orm-dev**: https://github.com/half-orm/half-orm-dev
- **half-orm**: https://github.com/half-orm/half-orm
- **Workflow guide**: https://github.com/half-orm/half-orm-dev#readme
---
## 🆘 Getting Help
```bash
half_orm dev --help # General help
half_orm dev patch --help # Patch commands
half_orm dev release --help # Release commands
```
---
## ⚠️ Important Notes
1. **Always work on patch branches** - Never commit directly to ho-prod or ho-release
2. **Test before merging** - `patch merge` runs tests automatically
3. **Sequential releases** - Only the smallest version in preparation can be promoted
4. **Idempotent patches** - SQL must be replayable (use `IF NOT EXISTS`, etc.)
5. **Production schema** - `.hop/model/schema.sql` is the source of truth
---
Generated by half-orm-dev 1.0.0-a32