Skip to content

Migration Cơ sở dữ liệu

BANA sử dụng hai hệ thống migration bổ sung cho nhau:

  1. Drizzle Kit CLI -- Tạo và áp dụng SQL migration DDL từ định nghĩa schema (quản lý trong @nx/core)
  2. 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ảnMục đích
Drizzle ORM^0.45.1Trình tạo truy vấn an toàn kiểu và định nghĩa schema
Drizzle Kit^0.31.8Công cụ CLI migration (tạo SQL, áp dụng vào cơ sở dữ liệu)
PostgreSQLMới nhấtCông cụ cơ sở dữ liệu
Bun>=1.3.8Runtime 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 scripts

Drizzle 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

bash
bun run src/migrations/migrators/cli.ts <command> [schema] [options]

Các Lệnh

LệnhMô tả
generateTạ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ả
schemaTên schema đích: allocation, finance, helpdesk, identity, inventory, invoice, ledger, licensing, outreach, payment, pricing, public, sale, tax
--allChạ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:

bash
# 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 public

Ví dụ

bash
# 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 --all

Script 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:

json
{
  "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:

bash
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:all

Cấ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

typescript
// 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ìnhBộ lọc SchemaThư mục Đầu raNguồn Schema
allocation.tsallocationdrizzle/allocation/schemas/allocation/**/schema.ts
finance.tsfinancedrizzle/finance/schemas/finance/**/schema.ts
helpdesk.tshelpdeskdrizzle/helpdesk/schemas/helpdesk/**/schema.ts
identity.tsidentitydrizzle/identity/schemas/identity/**/schema.ts
inventory.tsinventorydrizzle/inventory/schemas/inventory/**/schema.ts
invoice.tsinvoicedrizzle/invoice/schemas/invoice/**/schema.ts
ledger.tsledgerdrizzle/ledger/schemas/ledger/**/schema.ts
licensing.tslicensingdrizzle/licensing/schemas/licensing/**/schema.ts
outreach.tsoutreachdrizzle/outreach/schemas/outreach/**/schema.ts
payment.tspaymentdrizzle/payment/schemas/payment/**/schema.ts
pricing.tspricingdrizzle/pricing/schemas/pricing/**/schema.ts
public.tspublicdrizzle/public/schemas/public/**/schema.ts
sale.tssaledrizzle/sale/schemas/sale/**/schema.ts
tax.tstaxdrizzle/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.json

File Journal

File meta/_journal.json theo dõi lịch sử migration:

json
{
  "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ếnBắt buộcMô tả
RUN_MODEPhải là migrate (biện pháp an toàn)
APP_ENV_POSTGRES_HOSTHost PostgreSQL
APP_ENV_POSTGRES_PORTCổng PostgreSQL
APP_ENV_POSTGRES_DATABASETên cơ sở dữ liệu
APP_ENV_POSTGRES_USERNAMETên người dùng cơ sở dữ liệu
APP_ENV_POSTGRES_PASSWORDMậ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:

typescript
// 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:

typescript
// 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:

typescript
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

typescript
// 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:

  1. Giai đoạn dọn dẹp -- Chạy cleanFn cho mỗi quy trình (tùy chọn)
  2. Giai đoạn migration -- Chạy migrateFn cho mỗi quy trình, theo dõi trạng thái trong bảng Migration

Các hành vi chính:

Hành viMô tả
Thực thi idempotentMỗ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 đè alwaysRunCá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áiMỗi quy trình được ghi nhận là SUCCESS hoặc FAIL trong bảng Migration
Hook vòng đờiHỗ 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óiQuy trình Seed
@nx/identityidentity-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/commercecommerce-0001-seed-tax-types, commerce-0002-seed-permissions, commerce-0003-seed-typesense-embedding-configs
@nx/financefinance-0001-seed-categories, finance-0002-seed-permissions
@nx/inventoryinventory-0001-seed-inventory-tracking-types, inventory-0002-seed-permissions
@nx/paymentpayment-0001-seed-vnpay-qr-mms-configuration, payment-0002-seed-vnpay-phone-pos-configuration, payment-0003-seed-permissions
@nx/pricingpricing-0001-seed-permissions
@nx/salesale-0001-seed-permissions
@nx/signalsignal-0001-seed-permissions
@nx/ledgerledger-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:

bash
cd packages/identity
bun run migrate:dev    # Runs with .env.development

Lệ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

  1. Sửa đổi định nghĩa schema trong packages/core/src/models/schemas/{schema}/
  2. Tạo SQL migration từ packages/core/:
    bash
    bun run db:generate public     # or: bun run db:generate:all
  3. Xem lại SQL đã tạo trong src/migrations/drizzle/{schema}/
  4. Áp dụng DDL migration vào cơ sở dữ liệu:
    bash
    bun run db:migrate public      # or: bun run db:migrate:all
  5. 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
  6. 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ờ --all lặ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):

bash
# Verify environment
cat .env.development | grep APP_ENV_POSTGRES

Tài liệu Liên quan

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.