1. Cấu trúc thư mục

src/
├── routes/
│   ├── (admin)/
│   │   └── admin/
│   │       ├── dashboard/+page.svelte
│   │       ├── events/
│   │       │   ├── +page.svelte
│   │       │   ├── new/+page.svelte
│   │       │   └── [id]/+page.svelte
│   │       └── +layout.svelte
│   ├── (customer)/
│   │   ├── events/
│   │   │   ├── [id]/
│   │   │   │   ├── +page.svelte
│   │   │   │   ├── seats/+page.svelte
│   │   │   │   └── queue/+page.svelte
│   │   │   └── +page.svelte         # trang chủ
│   │   ├── checkout/[id]/+page.svelte
│   │   ├── me/tickets/+page.svelte
│   │   └── +layout.svelte
│   ├── login/+page.svelte
│   ├── register/+page.svelte
│   └── api/
│       ├── auth/
│       │   ├── register/+server.ts
│       │   └── login/+server.ts
│       ├── events/
│       │   ├── +server.ts            # GET list
│       │   └── [id]/
│       │       ├── +server.ts        # GET detail
│       │       ├── queue/+server.ts    # POST join, GET status
│       │       └── seats/
│       │           ├── +server.ts    # GET seats
│       │           ├── hold/+server.ts # POST hold
│       │           └── stream/+server.ts # GET SSE
│       ├── orders/
│       │   └── [id]/checkout/+server.ts
│       ├── me/tickets/+server.ts
│       └── stats/
│            ├── revenue/+server.ts      # middleware: requireAdmin
│            ├── occupancy/+server.ts
│            └── demographics/+server.ts
├── lib/
│   ├── server/
│   │   ├── db/
│   │   │   ├── index.ts              # drizzle client
│   │   │   ├── schema.ts             # all tables
│   │   │   └── seed.ts
│   │   ├── auth/
│   │   │   ├── jwt.ts
│   │   │   └── password.ts
│   │   ├── mq/
│   │   │   ├── connection.ts
│   │   │   ├── producer.ts
│   │   │   └── consumer.ts
│   │   └── services/
│   │       ├── event.service.ts
│   │       ├── seat.service.ts
│   │       ├── order.service.ts
│   │       ├── queue.service.ts
│   │       └── stats.service.ts
│   ├── components/
│   │   ├── ui/                       # Button, Input, Modal,...
│   │   ├── SeatGrid.svelte
│   │   ├── CountdownTimer.svelte
│   │   ├── Toast.svelte
│   │   └── Navbar.svelte
│   ├── stores/
│   │   └── toast.ts
│   └── utils/
		|   ├── api.ts              
│       ├── format.ts                 # formatCurrency, formatDate
│       └── constants.ts
├── hooks.server.ts                   # auth middleware
└── app.d.ts                          # type declarations

2. Quy tắc đặt tên

Files & Folders
────────────────────────────────────────
Svelte components     PascalCase.svelte     SeatGrid.svelte
TS modules            kebab-case.ts         event.service.ts
Route folders         kebab-case             checkout/[id]/

Variables & Functions
────────────────────────────────────────
Variables             camelCase              activeCount
Functions             camelCase              holdSeats()
Constants             UPPER_SNAKE            MAX_CONCURRENT_USERS
Types / Interfaces    PascalCase             SeatStatus

Database
────────────────────────────────────────
Tables                snake_case (số nhiều)  seat_sections
Columns               snake_case             locked_at
Enums                 lowercase              'available' | 'locked' | 'sold'

3. TypeScript

// ✅ Dùng type cho data shape
type Seat = {
  id: number;
  row: string;
  col: number;
  status: 'available' | 'locked' | 'sold';
};

// ✅ Dùng interface cho contract / extensible
interface ApiResponse<T> {
  data: T;
  message?: string;
}

// ❌ Không dùng any
// ❌ Không dùng @ts-ignore (trừ khi comment lý do)
// ✅ Bật strict mode trong tsconfig.json

4. API Response Format

// Thành công
return json({ data: event }, { status: 200 });
return json({ data: order }, { status: 201 });

// Lỗi
return json({ error: 'Ghế đã được người khác chọn' }, { status: 409 });
return json({ error: 'Unauthorized' }, { status: 401 });
return json({ error: 'Forbidden' }, { status: 403 });
return json({ error: 'Not found' }, { status: 404 });

// Validation error
return json({
  error: 'Validation failed',
  details: { email: 'Email đã tồn tại' }
}, { status: 400 });

5. API Handler Pattern

// src/routes/api/seats/hold/+server.ts
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { holdSeats } from '$lib/server/services/seat.service';

export const POST: RequestHandler = async ({ request, locals }) => {
  // 1. Auth check
  if (!locals.user) throw error(401, 'Unauthorized');

  // 2. Parse & validate input
  const body = await request.json();
  const { seat_ids } = body;
  if (!seat_ids?.length) throw error(400, 'seat_ids is required');

  // 3. Call service
  const result = await holdSeats(locals.user.id, seat_ids);

  // 4. Return response
  return json({ data: result }, { status: 200 });
};

6. Service Layer Pattern

// src/lib/server/services/seat.service.ts
import { db } from '$lib/server/db';
import { seats, orders, orderItems } from '$lib/server/db/schema';
import { eq, inArray, sql } from 'drizzle-orm';

export async function holdSeats(userId: number, seatIds: number[]) {
  return await db.transaction(async (tx) => {
    // Row lock
    const available = await tx
      .select()
      .from(seats)
      .where(inArray(seats.id, seatIds))
      .for('update');

    // Validate
    const allAvailable = available.every((s) => s.status === 'available');
    if (!allAvailable || available.length !== seatIds.length) {
      throw new Error('SEAT_CONFLICT');
    }

    // Update seats
    await tx
      .update(seats)
      .set({ status: 'locked', lockedBy: userId, lockedAt: new Date() })
      .where(inArray(seats.id, seatIds));

    // Create order
    // ...

    return { orderId, expiresAt };
  });
}

7. Svelte Components

<!-- ✅ Thứ tự trong file .svelte -->
<script lang="ts">
  // 1. Imports
  // 2. Props (export let)
  // 3. State ($state / let)
  // 4. Derived ($derived / $:)
  // 5. Functions
  // 6. Lifecycle (onMount, onDestroy)
</script>

<!-- HTML -->

<style>
  /* Scoped styles — ưu tiên dùng Tailwind class thay vì viết CSS ở đây */
</style>