Architecture Patterns
Light Stack Overview
Kiến trúc tổng quan
┌─────────────────────────────────────────────────────────────┐
│ LIGHT STACK ARCHITECTURE │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ CLIENTS │ │
│ │ Browser / Mobile App / API Consumer │ │
│ └───────────────────────┬─────────────────────────────┘ │
│ │ │
│ ┌───────────────────────▼─────────────────────────────┐ │
│ │ CLOUDFLARE EDGE │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Workers │ │ KV Cache │ │ R2 CDN │ │ │
│ │ │ (Next.js) │ │ │ │ │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │
│ │ │ │ │ │ │
│ │ ┌──────▼────────────────▼────────────────▼──────┐ │ │
│ │ │ Queue / Cron │ │ │
│ │ └──────────────────────┬────────────────────────┘ │ │
│ └─────────────────────────┼────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────▼────────────────────────────┐ │
│ │ SUPABASE │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │PostgreSQL│ │ Auth │ │ Storage │ │ │
│ │ │ + RLS │ │ │ │ │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Realtime │ │Edge Func │ │ pgmq │ │ │
│ │ │ │ │ │ │ pg_cron │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Pattern 1: Direct Supabase Access
Khi nào dùng?
✅ Simple CRUD operations
✅ Client-side with RLS protection
✅ Real-time subscriptions
✅ Quick prototyping
❌ Complex business logic
❌ External API calls
❌ Multi-step transactions
Implementation
// Client-side direct access
// RLS handles authorization
// app/tasks/page.tsx (Server Component)
import { createClient } from '@/lib/supabase/server';
export default async function TasksPage() {
const supabase = await createClient();
const { data: tasks } = await supabase
.from('tasks')
.select('*, project:projects(name)')
.order('created_at', { ascending: false });
return <TaskList tasks={tasks} />;
}
// Client component for mutations
'use client';
export function CreateTaskButton({ projectId }: { projectId: string }) {
const supabase = createClient();
const handleCreate = async () => {
const { error } = await supabase
.from('tasks')
.insert({ title: 'New Task', project_id: projectId });
if (error) toast.error(error.message);
};
return <button onClick={handleCreate}>Add Task</button>;
}
Pattern 2: Worker API Gateway
Khi nào dùng?
✅ External API integration (Stripe, SendGrid)
✅ Complex business logic
✅ Rate limiting / caching
✅ Multi-service orchestration
❌ Simple CRUD (overkill)
❌ Real-time features
Architecture
┌──────────────────────────────────────────────────────────┐
│ WORKER API GATEWAY │
├──────────────────────────────────────────────────────────┤
│ │
│ Client │
│ │ │
│ ▼ │
│ Cloudflare Worker (Hono) │
│ ├── Authentication (validate JWT) │
│ ├── Rate Limiting (KV counter) │
│ ├── Request Validation │
│ │ │
│ ├── Business Logic │
│ │ ├── Call Stripe API │
│ │ ├── Call Supabase (service role) │
│ │ └── Send Email │
│ │ │
│ └── Response │
│ │
└──────────────────────────────────────────────────────────┘
Implementation
// Worker as API gateway
import { Hono } from 'hono';
import { createClient } from '@supabase/supabase-js';
import Stripe from 'stripe';
const app = new Hono<{ Bindings: Env }>();
// Authentication middleware
app.use('/api/*', async (c, next) => {
const authHeader = c.req.header('Authorization');
if (!authHeader) {
return c.json({ error: 'Unauthorized' }, 401);
}
const supabase = createClient(
c.env.SUPABASE_URL,
c.env.SUPABASE_ANON_KEY
);
const { data: { user }, error } = await supabase.auth.getUser(
authHeader.replace('Bearer ', '')
);
if (error || !user) {
return c.json({ error: 'Invalid token' }, 401);
}
c.set('user', user);
await next();
});
// Payment endpoint
app.post('/api/payments', async (c) => {
const user = c.get('user');
const { amount, productId } = await c.req.json();
// 1. Validate product exists (Supabase)
const adminClient = createClient(
c.env.SUPABASE_URL,
c.env.SUPABASE_SERVICE_KEY
);
const { data: product } = await adminClient
.from('products')
.select('*')
.eq('id', productId)
.single();
if (!product) {
return c.json({ error: 'Product not found' }, 404);
}
// 2. Create Stripe payment
const stripe = new Stripe(c.env.STRIPE_SECRET_KEY);
const paymentIntent = await stripe.paymentIntents.create({
amount,
currency: 'usd',
metadata: { userId: user.id, productId },
});
// 3. Record transaction
await adminClient.from('transactions').insert({
user_id: user.id,
product_id: productId,
stripe_payment_id: paymentIntent.id,
status: 'pending',
});
return c.json({ clientSecret: paymentIntent.client_secret });
});
export default app;
Pattern 3: Queue-based Processing
Khi nào dùng?
✅ Long-running tasks
✅ Batch processing
✅ Email/notification sending
✅ Retry-able operations
❌ Synchronous responses needed
❌ Simple operations
Architecture
┌──────────────────────────────────────────────────────────┐
│ QUEUE-BASED PROCESSING │
├──────────────────────────────────────────────────────────┤
│ │
│ Client │
│ │ │
│ ▼ │
│ API (Worker or Supabase Edge Function) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ QUEUE │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Message │ │ Message │ │ Message │ ... │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Consumer (Worker or Edge Function) │
│ ├── Process message │
│ ├── Update database │
│ ├── Send notification │
│ └── Acknowledge / Retry │
│ │
└──────────────────────────────────────────────────────────┘
Chọn Queue nào?
| Scenario |
Cloudflare Queue |
Supabase pgmq |
| High throughput |
✅ |
Limited by DB |
| Transaction with data |
Complex |
✅ Native |
| Edge processing |
✅ Native |
Needs Edge Func |
| Simple setup |
Moderate |
✅ Easy |
Pattern 4: Event-Driven Architecture
Khi nào dùng?
✅ Loose coupling between services
✅ Multiple consumers for same event
✅ Audit trail / logging
✅ Realtime updates
❌ Simple request-response
❌ Tight latency requirements
Architecture
┌──────────────────────────────────────────────────────────┐
│ EVENT-DRIVEN ARCHITECTURE │
├──────────────────────────────────────────────────────────┤
│ │
│ Database Trigger │
│ │ │
│ ▼ │
│ Event │
│ │ │
│ ├──▶ Supabase Realtime ──▶ Client UI Update │
│ │ │
│ ├──▶ pgmq ──▶ Email Worker ──▶ Send Email │
│ │ │
│ ├──▶ pg_notify ──▶ Edge Function ──▶ External API │
│ │ │
│ └──▶ Audit Table ──▶ Analytics │
│ │
└──────────────────────────────────────────────────────────┘
Implementation
-- Trigger when task is completed
CREATE OR REPLACE FUNCTION on_task_completed()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.status = 'completed' AND OLD.status != 'completed' THEN
-- 1. Enqueue notification
PERFORM pgmq.send('notifications', jsonb_build_object(
'type', 'task_completed',
'task_id', NEW.id,
'user_id', NEW.user_id
));
-- 2. Insert audit log
INSERT INTO audit_log (entity, entity_id, action, changed_by)
VALUES ('task', NEW.id, 'completed', auth.uid());
-- 3. Notify for external integration
PERFORM pg_notify('task_events', json_build_object(
'event', 'completed',
'task_id', NEW.id
)::text);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER task_completed_trigger
AFTER UPDATE ON tasks
FOR EACH ROW
EXECUTE FUNCTION on_task_completed();
Tổng kết
Chọn Pattern phù hợp
| Pattern |
Use Case |
Complexity |
| Direct Supabase |
Simple CRUD, prototyping |
Low |
| Worker Gateway |
External APIs, complex logic |
Medium |
| Queue-based |
Async processing, retries |
Medium |
| Event-driven |
Loose coupling, multi-consumer |
High |
Combine Patterns
Thực tế: Combine nhiều patterns
Example: E-commerce checkout
├── Direct Supabase: Cart management
├── Worker Gateway: Payment processing
├── Queue: Order fulfillment
└── Event-driven: Notifications
Best Practices
- Start simple - Direct Supabase, add complexity when needed
- RLS first - Let database handle authorization
- Queue for async - Don't block user requests
- Monitor everything - Logs, metrics, alerts
Q&A
- Pattern nào phù hợp với use case của bạn?
- Có experience với event-driven architecture?
- Challenges khi integrate nhiều services?