Migration Cơ sở dữ liệu
BANA sử dụng hai hệ thống migration bổ sung cho nhau:
- Drizzle Kit CLI -- Tạo và áp dụng SQL migration DDL từ định nghĩa schema (quản lý trong
@nx/core) - Framework Seed Migration -- Chạy các quy trình dữ liệu mẫu thông qua
bootstrapMigration()(sử dụng bởi mỗi gói dịch vụ)
Nguồn: packages/core/src/migrations/
Ngăn xếp Công nghệ
| Công cụ | Phiên bản | Mục đích |
|---|---|---|
| Drizzle ORM | ^0.45.1 | Trình tạo truy vấn an toàn kiểu và định nghĩa schema |
| Drizzle Kit | ^0.31.8 | Công cụ CLI migration (tạo SQL, áp dụng vào cơ sở dữ liệu) |
| PostgreSQL | Mới nhất | Công cụ cơ sở dữ liệu |
| Bun | >=1.3.8 | Runtime cho script CLI |
Cấu trúc Thư mục
packages/core/
├── src/
│ ├── models/
│ │ └── schemas/
│ │ ├── common/ # Shared pgSchema objects + column generators (no tables)
│ │ ├── public/ # Public schema entities (25)
│ │ ├── helpdesk/ # Helpdesk schema entities (30)
│ │ ├── inventory/ # Inventory schema entities (17)
│ │ ├── sale/ # Sale schema entities (16)
│ │ ├── invoice/ # Invoice schema entities (9)
│ │ ├── pricing/ # Pricing schema entities (9)
│ │ ├── identity/ # Identity schema entities (8)
│ │ ├── ledger/ # Ledger schema entities (8)
│ │ ├── finance/ # Finance schema entities (6)
│ │ ├── tax/ # Tax schema entities (6)
│ │ ├── licensing/ # Licensing schema entities (5)
│ │ ├── allocation/ # Allocation schema entities (4)
│ │ ├── outreach/ # Outreach schema entities (2)
│ │ └── payment/ # Payment schema entities (0, reserved)
│ ├── migrations/
│ │ ├── migrators/
│ │ │ ├── cli.ts # Migration CLI entry point (handles --all flag)
│ │ │ ├── allocation.ts # Config: allocation schema only
│ │ │ ├── finance.ts # Config: finance schema only
│ │ │ ├── helpdesk.ts # Config: helpdesk schema only
│ │ │ ├── identity.ts # Config: identity schema only
│ │ │ ├── inventory.ts # Config: inventory schema only
│ │ │ ├── invoice.ts # Config: invoice schema only
│ │ │ ├── ledger.ts # Config: ledger schema only
│ │ │ ├── licensing.ts # Config: licensing schema only
│ │ │ ├── outreach.ts # Config: outreach schema only
│ │ │ ├── payment.ts # Config: payment schema only
│ │ │ ├── pricing.ts # Config: pricing schema only
│ │ │ ├── public.ts # Config: public schema only
│ │ │ ├── sale.ts # Config: sale schema only
│ │ │ └── tax.ts # Config: tax schema only
│ │ └── drizzle/ # Generated migration output
│ │ ├── allocation/ # Allocation schema SQL + meta/
│ │ ├── finance/ # Finance schema SQL + meta/
│ │ ├── helpdesk/ # Helpdesk schema SQL + meta/
│ │ ├── identity/ # Identity schema SQL + meta/
│ │ ├── inventory/ # Inventory schema SQL + meta/
│ │ ├── invoice/ # Invoice schema SQL + meta/
│ │ ├── ledger/ # Ledger schema SQL + meta/
│ │ ├── licensing/ # Licensing schema SQL + meta/
│ │ ├── outreach/ # Outreach schema SQL + meta/
│ │ ├── payment/ # Payment schema SQL + meta/
│ │ ├── pricing/ # Pricing schema SQL + meta/
│ │ ├── public/ # Public schema SQL + meta/
│ │ ├── sale/ # Sale schema SQL + meta/
│ │ └── tax/ # Tax schema SQL + meta/
│ └── helpers/
│ ├── bootstraps/
│ │ └── migration.ts # bootstrapMigration() helper
│ └── migration/
│ ├── helper.ts # MigrationHelper class
│ ├── process-loader.ts # createMigrationProcessLoader()
│ └── types.ts # TMigrationProcess, TMigrationHooks
└── package.json # db:generate, db:migrate scriptsDrizzle Kit CLI
Tổng quan
Công cụ CLI tại src/migrations/migrators/cli.ts bọc Drizzle Kit để hỗ trợ quản lý migration theo từng schema. Nó yêu cầu RUN_MODE=migrate như một biện pháp an toàn.
Sử dụng
bun run src/migrations/migrators/cli.ts <command> [schema] [options]Các Lệnh
| Lệnh | Mô tả |
|---|---|
generate | Tạo file SQL migration bằng cách so sánh định nghĩa schema với cơ sở dữ liệu |
migrate | Áp dụng các migration đang chờ vào cơ sở dữ liệu |
Tham số
| Tham số | Mô tả |
|---|---|
schema | Tên schema đích: allocation, finance, helpdesk, identity, inventory, invoice, ledger, licensing, outreach, payment, pricing, public, sale, tax |
--all | Chạy lệnh cho tất cả 14 schema tuần tự |
Biện pháp An toàn: RUN_MODE
CLI yêu cầu biến môi trường RUN_MODE được đặt là migrate. Điều này ngăn việc thực thi migration vô tình:
# Without RUN_MODE -- will fail
bun run src/migrations/migrators/cli.ts generate public
# Error: RUN_MODE environment variable must be set to "migrate"
# With RUN_MODE -- will succeed
RUN_MODE=migrate bun run src/migrations/migrators/cli.ts generate publicVí dụ
# Generate migration SQL for a single schema
RUN_MODE=migrate bun run src/migrations/migrators/cli.ts generate public
RUN_MODE=migrate bun run src/migrations/migrators/cli.ts generate sale
# Apply migrations for a single schema
RUN_MODE=migrate bun run src/migrations/migrators/cli.ts migrate inventory
RUN_MODE=migrate bun run src/migrations/migrators/cli.ts migrate finance
# Generate migrations for ALL schemas
RUN_MODE=migrate bun run src/migrations/migrators/cli.ts generate --all
# Apply migrations for ALL schemas
RUN_MODE=migrate bun run src/migrations/migrators/cli.ts migrate --allScript Package.json
Gói @nx/core cung cấp các script viết tắt với RUN_MODE=migrate được cấu hình sẵn:
{
"db:generate": "RUN_MODE=migrate bun run src/migrations/migrators/cli.ts generate",
"db:migrate": "RUN_MODE=migrate bun run src/migrations/migrators/cli.ts migrate",
"db:generate:all": "RUN_MODE=migrate bun run src/migrations/migrators/cli.ts generate --all",
"db:migrate:all": "RUN_MODE=migrate bun run src/migrations/migrators/cli.ts migrate --all"
}Sử dụng với bun run:
cd packages/core
# Single schema
bun run db:generate public
bun run db:migrate sale
# All schemas
bun run db:generate:all
bun run db:migrate:allCấu hình Theo Schema
Mỗi schema có file cấu hình Drizzle Kit riêng trong src/migrations/migrators/. Các cấu hình tuân theo một mẫu nhất quán:
Mẫu Cấu hình
// src/migrations/migrators/{schema}.ts
import 'dotenv-flow/config';
import { applicationEnvironment, int, LoggerFactory } from '@venizia/ignis';
import { defineConfig } from 'drizzle-kit';
import { EnvironmentKeys, PostgresSchemas } from './../../common';
const migrate = () => {
const databaseConfigs = {
host: applicationEnvironment.get<string>(EnvironmentKeys.APP_ENV_POSTGRES_HOST),
port: int(applicationEnvironment.get<number>(EnvironmentKeys.APP_ENV_POSTGRES_PORT)),
database: applicationEnvironment.get<string>(EnvironmentKeys.APP_ENV_POSTGRES_DATABASE),
user: applicationEnvironment.get<string>(EnvironmentKeys.APP_ENV_POSTGRES_USERNAME),
password: applicationEnvironment.get<string>(EnvironmentKeys.APP_ENV_POSTGRES_PASSWORD),
ssl: false,
};
return defineConfig({
dialect: 'postgresql',
out: './src/migrations/drizzle/{schema}', // Output directory per schema
schema: ['./src/models/schemas/{schema}/**/schema.ts',
'./src/models/schemas/{schema}/common.ts'], // Schema source files
dbCredentials: databaseConfigs,
schemaFilter: [PostgresSchemas.{SCHEMA}], // Filter to this schema only
});
};
export default migrate();Tóm tắt Cấu hình
| File Cấu hình | Bộ lọc Schema | Thư mục Đầu ra | Nguồn Schema |
|---|---|---|---|
allocation.ts | allocation | drizzle/allocation/ | schemas/allocation/**/schema.ts |
finance.ts | finance | drizzle/finance/ | schemas/finance/**/schema.ts |
helpdesk.ts | helpdesk | drizzle/helpdesk/ | schemas/helpdesk/**/schema.ts |
identity.ts | identity | drizzle/identity/ | schemas/identity/**/schema.ts |
inventory.ts | inventory | drizzle/inventory/ | schemas/inventory/**/schema.ts |
invoice.ts | invoice | drizzle/invoice/ | schemas/invoice/**/schema.ts |
ledger.ts | ledger | drizzle/ledger/ | schemas/ledger/**/schema.ts |
licensing.ts | licensing | drizzle/licensing/ | schemas/licensing/**/schema.ts |
outreach.ts | outreach | drizzle/outreach/ | schemas/outreach/**/schema.ts |
payment.ts | payment | drizzle/payment/ | schemas/payment/**/schema.ts |
pricing.ts | pricing | drizzle/pricing/ | schemas/pricing/**/schema.ts |
public.ts | public | drizzle/public/ | schemas/public/**/schema.ts |
sale.ts | sale | drizzle/sale/ | schemas/sale/**/schema.ts |
tax.ts | tax | drizzle/tax/ | schemas/tax/**/schema.ts |
Lưu ý: payment.ts bổ sung glob schema đã biên dịch của MQ-Pay từ third-parties/mq-pay/dist/... ngoài nguồn schemas/payment/.
Cờ --all
Cờ --all chạy lệnh lần lượt cho từng schema. cli.ts lấy toàn bộ tập từ PostgresSchemas.SCHEME_SET (tất cả 14 schema) và gọi cấu hình riêng của mỗi schema, ghi vào thư mục drizzle/{schema}/ tương ứng.
Quy trình Migration
Kết quả Migration
Mỗi thư mục schema chứa các file SQL đã tạo và journal để theo dõi:
src/migrations/drizzle/{schema}/
├── 0000_initial_migration.sql
├── 0001_add_new_column.sql
├── 0002_create_table.sql
└── meta/
└── _journal.jsonFile Journal
File meta/_journal.json theo dõi lịch sử migration:
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1705708800000,
"tag": "0000_initial_migration",
"breakpoints": true
}
]
}Biến Môi trường
Tất cả cấu hình migrator đọc thông tin xác thực cơ sở dữ liệu từ môi trường thông qua dotenv-flow:
| Biến | Bắt buộc | Mô tả |
|---|---|---|
RUN_MODE | Có | Phải là migrate (biện pháp an toàn) |
APP_ENV_POSTGRES_HOST | Có | Host PostgreSQL |
APP_ENV_POSTGRES_PORT | Có | Cổng PostgreSQL |
APP_ENV_POSTGRES_DATABASE | Có | Tên cơ sở dữ liệu |
APP_ENV_POSTGRES_USERNAME | Có | Tên người dùng cơ sở dữ liệu |
APP_ENV_POSTGRES_PASSWORD | Có | Mật khẩu cơ sở dữ liệu |
Các file môi trường được tải thông qua dotenv-flow/config ở đầu mỗi file cấu hình.
Seed Data Migration
Ngoài các thay đổi DDL schema, mỗi gói dịch vụ có thể định nghĩa quy trình dữ liệu mẫu để chèn hoặc cập nhật các bản ghi ban đầu (vai trò, cấu hình, danh mục mặc định, v.v.). Điều này được quản lý bởi framework MigrationHelper trong @nx/core.
bootstrapMigration()
Helper bootstrapMigration() khởi động ứng dụng, đăng ký MigrationRepository, và chạy tất cả quy trình seed:
// packages/{package}/src/migrate.ts
import { bootstrapMigration } from '@nx/core';
import { Application } from './application';
import { getMigrationProcesses } from './migrations/processes/migration-process';
bootstrapMigration({
ApplicationClass: Application,
getMigrationProcesses,
})
.then(() => {
process.exit(0);
})
.catch(err => {
console.error('Cannot migrate database schema', err);
process.exit(1);
});createMigrationProcessLoader()
Định nghĩa các quy trình seed cần tải thông qua import động:
// packages/{package}/src/migrations/processes/migration-process.ts
import { createMigrationProcessLoader } from '@nx/core';
export const getMigrationProcesses = createMigrationProcessLoader({
seedPaths: [
'identity-0001-seed-roles',
'identity-0002-seed-users',
'identity-0003-seed-table-configurations',
'identity-0004-seed-mail-configurations',
],
importFn: path => import(`../processes/${path}.js`),
});TMigrationProcess
Mỗi quy trình seed triển khai kiểu TMigrationProcess:
type TMigrationProcess = {
name: string; // Unique process name
migrateFn: (opts: { context: BaseApplication }) => ValueOrPromise<void>; // Migration logic
cleanFn?: (opts: { context: BaseApplication }) => ValueOrPromise<void>; // Optional cleanup
options?: {
alwaysRun?: boolean; // If true, runs even if previously completed
};
};Ví dụ Quy trình Seed
// packages/identity/src/migrations/processes/identity-0001-seed-roles.ts
import { INameI18n, TMigrationProcess } from '@nx/core';
import { RoleRepository } from '@/repositories';
const defaultRoles: Array<{ identifier: string; name: INameI18n; priority: number }> = [
{ identifier: '999_super-admin', name: { en: 'Super Admin', vi: 'Sieu Quan Tri Vien' }, priority: 999 },
{ identifier: '900_admin', name: { en: 'Admin', vi: 'Quan Tri Vien' }, priority: 900 },
// ...
];
const migrationProcess: TMigrationProcess = {
name: __filename.slice(__dirname.length + 1),
migrateFn: async ({ context: application }) => {
const roleRepository = application.get<RoleRepository>({
key: 'repositories.RoleRepository',
});
for (const role of defaultRoles) {
const existing = await roleRepository.findOne({
filter: { where: { identifier: role.identifier } },
});
if (!existing) {
await roleRepository.create({ data: role });
} else {
await roleRepository.updateById({ id: existing.id, data: role });
}
}
},
};
export default migrationProcess;MigrationHelper
Lớp MigrationHelper quản lý toàn bộ quy trình seed migration:
- Giai đoạn dọn dẹp -- Chạy
cleanFncho mỗi quy trình (tùy chọn) - Giai đoạn migration -- Chạy
migrateFncho mỗi quy trình, theo dõi trạng thái trong bảngMigration
Các hành vi chính:
| Hành vi | Mô tả |
|---|---|
| Thực thi idempotent | Mỗi quy trình được ghi nhận trong bảng public.Migration. Các quy trình đã hoàn thành sẽ bị bỏ qua |
Ghi đè alwaysRun | Các quy trình có options.alwaysRun = true chạy mỗi lần, ngay cả khi đã hoàn thành trước đó |
| Theo dõi trạng thái | Mỗi quy trình được ghi nhận là SUCCESS hoặc FAIL trong bảng Migration |
| Hook vòng đời | Hỗ trợ beforeCleanup, afterCleanup, beforeMigration, afterMigration, onMigrationSuccess, onMigrationError hook |
| Thực thi tuần tự | Các quy trình chạy theo thứ tự (vị trí mảng quyết định thứ tự thực thi) |
Các Gói Sử dụng Seed Migration
| Gói | Quy trình Seed |
|---|---|
@nx/identity | identity-0001-seed-roles, identity-0002-seed-users, identity-0003-seed-table-configurations, identity-0004-seed-mail-configurations, identity-0005-seed-permissions, identity-0006-seed-sms-configurations |
@nx/commerce | commerce-0001-seed-tax-types, commerce-0002-seed-permissions, commerce-0003-seed-typesense-embedding-configs |
@nx/finance | finance-0001-seed-categories, finance-0002-seed-permissions |
@nx/inventory | inventory-0001-seed-inventory-tracking-types, inventory-0002-seed-permissions |
@nx/payment | payment-0001-seed-vnpay-qr-mms-configuration, payment-0002-seed-vnpay-phone-pos-configuration, payment-0003-seed-permissions |
@nx/pricing | pricing-0001-seed-permissions |
@nx/sale | sale-0001-seed-permissions |
@nx/signal | signal-0001-seed-permissions |
@nx/ledger | ledger-0001-seed-permissions |
@nx/outreach | (chưa có seed) |
@nx/search | (chưa có seed) |
Chạy Seed Migration
Mỗi gói cung cấp script migrate:dev:
cd packages/identity
bun run migrate:dev # Runs with .env.developmentLệnh này thực thi src/migrate.ts gọi bootstrapMigration().
Quy trình Migration Hoàn chỉnh
Khi thiết lập môi trường mới hoặc thêm thay đổi schema, thực hiện theo trình tự sau:
Hướng dẫn Từng bước
- Sửa đổi định nghĩa schema trong
packages/core/src/models/schemas/{schema}/ - Tạo SQL migration từ
packages/core/:bashbun run db:generate public # or: bun run db:generate:all - Xem lại SQL đã tạo trong
src/migrations/drizzle/{schema}/ - Áp dụng DDL migration vào cơ sở dữ liệu:bash
bun run db:migrate public # or: bun run db:migrate:all - Chạy seed migration cho mỗi gói dịch vụ cần dữ liệu:bash
cd packages/identity && bun run migrate:dev cd packages/payment && bun run migrate:dev - Commit file schema, SQL đã tạo, và quy trình seed cùng nhau
Khắc phục Sự cố
Lỗi RUN_MODE
[cli] Error: RUN_MODE environment variable must be set to "migrate"CLI yêu cầu RUN_MODE=migrate. Sử dụng script package.json (bun run db:generate, bun run db:migrate) đã tự động thiết lập sẵn.
Schema Không tìm thấy
Error: Unknown schema "order"
Available schemas: allocation, finance, helpdesk, identity, inventory, invoice, ledger, licensing, outreach, payment, pricing, public, sale, taxĐảm bảo bạn sử dụng tên schema hợp lệ từ PostgresSchemas.SCHEME_SET. Schema order đã được đổi tên thành sale.
Migration Không phát hiện Thay đổi
- File nguồn schema là TypeScript -- đảm bảo mẫu glob schema khớp với vị trí file của bạn
- Cờ
--alllặp qua từng cấu hình theo schema (đọc trực tiếp.ts); không cần build đặc biệt - Xác minh
schemaFilterđúng được đặt trong cấu hình
Lỗi Kết nối
Kiểm tra biến môi trường được đặt chính xác. Các cấu hình tải từ dotenv-flow, đọc .env.development (hoặc .env.local):
# Verify environment
cat .env.development | grep APP_ENV_POSTGRESTài liệu Liên quan
- Tổng quan Cơ sở dữ liệu -- Tóm tắt schema, cột chung, kiểu chia sẻ
- Public Schema -- 26 thực thể trong public schema
- Pricing Schema -- 9 thực thể định giá
- Allocation Schema -- 4 thực thể phân bổ
- Sale Schema -- 21 thực thể bán hàng
- Inventory Schema -- 17 thực thể tồn kho
- Finance Schema -- 6 thực thể tài chính
- Payment Schema -- 0 thực thể (namespace dự trữ)
- Outreach Schema -- 2 thực thể outreach
- Sơ đồ Quan hệ Thực thể -- ERD toàn diện cho tất cả các model