Cấp độ: intermediate40 phútminMiễn phíFreeBuilder & EntrepreneurBuilder & Entrepreneur

SaaS Architecture cơ bảnBasic SaaS architecture

Bản thiết kế kiến trúc SaaS nhỏA small SaaS architecture blueprint

Bạn sẽ học gìWhat you will learn

  • Giải thích multi-tenancyExplain multi-tenancy
  • Thiết kế user/subscription schemaDesign user/subscription schema
  • Hiểu feature flag patternUnderstand feature flag patterns

Bạn sẽ tạo gìWhat you will build

Bản thiết kế kiến trúc SaaS nhỏA small SaaS architecture blueprint

Nội dung bài họcLesson content

SaaS là gì?What is SaaS?

SaaS (Software as a Service) là mô hình phần mềm chạy trên cloud, user đăng ký thuê bao hàng tháng/năm. Ví dụ: Notion, Figma, Slack, Google Workspace. Đặc điểm: multi-tenant (nhiều khách dùng chung 1 instance), subscription billing, truy cập qua trình duyệt.SaaS (Software as a Service) is a software model running on cloud, where users subscribe monthly/yearly. Examples: Notion, Figma, Slack, Google Workspace. Characteristics: multi-tenant (many customers share 1 instance), subscription billing, browser access.

Multi-tenancy: nhiều tenant chung hệ thốngMulti-tenancy: many tenants sharing the system

Tenant = 1 tổ chức/khách hàng. Multi-tenancy là nhiều tenant dùng chung 1 ứng dụng nhưng dữ liệu cô lập. 3 mô hình: (1) Database per tenant — cô lập tốt, đắt. (2) Schema per tenant — cân bằng. (3) Shared database với tenant_id — rẻ, phổ biến nhất cho SaaS nhỏ.A tenant = 1 organization/customer. Multi-tenancy is many tenants sharing 1 application but with isolated data. 3 models: (1) Database per tenant — best isolation, expensive. (2) Schema per tenant — balanced. (3) Shared database with tenant_id — cheap, most common for small SaaS.

sql
                      
                        -- Shared database với tenant_id (phổ biến nhất)
CREATE TABLE tenants (
  id UUID PRIMARY KEY,
  name TEXT NOT NULL,
  plan TEXT DEFAULT 'free'
);

CREATE TABLE users (
  id UUID PRIMARY KEY,
  tenant_id UUID REFERENCES tenants(id),
  email TEXT UNIQUE NOT NULL,
  role TEXT DEFAULT 'member'
);

CREATE TABLE documents (
  id UUID PRIMARY KEY,
  tenant_id UUID REFERENCES tenants(id),  -- Cô lập dữ liệu
  title TEXT,
  content TEXT
);

-- Mọi query PHẢI lọc theo tenant_id
SELECT * FROM documents WHERE tenant_id = ?;
                      
                    

Subscription billingSubscription billing

SaaS tính phí theo plan (gói): Free, Pro, Business, Enterprise. Mỗi plan có giới hạn (số user, số project, features). Billing cycle: monthly hoặc yearly. Cần: bảng subscriptions (tenant_id, plan, status, current_period_end), tích hợp Stripe/Paddle cho thanh toán, webhook xử lý renewal/cancel.SaaS charges by plan: Free, Pro, Business, Enterprise. Each plan has limits (users, projects, features). Billing cycle: monthly or yearly. Need: a subscriptions table (tenant_id, plan, status, current_period_end), integrate Stripe/Paddle for payments, webhooks for renewal/cancel.

javascript
                      
                        CREATE TABLE subscriptions (
  id UUID PRIMARY KEY,
  tenant_id UUID REFERENCES tenants(id),
  plan TEXT NOT NULL,           -- free | pro | business
  status TEXT NOT NULL,         -- active | canceled | past_due
  stripe_customer_id TEXT,
  stripe_subscription_id TEXT,
  current_period_end TIMESTAMP,
  created_at TIMESTAMP DEFAULT NOW()
);

-- Kiểm tra quyền truy cập feature
function canAccessFeature(tenant, feature) {
  const planFeatures = {
    free: ['basic_docs', '5_users'],
    pro: ['basic_docs', 'unlimited_users', 'ai_assist'],
    business: ['all_pro', 'sso', 'audit_log']
  };
  return planFeatures[tenant.plan]?.includes(feature);
}
                      
                    

Feature flags: bật/tắt tính năngFeature flags: toggling features

Feature flag là công tắc bật/tắt tính năng không cần deploy lại. Lý do: (1) Rollout dần — bật cho 10% user trước. (2) A/B test — so sánh 2 phiên bản. (3) Kill switch — tắt nhanh nếu lỗi. Lưu trong database hoặc service như LaunchDarkly.A feature flag is a switch to toggle features without redeploying. Reasons: (1) Gradual rollout — enable for 10% of users first. (2) A/B testing — compare 2 versions. (3) Kill switch — quickly disable if buggy. Store in database or a service like LaunchDarkly.

javascript
                      
                        CREATE TABLE feature_flags (
  id UUID PRIMARY KEY,
  tenant_id UUID,        -- NULL = global, UUID = tenant-specific
  feature TEXT NOT NULL,
  enabled BOOLEAN DEFAULT false,
  rollout_percentage INTEGER DEFAULT 0  -- 0-100
);

-- Kiểm tra feature flag
async function isFeatureEnabled(tenantId, feature) {
  const flag = await db.query(
    'SELECT * FROM feature_flags WHERE feature = ? AND (tenant_id = ? OR tenant_id IS NULL) ORDER BY tenant_id DESC LIMIT 1',
    [feature, tenantId]
  );
  if (!flag) return false;
  if (!flag.enabled) return false;
  if (flag.rollout_percentage >= 100) return true;
  const hash = hashCode(tenantId) % 100;
  return hash < flag.rollout_percentage;
}
                      
                    

Bài tập thực hànhHands-on exercises

1Thiết kế schema cho SaaS quản lý project: tenants, users (thuộc tenant), projects (thuộc tenant), tasks (thuộc project). Mọi bảng có tenant_id để cô lập.Design schema for a project management SaaS: tenants, users (belonging to tenant), projects (belonging to tenant), tasks (belonging to project). All tables have tenant_id for isolation.

Gợi ýHint

Mỗi bảng cần tenant_id (trừ tenants). Tasks cần thêm project_id. Mọi query lọc theo tenant_id.Each table needs tenant_id (except tenants). Tasks also need project_id. All queries filter by tenant_id.

Lời giảiSolution
sql
                      
                        CREATE TABLE tenants (
  id UUID PRIMARY KEY,
  name TEXT NOT NULL,
  plan TEXT DEFAULT 'free'
);

CREATE TABLE users (
  id UUID PRIMARY KEY,
  tenant_id UUID REFERENCES tenants(id),
  email TEXT UNIQUE NOT NULL
);

CREATE TABLE projects (
  id UUID PRIMARY KEY,
  tenant_id UUID REFERENCES tenants(id),
  name TEXT NOT NULL
);

CREATE TABLE tasks (
  id UUID PRIMARY KEY,
  tenant_id UUID REFERENCES tenants(id),
  project_id UUID REFERENCES projects(id),
  title TEXT NOT NULL,
  done BOOLEAN DEFAULT false
);
                      
                    

2Viết hàm canAccessPlan(tenant, requiredPlan) kiểm tra tenant có plan đủ cao không (free < pro < business).Write a function canAccessPlan(tenant, requiredPlan) checking if the tenant's plan is high enough (free < pro < business).

Gợi ýHint

Dùng object map level: { free: 0, pro: 1, business: 2 }. So sánh level.Use an object map level: { free: 0, pro: 1, business: 2 }. Compare levels.

Lời giảiSolution
javascript
                      
                        const PLAN_LEVELS = { free: 0, pro: 1, business: 2 };

function canAccessPlan(tenant, requiredPlan) {
  const tenantLevel = PLAN_LEVELS[tenant.plan] ?? 0;
  const requiredLevel = PLAN_LEVELS[requiredPlan] ?? 99;
  return tenantLevel >= requiredLevel;
}

// Test
canAccessPlan({ plan: 'pro' }, 'free');     // true
canAccessPlan({ plan: 'free' }, 'pro');      // false
canAccessPlan({ plan: 'business' }, 'pro');  // true
                      
                    

Kiểm traQuiz

Chọn đáp án đúng cho mỗi câu. Nhấn để xem giải thích.Choose the correct answer for each question. Click to see explanation.

Câu 1: SaaS là gì?Q1: What is SaaS?

Câu 2: Multi-tenancy là gì?Q2: What is multi-tenancy?

Câu 3: Feature flag dùng để làm gì?Q3: What are feature flags used for?

Câu 4: Trong shared database multi-tenancy, mọi query cần làm gì?Q4: In shared database multi-tenancy, what must every query do?