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.ts               # Shared pgSchema objects, column generators
│   │       ├── public/                  # Public schema models (30)
│   │       ├── pricing/                 # Pricing schema models (7)
│   │       ├── allocation/              # Allocation schema models (4)
│   │       ├── sale/                    # Sale schema models (2)
│   │       ├── inventory/               # Inventory schema models (8)
│   │       ├── finance/                 # Finance schema models (3)
│   │       └── payment/                 # Payment schema models (1)
│   ├── migrations/
│   │   ├── migrators/
│   │   │   ├── cli.ts                  # Migration CLI entry point
│   │   │   ├── all.ts                  # Config: all schemas combined
│   │   │   ├── public.ts              # Config: public schema only
│   │   │   ├── pricing.ts            # Config: pricing schema only
│   │   │   ├── allocation.ts          # Config: allocation schema only
│   │   │   ├── sale.ts               # Config: sale schema only
│   │   │   ├── inventory.ts          # Config: inventory schema only
│   │   │   ├── finance.ts            # Config: finance schema only
│   │   │   ├── payment.ts            # Config: payment schema only
│   │   │   ├── identity.ts           # Config: identity schema only
│   │   │   ├── ledger.ts             # Config: ledger schema only
│   │   │   └── outreach.ts           # Config: outreach schema only
│   │   └── drizzle/                    # Generated migration output
│   │       ├── public/                 # Public schema SQL + meta/
│   │       ├── pricing/                # Pricing schema SQL + meta/
│   │       ├── allocation/             # Allocation schema SQL + meta/
│   │       ├── sale/                   # Sale schema SQL + meta/
│   │       ├── inventory/              # Inventory schema SQL + meta/
│   │       ├── finance/                # Finance schema SQL + meta/
│   │       ├── payment/                # Payment schema SQL + meta/
│   │       ├── identity/               # Identity schema SQL + meta/
│   │       ├── ledger/                 # Ledger schema SQL + meta/
│   │       └── outreach/              # Outreach 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: public, pricing, allocation, sale, inventory, finance, payment, identity, ledger, outreach
--allChạy lệnh cho tất cả 7 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
public.tspublicdrizzle/public/schemas/public/**/schema.ts
pricing.tspricingdrizzle/pricing/schemas/pricing/**/schema.ts
allocation.tsallocationdrizzle/allocation/schemas/allocation/**/schema.ts
sale.tssaledrizzle/sale/schemas/sale/**/schema.ts
inventory.tsinventorydrizzle/inventory/schemas/inventory/**/schema.ts
finance.tsfinancedrizzle/finance/schemas/finance/**/schema.ts
payment.tspaymentdrizzle/payment/schemas/payment/**/schema.ts
identity.tsidentitydrizzle/identity/schemas/identity/**/schema.ts
ledger.tsledgerdrizzle/ledger/schemas/ledger/**/schema.ts
outreach.tsoutreachdrizzle/outreach/schemas/outreach/**/schema.ts
all.tsTất cả 10 schemadrizzle/ (gốc)dist/models/schemas/*.js

Lưu ý: Cấu hình all.ts khác với cấu hình schema đơn lẻ. Nó sử dụng file .js đã biên dịch từ dist/ và lọc tất cả 10 schema, xuất ra thư mục drizzle/ gốc.

Khác biệt Chính: Đơn lẻ vs Tất cả

Khía cạnhCấu hình Schema Đơn lẻCấu hình Tất cả (all.ts)
Nguồn schemaFile TypeScript nguồn (./src/models/schemas/{schema}/**/schema.ts)JavaScript đã biên dịch (./dist/models/schemas/*.js)
Thư mục đầu raThư mục con theo schema (drizzle/{schema}/)Thư mục drizzle gốc (drizzle/)
Bộ lọc schemaMột schemaTất cả 7 schema
Cần buildKhông (đọc .ts trực tiếp)Có (đọc .js đã biên dịch)

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: '998-admin', name: { en: 'Admin', vi: 'Quan Tri Vien' }, priority: 998 },
  // ...
];

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: public, pricing, allocation, sale, inventory, finance, payment

Đả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
  • Đối với cấu hình all.ts, cần build trước (bun run rebuild) vì nó đọc từ dist/
  • 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.