Skip to main content

Migration Guide

Complete guide to the Clean Architecture restructure of Send v5.

Overview

The repository has been restructured from a tightly-coupled monolith to a Clean Architecture monorepo with proper separation of concerns.

What Changed

1. Monorepo Structure

Before

projects/games/wheel/  (single app)
dashboard/             (separate app)

After

games/
├── package.json      (npm workspaces)
└── wheel/
    ├── wheel-core/           (domain layer)
    ├── wheel-infrastructure/ (infrastructure layer)
    ├── wheel-dashboard/      (admin UI)
    └── wheel-instances/      (public UI)

api/
├── Cargo.toml        (Rust workspace)
└── wheel/            (Rust API)

2. Config Schema Migration

Before: Hash as Primary Key

CREATE TABLE configs (
  id_config VARCHAR(64) PRIMARY KEY  -- Hash was PK
);

After: UUID Primary Key with Hash Deduplication

CREATE TABLE configs (
  id_config UUID PRIMARY KEY,
  hash VARCHAR(64) NOT NULL,
  UNIQUE (tenant_id, hash)  -- Deduplicate by hash
);
Migration Steps:
  1. Add new id column as UUID
  2. Add hash column with old PK values
  3. Update all foreign keys from config to config_id
  4. Update application code to use config_id

3. API Structure

Before: Nested Structure

Games
└── Wheel
    ├── Profiles
    ├── Instances
    └── Configs

After: Flat Resource Structure

Games API
├── Getting Started
├── Profiles (5 endpoints)
├── Instances (5 endpoints)
├── Prizes (5 endpoints)
├── Configs (4 endpoints)
└── Links (6 endpoints)

Breaking Changes

Config References

Before:
instance.configHash  // VARCHAR(64)
After:
instance.configId    // UUID

Repository Methods

Before:
configRepo.findByHash(hash);  // Only method
configRepo.delete(hash);      // Delete by hash
After:
configRepo.findById(id);       // Primary lookup
configRepo.findByHash(hash);   // For deduplication
configRepo.createOrGet(config); // Auto-deduplicates
configRepo.delete(id);          // Delete by ID

Migration Path

For Existing Data

-- Step 1: Backup
CREATE TABLE configs_backup AS SELECT * FROM configs;

-- Step 2: Add new columns
ALTER TABLE configs ADD COLUMN id_config_new UUID DEFAULT gen_random_uuid();
ALTER TABLE configs ADD COLUMN hash_new VARCHAR(64);

-- Step 3: Migrate data
UPDATE configs SET hash_new = id_config;  -- Old PK becomes hash
UPDATE configs SET id_config_new = gen_random_uuid();

-- Step 4: Update foreign keys
ALTER TABLE instances ADD COLUMN config_id_new UUID;
UPDATE instances i 
SET config_id_new = (
  SELECT id_config_new FROM configs c WHERE c.hash_new = i.config
);

-- Step 5: Drop old columns and rename
ALTER TABLE configs DROP COLUMN id_config;
ALTER TABLE configs RENAME COLUMN id_config_new TO id_config;
ALTER TABLE configs RENAME COLUMN hash_new TO hash;

ALTER TABLE instances DROP COLUMN config;
ALTER TABLE instances RENAME COLUMN config_id_new TO config_id;

-- Step 6: Add constraints
ALTER TABLE configs ADD PRIMARY KEY (id_config);
ALTER TABLE configs ADD CONSTRAINT unique_tenant_hash UNIQUE (tenant_id, hash);
ALTER TABLE instances ADD FOREIGN KEY (config_id) REFERENCES configs(id_config);

For Application Code

Update all references from config to config_id and from config_hash to config_id.

New Features

Config Deduplication

Automatically returns existing config if hash matches:
// First call
const config1 = await configRepo.createOrGet(configData);
// Returns: { id: "uuid-1", hash: "abc123..." }

// Second call with same data
const config2 = await configRepo.createOrGet(configData);
// Returns: { id: "uuid-1", hash: "abc123..." }  // Same ID!

Interactive API Playground

Test API endpoints directly in documentation:
  1. Navigate to any endpoint page
  2. Click “Try it” button
  3. Fill in parameters
  4. See live request/response
  5. Copy code snippets in 4 languages

Package Structure

wheel-core (Domain)

  • 5 entities with business rules
  • 5 use cases
  • 5 repository interfaces
  • Zero framework dependencies

wheel-infrastructure (Infrastructure)

  • 5 Nile repository implementations
  • Database schema
  • Nile client setup

wheel-dashboard (Admin UI)

  • Next.js 15 SSR
  • Material-UI
  • Dependency injection
  • Port 3001

wheel-instances (Public UI)

  • Next.js 15 SSG
  • Dynamic routing
  • Optimized for performance
  • Port 3002

api/wheel (Rust API)

  • Axum web framework
  • Clean Architecture in Rust
  • All endpoints defined
  • Port 3003

Deployment

No changes to deployment strategy:
app.{domain}     → wheel-dashboard
{domain}         → wheel-instances
api.{domain}     → api/wheel

Rollback Plan

If issues arise:
  1. Restore from /projects/games/wheel/ (old structure still exists)
  2. Revert database schema changes using backup
  3. Switch Cloudflare routing back to old deployment

Support

Questions? Check: Or contact: [email protected]