Demo

Plugin အတွက် DB migration ဖန်တီးနည်း

IDatabaseMigration class များ (getName၊ getSource၊ up၊ down) ကို IPlugin.getMigrations() မှ ပြန်ပေး၍ schema ပြောင်းလဲမှုကို ပို့ဆောင်ရပါသည်။ Staging / production upgrade တွင် synchronize ထက် migration ကို ဦးစားပေးရပါမည်။

ပန်းတိုင်နှင့် မည်သည့်အခါ အသုံးပြုမည်နည်း

Entity registration က runtime အတွက် table များကို ဆက်လက် ကြေညာနေသော်လည်း၊ အမှန်တကယ် schema ပြောင်းလဲမှုသည် migration မှတဆင့် ဖြစ်ရပါမည်။ Additive သို့မဟုတ် destructive DDL၊ rename၊ index၊ constraint နှင့် plugin upgrade အတွင်း တစ်ကြိမ်သာ run ရမည့် data backfill အတွက် အသုံးပြုပါ။

  • Staging / production — synchronize ကို မမှီခိုရပါ
  • ပြန်လှန်နိုင်သော ပြောင်းလဲမှု — up() နှင့် down() ကို တွဲထားပါ
  • Ship ပြီးသား ဖိုင်များ — မပြင်ရပါ; နံပါတ်အသစ်ဖြင့် migration အသစ် ထည့်ပါ

1. လိုအပ်ချက်များ

backend/src/index.ts တွင် default-export IPlugin ရှိရပါမည်။ ရည်ရွယ် datasource (များသောအားဖြင့် shared default) ကိုလည်း ရှင်းလင်းစွာ သိရှိရပါမည်။

  • backend/src/index.ts က IPlugin ကို implement လုပ်သော plugin class ကို export လုပ်ရပါသည်
  • ပြောင်းလဲမှု ပိုင်ဆိုင်သော connection ကို သိပါ — များသောအားဖြင့် { plugin: "default", name: "default" }
  • Entity definition များကို နောက်ဆုံး migrated schema နှင့် ကိုက်ညီအောင် ပြင်ဆင်မည် ဖြစ်ပါသည်
  • Numbering စည်းမျဉ်း — backend/src/migrations/ အောက်တွင် 001, 002, …

2. Folder အပြင်အဆင်

Migration class များကို backend/src/migrations/ အောက်တွင် ထားရှိပြီး၊ getMigrations() က ပြန်ပေးနိုင်အောင် export လုပ်ရပါသည်။

  • ဖိုင်တစ်ခုလျှင် class တစ်ခု; အမည် / နံပါတ်များကို စဉ်အတိုင်း ထားပါ
  • Plugin က ESM သုံးပါက index မှ .js extension ဖြင့် import လုပ်ပါ
TSlayout
1backend/ 2 src/ 3 index.ts # IPlugin — getMigrations() 4 migrations/ 5 001-initial.migration.ts 6 002-add-status.migration.ts 7 feature/ 8 my.module.ts

3. IDatabaseMigration implement လုပ်ခြင်း

getName()၊ getSource()၊ up(QueryRunner) နှင့် down(QueryRunner) ကို implement လုပ်ရပါသည်။ getName() ကို metadata.name ဖြင့် prefix လုပ်၍ plugin အချင်းချင်း ထူးခြားအောင် ထားပါ။ getSource() သည် များသောအားဖြင့် { plugin: "default", name: "default" } ကို ပြန်ပေးပါသည်။

  • getName() — ထူးခြားသော id; metadata.name ဖြင့် prefix လုပ်ပါ (ဥပမာ `${metadata.name}_002_add_status`)
  • getSource() — SQL run မည့် DataSource (အများအားဖြင့် shared default DB)
  • up() — ပြောင်းလဲမှုကို သက်ရောက်စေပါ; local ပြန် run အတွက် IF EXISTS / IF NOT EXISTS ကို ဦးစားပေးပါ
  • down() — တူညီသော ပြောင်းလဲမှုကို ပြန်ဖျက်ပါ
TSXmigration class
1import type { IDatabaseMigration } from "@quan-erp/shared-types"; 2import type { QueryRunner } from "typeorm"; 3import metadata from "../../module.metadata.json" with { type: "json" }; 4 5export class AddStatusMigration implements IDatabaseMigration { 6 getName() { 7 return `${metadata.name}_002_add_status`; 8 } 9 10 getSource() { 11 return { plugin: "default", name: "default" }; 12 } 13 14 async up(queryRunner: QueryRunner): Promise<void> { 15 await queryRunner.query( 16 `ALTER TABLE ${metadata.name}_order ADD COLUMN IF NOT EXISTS status varchar(32)`, 17 ); 18 } 19 20 async down(queryRunner: QueryRunner): Promise<void> { 21 await queryRunner.query( 22 `ALTER TABLE ${metadata.name}_order DROP COLUMN IF EXISTS status`, 23 ); 24 } 25}

4. getMigrations() ဖြင့် မှတ်ပုံတင်ခြင်း

backend/src/index.ts ၏ default-exported plugin class မှ getMigrations() ဖြင့် migration class များကို ပြန်ပေးရပါသည်။ Post-migration seed သို့မဟုတ် repair အတွက် optional onMigrate() ကို အသုံးပြုနိုင်ပါသည်။

  • Array အစဉ်သည် ရည်ရွယ် apply အစဉ်နှင့် ကိုက်ညီရပါမည်
  • onMigrate() သည် pending migration batch ပြီးဆုံးပြီးနောက် run ပါသည်
  • Custom datasource — getSource() သည် ထို connection ၏ plugin + name နှင့် ကိုက်ညီရပါမည်
TSXbackend/src/index.ts
1import type { GetMigrationsType, IPlugin } from "@quan-erp/shared-types"; 2import { InitialMigration } from "./migrations/001-initial.migration.js"; 3import { AddStatusMigration } from "./migrations/002-add-status.migration.js"; 4 5export default class MyPlugin implements IPlugin { 6 getMigrations(): GetMigrationsType { 7 return [InitialMigration, AddStatusMigration]; 8 } 9 10 async onMigrate(): Promise<void> { 11 // optional: seed / repair after migrations apply 12 } 13 14 // … getRootModule, getName, getVersion, getMetadata, … 15}

5. စစ်ဆေးခြင်းနှင့် စည်းမျဉ်းများ

Plugin ကို Install သို့မဟုတ် upgrade လုပ်သောအခါ core က getMigrations() ကို ဖတ်ပြီး၊ pending name များကို apply လုပ်ကာ၊ module init မတိုင်မီ onMigrate() ကို ခေါ်ပါသည်။

  • Pending getName() များသည် getSource() ပေါ်တွင် up() run ပါသည်
  • Entity column / index များသည် နောက်ဆုံး migrated schema နှင့် ကိုက်ညီရပါသည်
  • Rollback အရေးကြီးပါက down() ကို local / staging တွင် စမ်းသပ်ပါ
  • Ship ပြီးသား migration ကို မပြင်ရပါ — 003-… အသစ် ထည့်ပါ
  • အမှန်တကယ် upgrade အတွက် synchronize ထက် migration ကို ဦးစားပေးပါ
  1. 01

    Plugin install / upgrade

    Core က module.js ကို load လုပ်ပြီး getMigrations() ကို ဖတ်ပါသည်။

  2. 02

    Pending migration များ သက်ရောက်ခြင်း

    မသုံးရသေးသော getName() တိုင်းအတွက် getSource() ပေါ်တွင် up(queryRunner) run ပါသည်။

  3. 03

    onMigrate()

    Migration batch ပြီးဆုံးပြီးနောက် optional plugin hook ဖြစ်ပါသည်။

  4. 04

    Module init

    Entities၊ DI နှင့် @OnInit သည် ပုံမှန်အတိုင်း ဆက်လက် လုပ်ဆောင်ပါသည်။

အဖြစ်များသော အမှားများ

  • IDatabaseMigration ပေါ်တွင် getName() / getSource() အစား plain name property သုံးခြင်း
  • getName() ကို metadata.name ဖြင့် prefix မလုပ်ခြင်း (plugin အချင်းချင်း collision)
  • getSource() မှားခြင်း — entity နှင့် မတူသော DataSource ပေါ်တွင် run ခြင်း
  • Ship ပြီးသား migration ကို ပြင်ခြင်း — နံပါတ်အသစ်ဖြင့် ဖိုင်အသစ် ထည့်ရပါသည်
  • Entity ကိုသာ ပြင်ပြီး migration မပါခြင်း (သို့မဟုတ် ပြန်လှန်)
  • Production upgrade တွင် synchronize: true ကို မှီခိုခြင်း