Marryme

Migration Guide - Wedding RSVP App Refactor

Comprehensive step-by-step migration guide for transitioning the Wedding RSVP application from legacy architecture to modern feature-based structure.

Reference: CONST-P1 (Modular Architecture), CONST-P11 (Version Control Excellence), CONST-P12 (Deployment & Production Readiness)

Overview

This migration guide provides detailed instructions for migrating the Wedding RSVP application from its original structure to a modern, feature-based architecture with enhanced security, performance, and maintainability.

Migration Scope

Pre-Migration Checklist


Phase 1: Foundation Setup (Week 1)

1.1 Project Structure Migration

Current Structure

src/
├── components/         # Mixed UI components
├── pages/             # Legacy page structure
├── styles/            # Basic CSS
└── utils/             # Utility functions

Target Structure

src/
├── app/                          # Next.js App Router
│   ├── admin/                    # Admin dashboard pages
│   ├── api/                      # API routes
│   │   ├── auth/                 # Authentication endpoints
│   │   └── rsvp/                 # RSVP endpoints
│   ├── globals.css               # Global styles
│   ├── layout.tsx                # Root layout
│   └── page.tsx                  # Home page
├── features/                     # Feature-based modules
│   ├── auth/                     # Authentication feature
│   │   ├── components/           # Auth UI components
│   │   ├── hooks/                # Auth-specific hooks
│   │   ├── services/             # Auth API operations
│   │   ├── types/                # Auth-specific types
│   │   └── index.ts              # Clean exports
│   ├── rsvp/                     # RSVP feature
│   │   ├── components/           # RSVP form components
│   │   ├── hooks/                # RSVP data management hooks
│   │   ├── services/             # RSVP API operations
│   │   ├── types/                # RSVP-specific types
│   │   └── index.ts
│   ├── admin/                    # Admin feature
│   │   ├── components/           # Admin UI components
│   │   ├── hooks/                # Admin data management hooks
│   │   ├── services/             # Admin API operations
│   │   ├── types/                # Admin-specific types
│   │   └── index.ts
│   └── content/                  # Wedding Content Feature
│       ├── components/           # Story, moments, schedule components
│       ├── hooks/                # Content management hooks
│       ├── services/             # Content API operations
│       ├── types/                # Content types and schemas
│       └── index.ts
├── shared/                       # Cross-cutting concerns
│   ├── components/               # Reusable UI (Button, Card, Input)
│   ├── hooks/                    # Shared React hooks
│   ├── utils/                    # Utility functions
│   ├── types/                    # Shared type definitions
│   └── constants/                # Application constants
└── lib/                          # Core libraries
    ├── services/                 # Base service classes
    ├── security/                 # Security utilities
    └── validation/               # Validation schemas

Migration Steps

  1. Create New Directory Structure
    # From project root
    mkdir -p src/features/{auth,rsvp,admin,content}/{components,hooks,services,types}
    mkdir -p src/shared/{components,hooks,utils,types,constants}
    mkdir -p src/lib/{services,security,validation}
    
  2. Move Existing Components ```bash

    Move auth-related components

    mv src/components/LoginForm.tsx src/features/auth/components/ mv src/components/AuthGuard.tsx src/features/auth/components/

Move RSVP-related components

mv src/components/RSVPForm.tsx src/features/rsvp/components/ mv src/components/RSVPStatus.tsx src/features/rsvp/components/

Move shared components

mv src/components/Button.tsx src/shared/components/ mv src/components/Card.tsx src/shared/components/ mv src/components/Input.tsx src/shared/components/


3. **Update Import Paths**
```typescript
// Before
import { LoginForm } from '../components/LoginForm';
import { Button } from '../components/Button';

// After
import { LoginForm } from '@/features/auth';
import { Button } from '@/shared/components';
  1. Create Feature Index Files ```typescript // src/features/auth/index.ts export { LoginForm } from ‘./components/LoginForm’; export { useAuth } from ‘./hooks/useAuth’; export { AuthService } from ‘./services/auth-service’; export type { AuthUser, LoginRequest } from ‘./types’;

// src/features/rsvp/index.ts export { RSVPForm } from ‘./components/RSVPForm’; export { useRSVP } from ‘./hooks/useRSVP’; export { RSVPService } from ‘./services/rsvp-service’; export type { RSVPFormData, RSVPResponse } from ‘./types’;


### 1.2 Design System Implementation

#### Tailwind Configuration Migration
```javascript
// tailwind.config.js - Updated wedding theme
module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
  theme: {
    extend: {
      colors: {
        // Wedding color palette
        primary: {
          50: '#fdf7f0',
          100: '#faeee1',
          // ... full color scale
          900: '#7c2d12',
        },
        secondary: {
          // Complementary colors
        },
      },
      fontFamily: {
        sans: ['Inter', 'sans-serif'],
        serif: ['Playfair Display', 'serif'],
      },
    },
  },
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
  ],
};

Component Library Setup

// src/shared/components/Button.tsx
interface ButtonProps {
  variant: 'primary' | 'secondary' | 'outline' | 'ghost';
  size: 'sm' | 'md' | 'lg';
  children: React.ReactNode;
}

export const Button: React.FC<ButtonProps> = ({ variant, size, children, ...props }) => {
  const baseClasses = 'font-medium rounded-lg transition-colors';
  const variants = {
    primary: 'bg-primary-600 text-white hover:bg-primary-700',
    secondary: 'bg-secondary-600 text-white hover:bg-secondary-700',
    outline: 'border border-primary-600 text-primary-600 hover:bg-primary-50',
    ghost: 'text-primary-600 hover:bg-primary-50',
  };
  
  return (
    <button 
      className={cn(baseClasses, variants[variant], sizes[size])} 
      {...props}
    >
      {children}
    </button>
  );
};

1.3 Type Safety & Validation

Zod Schema Implementation

// src/shared/types/rsvp.ts
import { z } from 'zod';

export const CreateRSVPSchema = z.object({
  name: z.string().min(1, 'Name is required').max(100),
  email: z.string().email('Invalid email format').max(255),
  phone: z.string().optional(),
  attendance: z.enum(['yes', 'no', 'maybe']),
  plusOne: z.boolean().default(false),
  plusOneName: z.string().optional(),
  dietaryRestrictions: z.string().max(500).optional(),
  message: z.string().max(1000).optional(),
});

export type CreateRSVPRequest = z.infer<typeof CreateRSVPSchema>;

TypeScript Configuration

// tsconfig.json - Strict mode enabled
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Phase 2: Core Features Migration (Week 2)

2.1 Authentication Feature Migration

Service Layer Implementation

// src/features/auth/services/auth-service.ts
export class AuthService {
  private static readonly ADMIN_CREDENTIALS = {
    username: 'admin',
    password: process.env.ADMIN_PASSWORD || 'wedding2025',
  };

  static async login(credentials: LoginRequest): Promise<AuthResponse> {
    // Input validation
    const { username, password } = LoginSchema.parse(credentials);
    
    // Security: Rate limiting implemented at API level
    if (username === this.ADMIN_CREDENTIALS.username && 
        password === this.ADMIN_CREDENTIALS.password) {
      
      const token = jwt.sign(
        { username, role: 'admin' },
        process.env.JWT_SECRET || 'dev-secret',
        { expiresIn: '24h' }
      );
      
      return { success: true, token, user: { username, role: 'admin' } };
    }
    
    throw new Error('Invalid credentials');
  }
}

Component Migration

// src/features/auth/components/LoginForm.tsx
import { useAuth } from '../hooks/useAuth';
import { Button } from '@/shared/components';

export const LoginForm: React.FC = () => {
  const { login, loading, error } = useAuth();
  
  // Component implementation with proper error handling
  // and loading states
};

2.2 RSVP Feature Enhancement

Enhanced Service Implementation

// src/features/rsvp/services/rsvp-service.ts
export class RSVPService {
  private static readonly DATA_FILE = path.join(process.cwd(), 'rsvps.json');

  static async create(data: CreateRSVPRequest): Promise<RSVP> {
    // Validation
    const validData = CreateRSVPSchema.parse(data);
    
    // Security: Input sanitization
    const sanitizedData = this.sanitizeInput(validData);
    
    // Read existing data
    const rsvps = await this.readRSVPs();
    
    // Check for duplicates
    const existingRSVP = rsvps.find(rsvp => rsvp.email === sanitizedData.email);
    if (existingRSVP) {
      throw new Error('RSVP already exists for this email');
    }
    
    // Create new RSVP
    const newRSVP: RSVP = {
      id: crypto.randomUUID(),
      ...sanitizedData,
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
    };
    
    // Save to file
    rsvps.push(newRSVP);
    await this.writeRSVPs(rsvps);
    
    return newRSVP;
  }
}

2.3 Admin Dashboard Migration

Dashboard Component

// src/features/admin/components/AdminDashboard.tsx
export const AdminDashboard: React.FC = () => {
  const { rsvps, loading, error } = useRSVPList();
  const [activeSection, setActiveSection] = useState<SectionKey>('rsvps');
  
  // Optimized with useMemo and useCallback
  const handleSectionChange = useCallback((section: SectionKey) => {
    setActiveSection(section);
  }, []);
  
  const sectionContent = useMemo(() => {
    switch (activeSection) {
      case 'rsvps':
        return <RSVPManagement rsvps={rsvps} />;
      case 'events':
        return <EventManagement />;
      case 'content':
        return <ContentManagement />;
      default:
        return null;
    }
  }, [activeSection, rsvps]);
  
  return (
    <div className="admin-dashboard">
      {/* Dashboard implementation */}
    </div>
  );
};

Phase 3: Security & Infrastructure (Week 3)

3.1 Security Infrastructure Implementation

Comprehensive Security Headers

// src/lib/security/headers.ts
export const securityHeaders = {
  'X-Frame-Options': 'DENY',
  'X-Content-Type-Options': 'nosniff',
  'X-XSS-Protection': '1; mode=block',
  'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
  'Referrer-Policy': 'strict-origin-when-cross-origin',
  'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';",
};

Rate Limiting Implementation

// src/lib/security/rate-limit.ts
const rateLimits = new Map<string, { count: number; resetTime: number }>();

export function checkRateLimit(identifier: string, maxRequests: number, windowMs: number): boolean {
  const now = Date.now();
  const limit = rateLimits.get(identifier);
  
  if (!limit || now > limit.resetTime) {
    rateLimits.set(identifier, { count: 1, resetTime: now + windowMs });
    return true;
  }
  
  if (limit.count >= maxRequests) {
    return false;
  }
  
  limit.count++;
  return true;
}

Input Sanitization

// src/lib/security/sanitization.ts
export function sanitizeString(input: string): string {
  return input
    .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
    .replace(/javascript:/gi, '')
    .replace(/on\w+="[^"]*"/gi, '')
    .replace(/on\w+='[^']*'/gi, '')
    .trim();
}

3.2 API Security Enhancement

Protected API Routes

// src/app/api/auth/login/route.ts
export async function POST(request: Request) {
  // Security headers
  const headers = new Headers(securityHeaders);
  
  try {
    // Rate limiting
    const clientIP = getClientIP(request);
    if (!checkRateLimit(`auth:${clientIP}`, 5, 15 * 60 * 1000)) {
      return NextResponse.json(
        { error: 'Too many login attempts. Please try again later.' },
        { status: 429, headers }
      );
    }
    
    // Input validation and sanitization
    const body = await request.json();
    const credentials = LoginSchema.parse(body);
    
    // Authentication
    const result = await AuthService.login(credentials);
    
    return NextResponse.json(result, { headers });
  } catch (error) {
    return NextResponse.json(
      { error: 'Authentication failed' },
      { status: 401, headers }
    );
  }
}

Phase 4: Database Migration Strategy

4.1 Current State: JSON File Storage

Data Structure

{
  "rsvps": [
    {
      "id": "uuid",
      "name": "string",
      "email": "string",
      "attendance": "yes|no|maybe",
      "createdAt": "ISO string",
      "updatedAt": "ISO string"
    }
  ]
}

4.2 Migration Path: JSON → SQLite → PostgreSQL

Step 1: Implement Data Access Layer

// src/lib/data/data-access.ts
export abstract class DataAccess {
  abstract create<T>(data: T): Promise<T>;
  abstract findById<T>(id: string): Promise<T | null>;
  abstract findMany<T>(filter?: object): Promise<T[]>;
  abstract update<T>(id: string, data: Partial<T>): Promise<T>;
  abstract delete(id: string): Promise<void>;
}

// JSON implementation
export class JSONDataAccess extends DataAccess {
  // Current JSON file operations
}

// Future SQLite implementation
export class SQLiteDataAccess extends DataAccess {
  // SQLite operations
}

Step 2: Migration Scripts

// scripts/migrate-json-to-sqlite.ts
export async function migrateJSONToSQLite() {
  console.log('Starting JSON to SQLite migration...');
  
  // 1. Read existing JSON data
  const jsonData = await fs.readFile('rsvps.json', 'utf-8');
  const rsvps = JSON.parse(jsonData);
  
  // 2. Initialize SQLite database
  const db = new Database('wedding-rsvp.db');
  
  // 3. Create tables
  await db.exec(`
    CREATE TABLE IF NOT EXISTS rsvps (
      id TEXT PRIMARY KEY,
      name TEXT NOT NULL,
      email TEXT UNIQUE NOT NULL,
      phone TEXT,
      attendance TEXT NOT NULL CHECK (attendance IN ('yes', 'no', 'maybe')),
      plus_one BOOLEAN DEFAULT FALSE,
      plus_one_name TEXT,
      dietary_restrictions TEXT,
      message TEXT,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
    );
  `);
  
  // 4. Migrate data
  for (const rsvp of rsvps.rsvps || []) {
    await db.run(`
      INSERT INTO rsvps (id, name, email, phone, attendance, plus_one, plus_one_name, dietary_restrictions, message, created_at, updated_at)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `, [
      rsvp.id,
      rsvp.name,
      rsvp.email,
      rsvp.phone || null,
      rsvp.attendance,
      rsvp.plusOne || false,
      rsvp.plusOneName || null,
      rsvp.dietaryRestrictions || null,
      rsvp.message || null,
      rsvp.createdAt,
      rsvp.updatedAt,
    ]);
  }
  
  // 5. Create backup
  await fs.copyFile('rsvps.json', `rsvps.json.backup.${Date.now()}`);
  
  console.log(`Migration complete. Migrated ${rsvps.rsvps?.length || 0} RSVPs.`);
}

Step 3: Database Configuration

// src/lib/database/config.ts
export const databaseConfig = {
  development: {
    type: 'json',
    file: 'rsvps.json',
  },
  production: {
    type: 'sqlite',
    file: 'wedding-rsvp.db',
    // Future PostgreSQL config
    // host: process.env.DB_HOST,
    // port: parseInt(process.env.DB_PORT || '5432'),
    // database: process.env.DB_NAME,
    // username: process.env.DB_USER,
    // password: process.env.DB_PASSWORD,
  },
};

Phase 5: Deployment & Production (Week 4)

5.1 Environment Configuration

Environment Variables

# .env.example
# Authentication
ADMIN_PASSWORD=your_secure_admin_password
JWT_SECRET=your_jwt_secret_key_minimum_32_characters

# Database (for future SQLite/PostgreSQL)
DATABASE_URL=file:./wedding-rsvp.db
# DATABASE_URL=postgresql://username:password@localhost:5432/wedding_rsvp

# Email (for future email integration)
EMAIL_FROM=noreply@yourweddingdomain.com
RESEND_API_KEY=your_resend_api_key

# Analytics (optional)
VERCEL_ANALYTICS_ID=your_analytics_id

# Security
ALLOWED_ORIGINS=https://yourweddingdomain.com,https://www.yourweddingdomain.com

5.2 Production Deployment Checklist

Pre-Deployment

Deployment Steps

# 1. Build verification
npm run build
npm run start

# 2. Environment setup
cp .env.example .env.local
# Edit .env.local with production values

# 3. Security verification
npm audit --audit-level moderate

# 4. Deploy to Vercel
npx vercel --prod

# 5. Post-deployment verification
curl -I https://your-domain.com
curl -X POST https://your-domain.com/api/rsvp -d '{"test": "security"}'

Post-Deployment Monitoring

// Monitor key metrics
const monitoringChecks = {
  'Response Time': 'Average < 2 seconds',
  'Error Rate': '< 1% of requests',
  'Security Headers': 'All headers present',
  'Rate Limiting': 'Blocks excessive requests',
  'RSVP Submissions': 'Successful storage',
  'Admin Access': 'Secure login working',
};

Rollback Procedures

Emergency Rollback Plan

If Migration Fails During Phase 1-2

# 1. Stop current deployment
git checkout main

# 2. Restore from backup
cp rsvps.json.backup rsvps.json

# 3. Redeploy previous version
npm run build
npm run start

# 4. Verify functionality
curl https://your-domain.com/api/rsvp

If Database Migration Fails

# 1. Stop application
pkill -f "npm run start"

# 2. Restore JSON backup
cp rsvps.json.backup.{timestamp} rsvps.json

# 3. Revert database configuration
git checkout HEAD~1 -- src/lib/database/

# 4. Restart with JSON storage
npm run start

Data Recovery Procedures

// scripts/data-recovery.ts
export async function recoverData() {
  // 1. Check for JSON backup files
  const backupFiles = await fs.readdir('.').then(files => 
    files.filter(f => f.startsWith('rsvps.json.backup.'))
  );
  
  // 2. Find most recent backup
  const latestBackup = backupFiles.sort().reverse()[0];
  
  // 3. Validate backup data
  const backupData = JSON.parse(await fs.readFile(latestBackup, 'utf-8'));
  
  // 4. Restore from backup
  await fs.copyFile(latestBackup, 'rsvps.json');
  
  console.log(`Data recovered from ${latestBackup}`);
}

Testing & Validation

5.1 Migration Testing Checklist

Automated Tests

# Unit tests
npm run test

# Integration tests
npm run test:integration

# E2E tests
npm run test:e2e

# Performance tests
npm run test:performance

Manual Testing Scenarios

RSVP Functionality

Admin Dashboard

Security Testing

5.2 Performance Validation

Key Metrics

const performanceTargets = {
  'Page Load Time': '< 3 seconds',
  'First Contentful Paint': '< 1.5 seconds',
  'Largest Contentful Paint': '< 2.5 seconds',
  'Cumulative Layout Shift': '< 0.1',
  'First Input Delay': '< 100ms',
  'Bundle Size': '< 150kB gzipped',
};

Lighthouse Audit

# Install Lighthouse
npm install -g lighthouse

# Run audit
lighthouse https://your-domain.com --output html --output-path ./lighthouse-report.html

# Target scores
# Performance: > 90
# Accessibility: > 90
# Best Practices: > 90
# SEO: > 90

Troubleshooting

Common Migration Issues

TypeScript Compilation Errors

# Clear TypeScript cache
rm -rf .next/cache
rm -rf node_modules/.cache

# Reinstall dependencies
npm ci

# Check TypeScript version
npx tsc --version

Build Failures

# Check for missing environment variables
npm run build 2>&1 | grep -i "environment"

# Verify all imports
npm run build 2>&1 | grep -i "module not found"

# Check bundle analysis
npm run build && npm run analyze

Runtime Security Errors

// Debug security headers
const debugHeaders = (request: Request) => {
  console.log('Request headers:', Object.fromEntries(request.headers.entries()));
  console.log('Origin:', request.headers.get('origin'));
  console.log('Referer:', request.headers.get('referer'));
};

Support Contacts


Success Criteria

Phase Completion Validation

Phase 1: Foundation ✅

Phase 2: Core Features ✅

Phase 3: Security ✅

Phase 4: Database (Future)

Phase 5: Production

Final Acceptance Criteria

Technical Excellence

Business Requirements


Maintenance & Future Enhancements

Immediate Post-Migration Tasks

  1. Monitor error logs for any runtime issues
  2. Track RSVP submission success rates
  3. Verify security measures are functioning
  4. Collect user feedback on new interface
  5. Document any configuration changes needed

Planned Future Enhancements

  1. Database Migration: JSON → SQLite → PostgreSQL
  2. Email Integration: Automated RSVP confirmations
  3. Testing Infrastructure: Comprehensive test suite
  4. Performance Monitoring: Real-time analytics
  5. Content Management: Dynamic wedding content updates

Long-term Maintenance


This migration guide is a living document. Update it as needed during the migration process to reflect any changes or lessons learned.