Backend · Deep dive · dev branch

Kỹ thuật chi tiết

Bootstrap composition root, phân lớp biz/core/infra, 4 payment gateway, 22 game adapters, RabbitMQ + masterWorker. Đọc sau 01-backend.html.

bootstrap/ 58 biz · 48 core · 58 infra 45 repositories 16 workers

Cây thư mục src/

kioskgaming_backend/ ├── src/server.js ← entry: buildAppWithRoutes + startWorkers ├── src/bootstrap/ ← composition root │ ├── index.js, database.js, repositories.js │ ├── services.js, app.js, routes.js, workers.js ├── controllers/ admin/ agent/ shop/ user/ … ├── routes/ 59 route files │ ├── users/ admin/ payment/ kiosk/ │ ├── agent.js shop.js superAgent.js cashier.js │ └── *Auth.js portalAuth.js ├── middleware/ JWT, RBAC, request signing, domain ├── repositories/ 45 repository classes ├── services/ │ ├── biz/ 58 orchestration services │ ├── core/ 48 domain + repository access │ ├── infra/ 58 integrations (email, wallet, fraud…) │ ├── payment/ deposit/withdrawal adapters, webhooks │ ├── gameProviderAdapters/ 22 game adapters + baseProvider │ ├── queues/ retryQueue, rabbitmqProducer, gameWalletSyncQueue │ └── outbound/ outbound queue payloads ├── database/models/ 78 Sequelize models ├── workers/ 16 background workers ├── thirdpartyapi/ low-level HTTP clients per provider ├── modules/cashier-management/ cashier domain module └── utils/ redis, rabbitmq, logger, caches ├── scripts/architecture/ checkLayering.js, violation inventory ├── tests/unit/ (148) · tests/integration/ └── stress-test/ load test v1/v2 + scenarios

Request lifecycle qua bootstrap

1
server.js

buildAppWithRoutes()startWorkers(db) → listen PORT (default 3001). Graceful shutdown: SIGTERM/SIGINT → stopWorkers().

2
bootstrap/database.js

Sequelize connect, load 78 models, associations. UTC timezone (process.env.TZ = 'UTC').

3
bootstrap/repositories.js + services.js

Wire 45 repositories vào core/infra services; biz services nhận core/infra dependencies qua setter injection.

4
bootstrap/app.js

Express + helmet + cors (DB-driven origins + DEV_ORIGINS) + compression + morgan + rate limiters + language middleware.

5
bootstrap/routes.js

mountRoutes: inject models/services vào controllers, mount ~40 route groups, errorHandler cuối cùng.

6
Controller → Biz → Core → Infra

Controller không gọi global.db trực tiếp (architecture lint). Transaction boundaries thường ở biz hoặc infra wallet services.

src/bootstrap/index.jsjs
async function buildAppWithRoutes() {
  const db = await initDatabase();
  const repos = createRepositories(db);
  const services = initServices(db, repos);
  const { app, authLimiter, portalAuthLimiter } = buildApp();
  mountRoutes(app, authLimiter, db, repos, services, portalAuthLimiter);
  return { app, db, repos };
}

Service layer pattern (biz / core / infra)

biz/

Orchestration: validate input nghiệp vụ, gọi nhiều core/infra, emit events. Ví dụ: agentCreditPurchaseBizService.js, paymentTransactionBizService.js.

core/

Domain access qua repository — CRUD + query cho 1 aggregate. Ví dụ: paymentTransactionCoreService, userCoreService.

infra/

Side effects & integrations: email, SMS, telegram, wallet transfer, game sync, fraud. Ví dụ: walletService, gameWalletSyncService.

repositories/

Sequelize queries only. Core services gọi repository — biz không import repository trực tiếp (lint rule).

💡
Quy tắc import

Controller → biz/core/infra OK. Biz → core/infra OK. Core → repository OK. Biz → repository = violation. Controller → repository = critical violation.

Deposit & withdrawal flows (4 providers)

Deposit

  • depositService.createDeposit() → chọn adapter qua getDepositAdapter(provider)
  • Adapters: linkMePayDepositAdapter, btcPayDepositAdapter, zeroxProcessingDepositAdapter, meldDepositAdapter
  • Webhook routes: /api/payment/linkmepay/*, /btcpay/webhook, /zeroxprocessing/webhook, /meld/webhook
  • depositCallbackService + paymentWebhookShared: verify, idempotency, credit wallet, transaction state machine

Withdrawal

  • withdrawalApprovalService — admin/portal approve → getWithdrawalAdapter()
  • Adapters: LinkMePay, BTCPay, 0xProcessing (Meld không hỗ trợ withdrawal)
  • Payout status poll: btcPayPayoutStatusWorker, zeroxProcessingPayoutStatusWorker trong masterWorker
  • Portal USD withdrawal: portalUsdWithdrawalPayoutService + OTP qua withdrawalOtpService
src/services/payment/depositAdapters/index.js
const ADAPTERS = {
  btcpay: btcPayDepositAdapter,
  linkmepay: linkMePayDepositAdapter,
  zeroxprocessing: zeroxProcessingDepositAdapter,
  meld: meldDepositAdapter
};

Adapter patterns

PaymentDeposit / Withdrawal registry

Factory functions getDepositAdapter / getWithdrawalAdapter — validate contract (paymentUrl, externalTransactionId) fail-fast.

GamegameProviderAdapters registry

AdapterRegistry.getAdapter(code) — alias map (firekirin→fkterminal), load config từ GameProvider model (strip sensitive fields), instantiate adapter class.

22 adapters + baseProvider.js fallback cho vegas/apex/valor/mythic/arcadia.

Third-partythirdpartyapi/

Low-level HTTP clients (bluedragon, gamevault, jack2win, megaspin…). Adapter gọi thirdpartyapi, log qua gameProviderApiLogService.

Auth & JWT (multi-portal)

ActorSecretSession tableNotes
Kiosk playerJWT_SECRETUserSessionRefresh token rotation, phone/email OTP
AdminADMIN_JWT_SECRET (super: SUPER_ADMIN_JWT_SECRET)AdminSessionEmail OTP 2FA trước khi cấp token
Agent / Shop / SuperAgentJWT_SECRETAgentSession / ShopSession / SuperAgentSessionPortal login 2FA, email verify gate writes
CashierModule-specificCashierSessionmodules/cashier-management/middleware/
Kiosk deviceRequest signing HMACREQUEST_SIGNING_SECRET cho /api/kiosk/*

Shared flows: /api/portal-auth (forgot password), portalLogin2faService, portalCredentialNotifyService dùng portalSiteUrl.js để build link theo role.

Database models (78)

Sequelize models trong src/database/models/. Migrations: npm run migrate. Partitioned tables: wallet transactions — npm run partition:list.

User & Session

  • User
  • UserSession
  • UserGameAccount
  • UserGameWallet
  • PasswordReset
  • PasswordChangeHistory
  • AccountOtp
  • EmailCode
  • PhoneCode

Wallet & Ledger

  • Wallet
  • WalletTransaction
  • UsdWallet
  • UsdWalletTransaction
  • UsdWalletReservation
  • GameWalletTransaction
  • ZeroxStaticWallet

Payment

  • PaymentTransaction
  • PaymentApiLog
  • PaymentProviderFeeConfig
  • CallbackLog
  • TransactionStep
  • TransactionEvent
  • TransactionFraudFlag

Hierarchy B2B

  • Agent
  • AgentSession
  • AgentGameMapping
  • SuperAgent
  • SuperAgentSession
  • SuperAgentGameMapping
  • Shop
  • ShopSession
  • ShopGameMapping
  • ShopActionLog
  • CommissionLog

Cashier

  • Cashier
  • CashierSession
  • CashierDevice
  • CashierDeviceAccess
  • CashierShift
  • CashierShiftHandover
  • CashierShiftAdjustment
  • CashierTransaction
  • CashierCashDrop
  • CashierAuditLog
  • CashierRedemptionApproval
  • CashDenominationCount

Cash & CDN

  • CashTransaction
  • CashTransactionEvent
  • OnlineDebtSettlement
  • OnlineDebtSettlementLine
  • PlayerTransferLog

Game & Domain

  • GameProvider
  • GameProviderApiLog
  • GameAgentBalance
  • Domain
  • DomainGameMapping
  • KioskHeartbeat

Admin & Support

  • Admin
  • AdminSession
  • AdminOTP
  • AdminPermission
  • Permission
  • AdminActionLog
  • AdminLoginLog
  • AdminBroadcast
  • SupportTicket
  • TicketReply

Ops & Config

  • TransactionLimitsConfig
  • VfxConfiguration
  • VfxConfigSchedule
  • InAppPopup
  • ReconciliationReport
  • GamifyRewardWebhookLog

Queue & background workers

RabbitMQ (khi RABBITMQ_ENABLED ≠ false)

  • emailQueueWorker, smsQueueWorker, telegramQueueWorker, lowBalanceCheckQueueWorker
  • Producer: services/queues/rabbitmqProducer.js

masterWorker (60s tick, Redis lock)

  • retryretryWorker.processRetryQueue + services/queues/retryQueue.js
  • deposit_timeout / withdrawal_timeout — pending transaction cleanup
  • btcpay_payout / zerox_payout — poll external payout status
  • agent_balance_sync — sync game agent balances
  • expired_data_cleanup — sessions, email/phone codes (daily)
  • player_inactivity_spike — fraud detection

Standalone workers

cashierManagementWorker, lowBalanceDailyWorker, dailyStatsReportWorker, reconciliationWorker, agentBalanceSyncWorker

Game wallet async: gameWalletSyncQueue — decouple provider latency khỏi HTTP response.

Architecture lint scripts

npm run lint              # ESLint biz/core/infra + scripts/architecture
npm run architecture:inventory   # regenerate violation baseline
npm run architecture:check       # fail CI if NEW violations vs baseline
npm run ci:backend        # lint + architecture:check + jest

Scanner: scripts/architecture/scanArchitectureViolations.js — detect controller→repository, biz→repository, cross-layer imports. Baseline: architecture/architecture-violation-baseline.json.

Hướng dẫn cho dev mới

AThêm API endpoint
  1. Tạo biz service trong services/biz/
  2. Core service + repository nếu cần DB mới
  3. Controller gọi biz — không query DB trực tiếp
  4. Route trong routes/, mount trong bootstrap/routes.js
  5. Wire dependencies trong bootstrap/services.js nếu service mới
BThêm payment provider
  1. Tạo deposit adapter (+ withdrawal nếu có) trong services/payment/
  2. Đăng ký trong depositAdapters/index.js và/hoặc withdrawalAdapters/index.js
  3. Webhook handler trong paymentUserController + route payment.js
  4. Env vars + PaymentProviderFeeConfig seed
CThêm game provider
  1. HTTP client trong thirdpartyapi/myprovider/
  2. Adapter extend baseProvider.js trong gameProviderAdapters/
  3. Register trong gameProviderAdapters/index.js
  4. Insert GameProvider record (code, config, gameUrl)
DChạy local
npm install && cp .env.example .env
npm run migrate          # DB migrations
npm run dev              # nodemon src/server.js
npm run ci:backend       # lint + architecture + tests
curl http://localhost:3001/health

Lỗi thường gặp

Triệu chứngNguyên nhânFix
Service undefined modelChưa wire trong bootstrap/services.jsThêm setRepository/setModels trong initServices()
architecture:check failImport sai tầng mớiDi chuyển logic xuống core/infra; không import repository từ biz
Webhook double creditIdempotency bypassKiểm tra CallbackLog + syncMetadata.walletCreditedAt
Portal JWT invalidDùng nhầm ADMIN_JWT cho portalAgent/shop/super-agent dùng JWT_SECRET + đúng session table
RabbitMQ workers không chạyRABBITMQ_ENABLED=false hoặc broker downCheck RABBITMQ_URL; fallback sync trong một số infra services
CORS blockedOrigin chưa có trong Domain tableAdmin → Domains hoặc thêm DEV_ORIGINS trong bootstrap/app.js
0x/BTCPay payout stuckPoll worker disabledZEROX_PAYOUT_POLL_ENABLED / BTCPAY_PAYOUT_POLL_ENABLED + API keys
Game sync timeoutProvider chậmJob vào gameWalletSyncQueue; check GameProviderApiLog