Skip to main content

Database code review — kioskgaming_backend

FieldValue
Repokioskgaming_backend/src/database/
Review date2026-06-20
StackPostgreSQL · Sequelize ORM · custom migration runner
Quy mô76 models · 207 migrations · 8 bảng partitioned (+ gamify logs)
Related docsFinance System · Backend code review (full) · Migration guide: kioskgaming_backend/src/database/migrations/MIGRATION_GUIDE.md

1. Tóm tắt

Tiêu chíĐánh giá
Schema design (wallet, payment, hierarchy)✅ Mạnh — dual wallet rõ, ledger có snapshot balance
Migration discipline✅ Tốt — migration-first, không sync() production
Partitioning high-volume tables✅ Có chủ đích + tooling CLI
Optimistic locking / idempotency✅ Wallet + USD ledger + payment status
Config & secrets❌ Config trùng 4 file; password fallback hardcoded
Model ↔ DB alignment⚠️ Một số unique/index chỉ khai báo trong model
Rollback thực tế⚠️ Nhiều migration data repair irreversible
JSONB metadata⚠️ Linh hoạt nhưng thiếu schema/version
Precision tiền tệ⚠️ DECIMAL(18,8) vs (15,2) lẫn lộn giữa domain
Test DB isolation⚠️ config.js test database expression có bug tiềm ẩn

Verdict: Nền tảng production-grade cho fintech kiosk (ledger, partition, idempotency), nhưng vận hành và governance cần siết: config tập trung, partition automation, align constraint DB ↔ model, tách data repair khỏi schema migration.


2. Schema overview — nhóm bảng

NhómBảng tiêu biểuGhi chú
Users & authusers, email_codes, phone_codes, user_sessions, account_otpsHooks bcrypt trên User
Payment & gatewaypayment_transactions, callback_logs, payment_api_logs, transaction_events, transaction_fraud_flagsVolume cao, partitioned
CDN credits walletwallets, wallet_transactionsownerType: player/agent/shop/super_agent/platform
USD walletusd_wallets, usd_wallet_transactions, usd_wallet_reservationsgross/net + reserved; type ENUM
Online debtonline_debt_settlements, online_debt_settlement_linesPayout network/wallet metadata
Gamegame_providers, user_game_wallets, game_wallet_transactionsPer-provider balance
CDN hierarchysuper_agents, agents, shops, commission_logsbuyer_type trên payment
Cashiercashiers, cashier_shifts, cashier_transactions, cashier_shift_adjustmentsFK/CASCADE thiết kế kỹ (Jun 2026)
Admin & governanceadmins, admin_action_logs, admin_broadcasts, permissionsAction log partitioned
Reconciliationreconciliation_reports, reconciliation_discrepancies⚠️ Model + repo có, chưa load vào connection.js
Config / limitspayment_provider_fee_configs, transaction_limits_configs, vfx_configurationsFee snapshot tại payment creation

Quy ước đặt tên

  • Bảng: snake_case, số nhiều, freezeTableName: true
  • Cột DB: snake_case · field JS: camelCase + field: 'snake_case'
  • Timestamps: created_at, updated_at · timezone UTC (+00:00)
  • PK: hỗn hợp — INTEGER auto-increment (payment, logs cũ) · UUID (wallet, cashier, CDN mới)
  • ENUM PostgreSQL: DataTypes.ENUM(...) — phổ biến cho status, type, owner_type
  • Soft delete: không dùng — lifecycle qua status (terminated, closed, cancelled)

3. Connection & runtime

File: src/database/connection.js

AspectChi tiết
Pool (development)max 20, min 2, acquire 15s
Pool (production)max 8 (conservative hơn dev)
Query timeoutSEQUELIZE_QUERY_TIMEOUT_MS, mặc định 8000ms
SchemaKhông sequelize.sync() — chỉ migration
Associations~330 dòng khai báo tập trung trong connection.js
global.dbExport ~74 model + sequelize — pattern legacy, coupling cao

Rủi ro P0 — credential fallback

// connection.js, migrate.js, config/config.js (development/test)
password: process.env.DB_PASSWORD || '@Linux121314'

Mật khẩu hardcoded xuất hiện khi thiếu env — phải fail fast ở production/staging.

Config trùng lặp (4 nơi)

FileDùng cho
src/database/connection.jsRuntime app
src/database/migrate.jsCLI migrate up/down/status/reset
src/database/scripts/partitionManager.jsPartition ops
src/database/config/config.jsLegacy sequelize-cli / docs

Drift giữa các file → deploy/migrate/partition có thể trỏ DB khác nhau nếu sửa một chỗ quên chỗ kia.

Bug tiềm ẩn — test database name

// config/config.js — operator precedence sai
database: process.env.DB_NAME + '_test' || 'kiosk_gaming_test'
// Thực tế: (process.env.DB_NAME + '_test') — nếu DB_NAME undefined → 'undefined_test'

4. Models — patterns Sequelize

Factory: mọi model module.exports = (sequelize) => sequelize.define(...).

Associations: tập trung connection.js, không trong từng model file. Nhiều quan hệ dùng constraints: false — ví dụ WalletTransactionPaymentTransaction qua polymorphic reference_type / reference_id (không FK vật lý).

Hooks: chủ yếu auth (User bcrypt, OTP/session lifecycle). Không raw SQL trong model files.

JSONB metadata (~29 model): nặng nhất ở PaymentTransaction — 6 field: metadata, callbackData, paymentResult, providerRequest, providerResponse, syncMetadata. Cũng có trên UsdWalletTransaction, CashierTransaction, API/callback logs.

Optimistic locking (version)

EntityCơ chế
PaymentTransaction_updateStatusWithValidation — UPDATE WHERE id AND version
Wallet, UsdWalletupdateByIdAndVersion + retry loop trong service (max 5 lần)
Config entitiesVfxConfiguration, TransactionLimitsConfig

Model chưa đăng ký global.db

  • ReconciliationReport, ReconciliationDiscrepancy — có model + repository + core service, không import trong connection.js
  • Gọi global.db.ReconciliationReport sẽ undefined — bug tiềm ẩn nếu worker/service chưa wire qua bootstrap repo

5. Migrations

MetricValue
Số file207 (YYYYMMDDHHMMSS-kebab-description.js)
RunnerCustom MigrationManager trong migrate.jskhông dùng sequelize-cli runtime
TrackingBảng "SequelizeMeta" tự quản
CLInpm run migrate · migrate:status · migrate:rollback · migrate:reset
Docssrc/database/migrations/MIGRATION_GUIDE.md
Generatornpm run migrate:generate

Điểm mạnh

  • Migration-first, không sync schema production
  • Status/rollback CLI rõ ràng
  • Partial indexes cho worker queries (add-worker-query-performance-indexes)
  • Seed config dùng ON CONFLICT trong migration (fee configs, zerox static wallet)

Rủi ro & anti-patterns

PatternVí dụRủi ro
Data repair trong migrationrepair-zerox-*, dedupe-*, delete-legacy-*down thường no-op / irreversible
Backfill hàng loạtwallet-transactions-type-credit-debit, CDN cutoverKhó rollback, chạy lâu trên prod lớn
migrate:resetDROP SCHEMA public CASCADEChỉ local/dev — nguy hiểm nếu nhầm env
ENUM thêm value liên tụcwithdrawal OTP, admin action log enumsPostgreSQL không xóa ENUM value — tech debt

Themes gần đây (202604–202606)

  1. Cashier full schema (cashier-management-full-schema)
  2. USD wallet ledger (gross/net, reservations, purchase_credits type)
  3. Online debt settlement refactor + payout wallet/network
  4. Payment provider fee configs + ZeroX/Meld repair
  5. CDN cutover user_walletswallets
  6. Portal 2FA, withdrawal OTP, player terminate/ban
  7. Worker performance indexes

Khuyến nghị migration governance

  • Phân loại header: reversible · data-only · irreversible
  • Data repair → script idempotent riêng (src/database/scripts/) thay vì DELETE/UPDATE trong migration
  • PR migration: bắt buộc mô tả rollback thực tế (không chỉ stub down)

6. Partitioning

Tooling: src/database/scripts/partitionManager.js + npm scripts partition:list, partition:create-year, partition:validate

8 bảng partitioned (monthly RANGE trên created_at):

  • payment_transactions
  • transaction_events
  • callback_logs
  • transaction_steps
  • game_provider_api_logs
  • wallet_transactions
  • game_wallet_transactions
  • admin_action_logs

Thêm: gamify_reward_webhook_logs (migration riêng).

Lý do: bảng append-only, volume cao — pre-create partition 2025-01 → 2030-12 (~576 partition child tables).

Hệ quả kiến trúc quan trọng

  • PostgreSQL yêu cầu UNIQUE/PK trên cột referenced bao gồm partition keykhông thể FK trực tiếp tới wallet_transactions(id) nếu chỉ PK (id)
  • Code comment rõ trong migration drop credit_transactions — polymorphic reference + app-level integrity thay FK

Vận hành

  • Partition không tự tạo khi app boot — cần cron/ops tạo partition trước năm mới (2031+)
  • Insert vào tháng chưa có partition → fail runtime

7. Idempotency & constraints (DB level)

BảngConstraintNguồn
payment_transactions.transaction_idUNIQUEModel + migration
usd_wallet_transactions (transaction_id, type)UNIQUEMigration create-usd-wallet-transactions
wallet_transactions (transaction_id, type)UNIQUE trong model index⚠️ Không tìm thấy migration tạo constraint tương ứng — cần verify trên DB production
usd_wallet_reservations.reservation_keyUNIQUEIdempotent hold USD
payment_provider_fee_configsUNIQUE scope+provider+operation+methodMigration

Application-level idempotency

(bổ sung khi DB chưa có constraint)

  • walletService.credit — check findByTransactionIdAndType trước insert
  • usdWalletService.recordAdjustment/recordReversal — unique (transactionId, type)

Partial indexes (worker)

Migration 20260414153000-add-worker-query-performance-indexes.js — pending deposits, retry queue, zerox inflight withdrawals.

Cashier integrity

  • Global unique cashiers.username
  • Partial unique: một open shift per cashier (WHERE closed_at IS NULL)

8. Data integrity & tiền tệ

Foreign keys

  • Có FK rõ: cashier schema (CASCADE/RESTRICT/SET NULL), usd_wallet_transactionsusd_wallets, online debt lines
  • Tắt FK Sequelize (constraints: false): log tables, polymorphic joins, admin permissions
  • Polymorphic refs: wallet_transactions.reference_type + reference_idkhông FK

Nullable / polymorphic

  • payment_transactions.user_id nullable — CDN buyer qua buyer_type / buyer_id
  • Dual reference pattern phổ biến cho ledger ↔ payment

DECIMAL precision — không thống nhất

ContextPrecision
CDN wallet balance/ledgerDECIMAL(18, 8)
USD wallet gross/netDECIMAL(18, 8)
Payment amountDECIMAL(15, 2) — legacy
Game wallet transactionDECIMAL(15, 2)
Cashier physical cashDECIMAL(18, 2)
Fee ratesDECIMAL(5,4)DECIMAL(8,6) snapshot

Cùng hệ monetary nhưng precision khác → risk rounding khi convert/cross-ledger; nên document invariant hoặc chuẩn hóa dần.

Dual wallet systems

  • wallets (CDN credits) + usd_wallets (USD gross/net) — không merge DB
  • Integrity phụ thuộc service layer discipline — mọi money movement phải nằm trong transaction boundary rõ ràng

9. Anti-patterns

Anti-patternChi tiết
global.db singleton122 controller-direct-db — query trực tiếp, khó test
Config DB ×4Drift giữa connection / migrate / partition / config.js
Model ↔ migration driftUnique index trong model nhưng thiếu migration (wallet_transactions)
Model orphanReconciliationReport không trong connection.js
JSONB không schemaProvider payload, sync state — khó query/audit/version
Partition ops manualQuên tạo partition → outage insert
Irreversible migrations labeled rollback-ablemigrate:rollback misleading cho data repair
Repository business logic267 baseline repository-conditional-logic
ENUM proliferationKhông remove được value cũ
Test DB name bugconfig.js test database expression
N+1Deep include chains payment + game + user

10. Điểm mạnh

  1. Migration-first schema — production an toàn
  2. Partitioning có tooling + validation scripts
  3. Optimistic locking trên wallet và payment status
  4. Idempotency design USD ledger (transaction_id, type)
  5. Fee/amount snapshot tại payment creation — audit trail
  6. UTC enforced everywhere
  7. Production pool conservative (max 8)
  8. Cashier schema FK thiết kế cẩn thận
  9. State machine payment trong model (_updateStatusWithValidation)
  10. Worker-oriented partial indexes

11. Khuyến nghị (ưu tiên)

#PriorityHành động
DB-1P0Gỡ hardcoded DB_PASSWORD — fail fast production/staging
DB-2P0Đăng ký ReconciliationReport + ReconciliationDiscrepancy vào connection.js
DB-3P0Verify + migration UNIQUE (transaction_id, type) trên wallet_transactions nếu DB thiếu
DB-4P0Automate partition provisioning (cron tạo partition N+1; alert partition:validate)
DB-5P1Centralize DB config → databaseConfig.js (1 source)
DB-6P1Document dual-wallet invariant (wallets vs usd_wallets)
DB-7P1Tách data repair khỏi schema migration
DB-8P1Chuẩn hóa DECIMAL hoặc document precision per domain
DB-9P2JSONB schema version (metadata_schema_version) + validate service layer
DB-10P2Fix config.js test database expression
DB-11P2ENUM governance — cân nhắc STRING + check constraint cho value hay đổi
DB-12P2Giảm constraints: false cho quan hệ ổn định (non-partitioned refs)
DB-13P3Migration class tag: reversible / data-only / irreversible
DB-14P3Repository refactor — pure query, logic lên service/core

12. Checklist PR (database)

  • Migration mới có tag rollback thực tế (reversible / data-only / irreversible)
  • Data repair không nằm trong migration DDL nếu có thể tách script
  • Model mới đăng ký connection.js + bootstrap/repositories.js
  • Unique/index trong model có migration tương ứng
  • Money field dùng đúng precision domain
  • JSONB metadata có lý do; cân nhắc schema version
  • Partition impact đã xem xét (insert vào bảng partitioned)
  • Không thêm hardcoded DB credentials

13. Definition of Done — database health

  • DB config single source; zero hardcoded credentials
  • ReconciliationReport models registered in connection.js
  • wallet_transactions UNIQUE (transaction_id, type) verified on production
  • Partition validate pass; partition năm N+1 đã provision
  • Dual-wallet invariant documented (link Finance System)
  • Test database name fixed in config.js

14. File tham chiếu nhanh

FileVai trò
src/database/connection.jsSequelize, pool, global.db, associations
src/database/migrate.jsCustom migration runner
src/database/scripts/partitionManager.jsPartition CRUD CLI
src/database/config/config.jsLegacy config (duplicate)
src/database/migrations/MIGRATION_GUIDE.mdHướng dẫn migration
src/database/migrations/20260121224500-convert-tables-to-partitioned.jsPartition conversion
src/database/migrations/20260608140000-cashier-management-full-schema.jsCashier schema
src/database/migrations/20260427070100-create-usd-wallet-transactions.jsUSD ledger + unique constraint
src/bootstrap/repositories.jsRepository wiring

15. Changelog

DateAuthorNotes
2026-06-20Code reviewTách từ Backend code review §8