Developer Documentation
BANA (BANA) is an enterprise Point of Sale (POS) system designed for multi-merchant event ticketing, retail, and hospitality environments. Built as a Bun monorepo, the platform leverages the IGNIS Framework for dependency injection-based microservices, Tauri for cross-platform desktop applications, and a modern React stack for web interfaces.
This documentation provides comprehensive technical reference for developers building with, extending, or integrating the BANA platform.
Developer Guides
New here? Start with these:
| Guide | What you'll learn |
|---|---|
| Getting Started | Clone, install, build, run your first service |
| Build System | Makefile targets, dependency graph, Docker builds |
| Environment Reference | Every environment variable across all services |
| Git Workflow | Branching, commits, MRs, code review, glab CLI |
| IGNIS Patterns | Application hierarchy, DI, controller factory, model patterns |
Architecture & Reference
| Guide | What it covers |
|---|---|
| System Architecture | Service registry, dependency chain, database schema mapping, component matrix |
| Event Architecture | Kafka topics, CDC pipeline, WebSocket topics, webhook events |
| Database Guide | 11 PostgreSQL schemas, Drizzle ORM, migration workflow, column enrichers |
| Testing Guide | Bun test runner, preload mocking, Object.create pattern, mock factories |
| Frontend Patterns | Shared stack, app dependency graph, component library, i18n |
Technology Stack
| Category | Technology | Version | Description |
|---|---|---|---|
| Runtime | Bun | >=1.3.8 | Fast all-in-one JavaScript runtime with native TypeScript support |
| Framework | IGNIS | 0.0.7-7 | TypeScript-first DI framework (@venizia/ignis) |
| Web Server | Hono | 4.11.3 | Ultra-fast web framework with type-safe routing |
| API Schema | Zod | 4.1.13 | TypeScript-first schema validation library |
| API Docs | Hono Zod OpenAPI | 1.2.0 | OpenAPI 3.1 spec generation from Zod schemas |
| ORM | Drizzle ORM | 0.45.1 | Type-safe SQL toolkit for PostgreSQL |
| ORM Migrations | Drizzle Kit | 0.31.8 | Schema migration tool for Drizzle |
| Validation | Drizzle Zod | 0.8.3 | Zod schema generation from Drizzle tables |
| Database | PostgreSQL | 14+ | Multi-schema relational database (7 schemas, 55+ models) |
| Cache/Queue | Redis | 6+ | In-memory data structure store (5 logical databases) |
| Job Queue | BullMQ | Latest | Redis-based distributed job queue (24 queues, 3 partitions each) |
| Search | Typesense | Latest | Fast, typo-tolerant search engine with real-time CDC sync |
| Storage | Minio | Latest | S3-compatible object storage for media assets |
| API Reference | Scalar Hono | 0.9.28 | Modern API documentation UI for Hono |
| Password Hashing | bcrypt | 6.0.0 | Adaptive hashing function for credentials |
| Date/Time | Day.js | 1.11.19 | Lightweight date manipulation library |
| PostgreSQL Client | node-postgres | 8.16.3 | Non-blocking PostgreSQL client for Node.js |
| TypeScript | TypeScript | ~5.9.3 | Typed superset of JavaScript |
| Helpers | IGNIS Helpers | 0.0.6-7 | IGNIS framework utility library (@venizia/ignis-helpers) |
| Desktop | Tauri | 2.x | Lightweight, secure desktop framework (Rust + WebView) |
| Frontend | React | 19.x | Declarative UI library for web and desktop apps |
| Build Tool | Vite | 7.x | Next-generation frontend build tool with HMR |
| Styling | TailwindCSS | 4.x | Utility-first CSS framework |
| UI Components | Radix UI + shadcn | Latest | Accessible, composable component primitives |
Architecture Overview
Frontend Applications
React 19 + Vite 7 + TailwindCSS 4 + shadcn/ui
client
Admin Dashboard
sale-renderer
POS Desktop UI
sale-main
Tauri Backend (Rust)
bo
Back Office
shared
admin-ui-kit
UI Component Library
shared
core
App Constants, Hooks
▼ HTTP REST / WebSocket / Tauri IPC
Backend Microservices
IGNIS Framework (Bun + Hono + Drizzle + Zod)
identity
Auth, Users, RBAC
commerce
Products, Pricing
sale
Orders, Checkout
inventory
Stock, PO, Tracking
search
Typesense + CDC
asset
Media, Minio S3
invoice
E-Invoice, PDF
finance
Wallets, Transactions
payment
Orchestration Layer
signal
WebSocket, E2E ECDH
shared
core
Base Classes, Utils
▼ IGNIS Components (Dependency Injection)
Third-Party Integrations
External service connectors and adapters
mq-pay
Multi-Provider Payments
iiapi
VNPAY E-Invoice API
t-van
Tax VAN / CQT
▼ HTTPS / REST API
External Services
Third-party providers and government authorities
VNPAY
Payment Gateway
VNPAY Invoice
E-Invoice Provider
CQT
General Tax Dept
T-VAN Providers
Viettel, VNPT
System Architecture
Package Dependency Graph
Frontend Applications
| Application | Package | Stack | Description |
|---|---|---|---|
| client | @nx-app/client | React 19.2.1, Vite 7.1.7, TailwindCSS 4.1.16, Redux Toolkit, TanStack Query/Table | Main admin dashboard with 28 screen modules: categories, products, sale orders, inventory, finance (wallets, transactions, categories), purchase orders, users, roles, merchants, organizers, devices, terminals, reports, settings |
| bo | @nx-app/bo | React 19, Vite 7, TailwindCSS 4 | Back Office for merchant/organizer staff with simplified admin interface (5 screens: home, merchant, organizer, sign-in, errors) |
| sale-renderer | @nx-app/sale-renderer | React 19.2.0, Vite 7.2.4, TailwindCSS 4.1.18, Tauri 2.9.1, Socket.IO Client | POS desktop app WebView: order creation, barcode scanning, receipt printing, payment integration, real-time updates via WebSocket |
| sale-main | @nx-app/sale-main | Tauri 2.x, Rust, SQLite (Sea-ORM) | POS desktop backend (Tauri main process): USB peripheral handling (scanner, printer, card reader), local database, custom plugins (USB, Payment, External Display, Machine UID) |
| admin-ui-kit | @nx-app/admin-ui-kit | Radix UI, shadcn/ui, lucide-react, TailwindCSS 4 | Shared UI component library: 38 shadcn components + custom core/icon components. Consumed by client and bo |
| core | @nx-app/core | React 19 | Shared app core module: constants, hooks, locales (i18n), utilities. Peer dependency: ra-core, @minimaltech/ra-core-infra |
Backend Packages
| Package | Package Name | Port | Key Features | Dependencies | Status |
|---|---|---|---|---|---|
| core | @nx/core | N/A | Shared foundation: DefaultApplication, SoftDeletableRepository, 7 PostgreSQL schemas (55 models), Snowflake ID generator, cross-service auth, CryptoUtility (AES-256-GCM), bootstrap helpers (bootstrapApplication, bootstrapMigration, createAppConfig, createMigrationProcessLoader) | N/A | Production |
| identity | @nx/identity | 3001 | Authentication & authorization: JWT/Basic auth strategies, user CRUD with atomic creation (user + identifiers + profile + roles), employee management with organizer/merchant mapping, email verification (6-digit code, 10min expiry, rate-limited), password hashing (bcrypt), RBAC with 5 default roles (SUPER_ADMIN 999, ADMIN 998, OPERATOR 997, ORGANIZER_OWNER 899, EMPLOYEE 898) | @nx/core | Production |
| commerce | @nx/commerce | 3002 | Product catalog & pricing engine: product aggregates (info, identifiers, variants), dynamic pricing with rule-based evaluation (OVERRIDE/DISCOUNT/MARKUP operators: EQ, NE, GT, GTE, LT, LTE, IN, NIN), multi-merchant support, categories, sale channels, role-based access filtering | @nx/core, @nx/asset, @nx/inventory, @nx/search, @nx/t-van | Production |
| sale | @nx/sale | 3003 | Order management & checkout: order lifecycle (DRAFT → PROCESSING → PARTIAL → COMPLETED/CANCELLED), item management with auto-merge for duplicates, checkout validation (non-empty cart, valid items), MQ-Pay event handling for payment status updates, max constraints (9999 qty/item, 100 items/order) | @nx/core, @nx/mq-pay | Production |
| inventory | @nx/inventory | N/A | Stock management & purchase orders: multi-location inventory tracking (quantityOnHand, quantityAvailable, quantityReserved), PO lifecycle (DRAFT → PROCESSING → CONFIRMED → COMPLETED), 20+ predefined tracking types (STOCK_IN, SALE, TRANSFER, RETURN, ADJUSTMENT, etc.), Redis-cached tracking type lookups | @nx/core | Production |
| search | @nx/search | N/A | Typesense search integration: real-time sync via Debezium CDC (PostgreSQL → Typesense), IGNIS filter to Typesense query conversion (eq, neq, gt, gte, lt, lte, between, inq, nin, and, or), 6 collections (products, organizers, merchants, categories, devices, sale-channels), diagnostic endpoints | @nx/core | Production |
| asset | @nx/asset | N/A | File & media storage: dual storage (Minio S3-compatible + local disk), MetaLink entity for metadata tracking (bucketName, objectName, link, mimetype, size, etag, storageType, principalId, principalType), presigned URL generation | @nx/core | Production |
| invoice | @nx/invoice | N/A | E-Invoice generation (Vietnam-compliant): invoice types (VAT, Sales, POS, delivery notes, receipts, PIT certificates), XML formatting, digital signature, PDF generation, sequential numbering | @nx/core, @nx/iiapi, @nx/t-van | Production |
| finance | @nx/finance | N/A | Financial management & accounting: wallets (CASH, BANK, EWALLET, CREDIT_CARD) with initial/current balance, income/expense transaction tracking, 20 seeded categories, event-driven processing (commerce initialized → create default wallet, payment success → record income, PO received → record expense) via Redis Pub/Sub + BullMQ | @nx/core | Production |
| payment | @nx/payment | N/A | Payment orchestration layer: webhook configuration management (endpoint registration, event types, retry metadata), payment credential loading/decryption from Configuration table (AES-256-GCM), webhook dispatch with exponential backoff retry, MQ-Pay component initialization with dynamic credential getter | @nx/core, @nx/mq-pay | Production |
| signal | @nx/signal | N/A | Real-time WebSocket communication: mandatory ECDH P-256 key exchange + AES-256-GCM encrypted messages, JWT authentication on handshake, REST API for broadcast/room/client messaging, Redis-backed state for cross-instance delivery (single or cluster mode), stateless (no database, no migrations) | @nx/core | Production |
| gateway | @nx/gateway | N/A | API Gateway: routing, load balancing, circuit breaker, rate limiting | @nx/core | Planned |
Third-Party Integrations
| Integration | Package Name | Description | Key Features | External Services |
|---|---|---|---|---|
| mq-pay | @nx/mq-pay | Multi-provider payment system | Providers: SYSTEM (CASH, BANK_TRANSFER), VNPAY_QR_MMS, VNPAY_PHONE_POS, VNPAY_SMART_POS; Transaction lifecycle (NEW 100 → PARTIAL 300 → SETTLED 600 / CANCELLED 505 / BLOCKED 800 / CLOSED 900); Payment attempt types (MAKE_PAYMENT 100, CANCEL_PAYMENT 200, REFUND_PAYMENT 300); BullMQ 3-partition queues (scheduler, confirmation) with 10 concurrent jobs/partition; Event emitter with 11+ event types; AppRegistry singleton pattern (cannot use DI @inject()) | VNPAY |
| iiapi | @nx/iiapi | VNPAY E-Invoice integration (viiAPI) | Invoice types: VAT, Sales, POS, internal delivery notes, receipts, PIT certificates; 15 services (VAT invoice, POS VAT invoice, POS sale invoice, invoice management, webhook, OTP); Multi-client registry with OAuth2 token auto-refresh (5min buffer); Request/response via VIIAPIRequestHelper; Test endpoint: https://invoice-api.vnpaytest.vn, Production: https://api.vnpayinvoice.vn | VNPAY Invoice |
| t-van | @nx/t-van | Vietnam Tax VAN integration | Query electronic invoices from Tax Authority (CQT); Tax payer info lookup by tax code/ID (4 query types: 00001 Citizen ID, 00003 Tax Code, 00130 ID Tax Payer, 00132 Personal Tax Code); Multi-client registry; Auto-batch splitting for large queries (VNPAY max 10 tax codes/request) | CQT (via Viettel, VNPT T-VAN providers) |
Infrastructure
Data Stores
| Service | Port | Database/Index | Purpose | Notes |
|---|---|---|---|---|
| PostgreSQL | 5432 | N/A | Relational database | 7 schemas, 55 models |
| Redis | 6379 | DB0 | Cache | General-purpose caching |
| Redis | 6379 | DB1 | BullMQ Queues | 24 queues (4 systems × 3 partitions × 2 types) |
| Redis | 6379 | DB2 | Pub/Sub Subscriber | Event subscription channel |
| Redis | 6379 | DB3 | Pub/Sub Publisher | Event publishing channel |
| Redis | 6379 | DB4 | WebSocket State | Signal service cross-instance state |
| Typesense | 8108 | N/A | Search engine | 6 collections: products, merchants, categories, organizers, devices, sale-channels |
| Minio | 9000 | N/A | Object storage | S3-compatible API, presigned URLs |
PostgreSQL Database Schemas
| Schema | Model Count | Description | Example Models |
|---|---|---|---|
public | 30 | Core business entities | User, Role, Product, ProductInfo, ProductIdentifier, ProductVariant, Merchant, Organizer, Category, Device, Terminal, SaleChannel, Configuration, Employee, Vendor |
pricing | 7 | Pricing engine | Fare, FareRule, Cost, Tax |
allocation | 4 | Event seating & venues | Venue, Section, Seat, Allocation |
inventory | 8 | Stock management | Inventory, PurchaseOrder, PurchaseOrderItem, InventoryTracking, InventoryTrackingType |
sale | 2 | Sales orders | SaleOrder, SaleOrderItem |
finance | 3 | Financial records | FinanceWallet, FinanceCategory, FinanceTransaction |
payment | 1 | Payment configuration | WebhookConfig |
| Total | 55 | 7 schemas |
BullMQ Queue Systems
| System | Queue Types | Partitions | Total Queues | Purpose |
|---|---|---|---|---|
| Commerce | 2 | 3 | 6 | Product sync, catalog updates |
| Finance | 2 | 3 | 6 | Transaction processing, reconciliation |
| Inventory | 2 | 3 | 6 | Stock updates, PO processing |
| Sale | 2 | 3 | 6 | Order fulfillment, payment updates |
| Total | 8 | 3 | 24 |
Note: Each queue type has 3 partitions (P01, P02, P03) for load distribution. Default: 10 concurrent jobs per partition.
Event-Driven Architecture
Redis Pub/Sub Channels
| Channel | Event Type | Publisher | Subscribers | Payload |
|---|---|---|---|---|
UserOnboardingEventChannels.COMMERCE_INITIALIZED | Commerce Initialized | @nx/commerce | @nx/finance | { merchantId: string, organizerId: string } |
PaymentEventChannels.PAYMENT_SUCCESS | Payment Success | @nx/mq-pay | @nx/sale, @nx/finance | { transactionId: string, amount: number, currency: string, referenceId: string, referenceType: string } |
UserOnboardingEventChannels.SELLER_REGISTERED | Seller Registered | @nx/identity | N/A | { userId: string, email: string, roles: string[] } |
MQ-Pay Event System
| Event Type | Trigger | Payload | Subscribers |
|---|---|---|---|
mq-pay:transaction.created | Transaction created | { transactionId, amount, currency, provider } | @nx/sale |
mq-pay:transaction.settled | Payment settled | { transactionId, settlementDate } | @nx/sale, @nx/finance |
mq-pay:transaction.cancelled | Payment cancelled | { transactionId, reason } | @nx/sale |
mq-pay:attempt.created | Payment attempt created | { attemptId, transactionId, type } | @nx/sale |
mq-pay:attempt.sent | Payment request sent | { attemptId, provider, sentAt } | @nx/sale |
mq-pay:attempt.success | Payment successful | { attemptId, transactionId, paidAt } | @nx/sale, @nx/finance |
mq-pay:attempt.failed | Payment failed | { attemptId, errorCode, errorMessage } | @nx/sale |
mq-pay:attempt.expired | Payment expired | { attemptId, expiredAt } | @nx/sale |
mq-pay:refund.success | Refund successful | { refundId, transactionId, amount } | @nx/sale, @nx/finance |
mq-pay:refund.failed | Refund failed | { refundId, errorCode } | @nx/sale |
Security
| Layer | Mechanism | Implementation |
|---|---|---|
| Authentication | JWT + Basic Auth | JWT tokens with configurable expiry (default: 3600s), Basic auth with bcrypt password hashing (cost factor: 6.0.0) |
| Authorization | RBAC | 5 default roles: SUPER_ADMIN (999), ADMIN (998), OPERATOR (997), ORGANIZER_OWNER (899), EMPLOYEE (898). Role-based middleware via @authenticate decorator |
| WebSocket Encryption | ECDH P-256 + AES-256-GCM | Mandatory key exchange on handshake, per-client shared keys, encrypted message envelopes { event: 'encrypted', data: { iv, ct } } |
| Credential Storage | AES-256-GCM Encryption | Payment credentials encrypted in Configuration table using CryptoUtility, decrypted on-the-fly via PaymentConfigurationService |
| Password Hashing | bcrypt | Adaptive hashing function with salt rounds (bcrypt 6.0.0) |
| CORS | Hono CORS Middleware | Configured in DefaultApplication with environment-based allowed origins |
| Rate Limiting | Email Verification | 60s cooldown, 5 resends/day, 15min lockout after 3 failed attempts |
Development Setup
Prerequisites
| Tool | Version | Purpose |
|---|---|---|
| Bun | >=1.3.8 | Runtime, package manager, build tool |
| PostgreSQL | 14+ | Primary database |
| Redis | 6+ | Cache, queues, pub/sub, WebSocket state |
| Typesense | Latest | Search engine (optional for search features) |
| Minio | Latest | Object storage (optional for asset features) |
| Node.js | 18+ | Tauri tooling, some frontend build tools |
| Rust | Latest | Tauri backend compilation (sale-main) |
Quick Start
bash
# Clone repository
git clone https://git.nexpando.com/bana/bana.git
cd bana
# Install dependencies
make install # or: bun install
# Set up environment variables
cd packages/core # or any package
cp .env.example .env.development
# Build all packages (respects dependency order)
make build # Builds: core → asset,search,identity,inventory,finance,sale,signal → commerce → payment
# Run database migrations (from any package directory)
cd packages/identity
bun run migrate:dev
# Start development server (from package directory)
bun run server:dev # Uses .env.developmentBuild Commands
bash
# Root-level build (respects dependency order)
make build # Build all: third-parties → packages
make build-packages # Build only packages/*
make build-3rd # Build only third-parties/*
# Individual package builds
make core # @nx/core (build first)
make identity # @nx/identity
make sale-order # @nx/sale
make commerce # @nx/commerce (requires asset, search, inventory)
make inventory # @nx/inventory
make finance # @nx/finance
make payment # @nx/payment (requires mq-pay)
make signal # @nx/signal
make mq-pay # @nx/mq-pay
make iiapi # @nx/iiapi
make t-van # @nx/t-van
# Per-package build (inside package directory)
cd packages/sale
bun run rebuild # ALWAYS use this (clean + tsc + tsc-alias)
# NEVER run `tsc` directly (path aliases won't resolve)Development Servers
bash
# Backend services
make dev-sale-order # Start @nx/sale (http://localhost:3003)
make dev-commerce # Start @nx/commerce (http://localhost:3002)
make dev-identity # Start @nx/identity (http://localhost:3001)
make dev-finance # Start @nx/finance
make dev-payment # Start @nx/payment
make dev-signal # Start @nx/signal (WebSocket)
# Frontend apps
make dev-client # Start admin dashboard (Vite HMR)
make dev-bo # Start back office (Vite HMR)
make dev-wiki # Start VitePress docs (http://localhost:5173)
# Tauri desktop app
cd apps/sale-renderer
bunx tauri dev # Start POS desktop app (hot reload)Linting & Testing
bash
# Linting (root level)
make lint # Lint everything
make lint-packages # Lint all packages/*
make lint-3rd # Lint all third-parties/*
make lint-sale-order # Lint @nx/sale
make lint-finance # Lint @nx/finance
make lint-mq-pay # Lint @nx/mq-pay
# Linting (per package)
cd packages/sale
bun run lint:fix # ESLint + Prettier auto-fix
# Testing (per package)
cd packages/sale
bun run rebuild # Build first
bun run test # Run all tests (requires .env.test)
bun run test:watch # Watch mode
# Single test file
bun test --env-file=.env.test dist/__tests__/path/to/file.test.jsGit Hooks
bash
# Set up git hooks (run once)
make setup-tools # Configure git to use .githooks directory
# Pre-commit hook runs automatically on `git commit`
make pre-commit # Run manually: lints all packagesEnvironment Configuration
dotenv-flow Strategy
Each package uses dotenv-flow for environment-specific configuration:
| File | Purpose | Git Tracked |
|---|---|---|
.env.example | Template with all variables | Yes |
.env.development | Development settings | No (copy from example) |
.env.test | Test environment | No (copy from example) |
.env.local | Local overrides (highest priority) | No |
Core Environment Variables
bash
# Application
APP_ENV_NODE_ENV=development
APP_ENV_APPLICATION_SECRET=<secret-key-32-chars>
APP_ENV_JWT_SECRET=<jwt-secret-key>
APP_ENV_JWT_EXPIRES_IN=3600
# PostgreSQL
APP_ENV_POSTGRES_HOST=localhost
APP_ENV_POSTGRES_PORT=5432
APP_ENV_POSTGRES_DATABASE=bana
APP_ENV_POSTGRES_USERNAME=postgres
APP_ENV_POSTGRES_PASSWORD=password
# Redis - Cache (DB0)
APP_ENV_CACHE_REDIS_HOST=localhost
APP_ENV_CACHE_REDIS_PORT=6379
APP_ENV_CACHE_REDIS_DB=0
APP_ENV_CACHE_REDIS_PASSWORD=
# Redis - Queue (DB1)
APP_ENV_QUEUE_REDIS_HOST=localhost
APP_ENV_QUEUE_REDIS_PORT=6379
APP_ENV_QUEUE_REDIS_DB=1
APP_ENV_QUEUE_REDIS_PASSWORD=
# Redis - WebSocket (DB4)
APP_ENV_WEBSOCKET_REDIS_MODE=single # or: cluster
APP_ENV_WEBSOCKET_REDIS_HOST=localhost
APP_ENV_WEBSOCKET_REDIS_PORT=6379
APP_ENV_WEBSOCKET_REDIS_DB=4
APP_ENV_WEBSOCKET_REDIS_PASSWORD=
APP_ENV_WEBSOCKET_REDIS_MAX_RETRY=5
APP_ENV_WEBSOCKET_ECDH_INFO=<HKDF-info-string>
# Typesense
APP_ENV_TYPESENSE_API_KEY=<api-key>
APP_ENV_TYPESENSE_NODES=http:localhost:8108 # format: protocol:host:port, comma-separated
# Minio
APP_ENV_MINIO_HOST=localhost
APP_ENV_MINIO_API_PORT=9000
APP_ENV_MINIO_CONSOLE_PORT=9001
APP_ENV_MINIO_ACCESS_KEY=minioadmin
APP_ENV_MINIO_SECRET_KEY=minioadmin
APP_ENV_MINIO_USE_SSL=false
# Snowflake ID Generator
APP_ENV_SNOWFLAKE_WORKER_ID=1
APP_ENV_SNOWFLAKE_EPOCH_CHECKPOINT=1704067200000 # 2024-01-01 00:00:00 UTC
# Cross-Service URLs
APP_ENV_IDENTITY_SERVICE_URL=http://localhost:3001
APP_ENV_COMMERCE_SERVICE_URL=http://localhost:3002
APP_ENV_SALE_SERVICE_URL=http://localhost:3003
# VNPAY (via mq-pay)
APP_ENV_VNPAY_TVAN_API_KEY=<api-key>
# Email (for identity email verification)
APP_ENV_SMTP_HOST=smtp.gmail.com
APP_ENV_SMTP_PORT=587
APP_ENV_SMTP_USER=<email@example.com>
APP_ENV_SMTP_PASSWORD=<app-password>Project Structure
bana/
├── apps/ # Frontend applications
│ ├── client/ # Admin dashboard (React 19, Vite 7, TailwindCSS 4)
│ │ ├── src/screens/ # 28 screen modules (products, orders, finance, users, etc.)
│ │ ├── src/layouts/ # Layout components
│ │ └── package.json
│ ├── bo/ # Back office (simplified admin)
│ │ ├── src/screens/ # 5 screens (home, merchant, organizer, sign-in, errors)
│ │ └── package.json
│ ├── sale-renderer/ # POS desktop UI (Tauri WebView)
│ │ ├── src/ # React app
│ │ └── package.json
│ ├── sale-main/ # POS desktop backend (Tauri Rust)
│ │ ├── src-tauri/ # Rust source
│ │ ├── src-tauri/tauri-plugin-*/ # Custom plugins (USB, Payment, Display, Machine UID)
│ │ └── package.json
│ ├── admin-ui-kit/ # Shared UI library (Radix UI + shadcn)
│ │ ├── components/shadcn/ # 38 shadcn components
│ │ ├── components/core/ # Custom core components
│ │ ├── components/icons/ # Icon components
│ │ └── package.json
│ └── core/ # Shared app core (constants, hooks, locales, utilities)
│ └── package.json
│
├── packages/ # Backend microservices (IGNIS-based)
│ ├── core/ # Foundation package
│ │ ├── src/default-application.ts # Base application class
│ │ ├── src/repositories/soft-deletable.repository.ts
│ │ ├── src/utilities/id-generator.utility.ts
│ │ ├── src/helpers/bootstraps/ # bootstrapApplication, bootstrapMigration
│ │ ├── src/models/schemas/ # 7 PostgreSQL schemas (55 models)
│ │ │ ├── public/ # 30 models
│ │ │ ├── pricing/ # 7 models
│ │ │ ├── allocation/ # 4 models
│ │ │ ├── inventory/ # 8 models
│ │ │ ├── sale/ # 2 models
│ │ │ ├── finance/ # 3 models
│ │ │ └── payment/ # 1 model
│ │ └── package.json
│ ├── identity/ # Auth, users, RBAC (port 3001)
│ │ ├── src/application.ts
│ │ ├── src/components/
│ │ ├── src/services/
│ │ └── package.json
│ ├── commerce/ # Products, pricing (port 3002)
│ ├── sale/ # Orders, checkout (port 3003)
│ ├── inventory/ # Stock, purchase orders
│ ├── search/ # Typesense integration
│ ├── asset/ # Minio storage
│ ├── invoice/ # E-Invoice generation
│ ├── finance/ # Wallets, transactions
│ ├── payment/ # Payment orchestration
│ └── signal/ # WebSocket service
│
├── third-parties/ # External integrations
│ ├── mq-pay/ # Multi-provider payments
│ │ ├── src/component.ts # MQPayComponent
│ │ ├── src/services/payment/ # Payment execution, cancellation, refund, verification
│ │ ├── src/helpers/app-registry.helper.ts # Singleton registry
│ │ └── package.json
│ ├── iiapi/ # VNPAY E-Invoice API
│ │ ├── src/iiapi.component.ts
│ │ └── package.json
│ └── t-van/ # Tax VAN connector
│ ├── src/t-van.component.ts
│ └── package.json
│
├── docs/wiki/ # VitePress documentation
│ ├── content/en/developer/ # English docs
│ └── content/vi/developer/ # Vietnamese docs
│
├── infrastructure/ # Infrastructure & deployment configs
│ └── deployments/
│ └── develop/ # Docker Compose configs per service
│
├── Makefile # Build automation
├── package.json # Root package (workspaces, overrides)
├── tsconfig.json # TypeScript config
├── .githooks/ # Git hooks (pre-commit)
└── AGENTS.md # AI agent instructions (project rules, architecture)IGNIS Framework Patterns
Dependency Injection
typescript
import { inject, BaseService, BindingKeys, BindingNamespaces } from '@venizia/ignis';
export class MyService extends BaseService {
constructor(
// String key pattern
@inject({ key: 'repositories.UserRepository' })
private userRepository: UserRepository,
// Or with BindingKeys helper
@inject({ key: BindingKeys.build({ namespace: BindingNamespaces.REPOSITORY, key: 'ProductRepository' }) })
private productRepository: ProductRepository,
) {
super({ scope: MyService.name });
}
async findUser(id: string): Promise<User> {
return this.userRepository.findById({ id });
}
}Component Structure
typescript
import { BaseComponent, inject, CoreBindings, BaseApplication } from '@venizia/ignis';
export class MyComponent extends BaseComponent {
constructor(
@inject({ key: CoreBindings.APPLICATION_INSTANCE })
protected application: BaseApplication,
) {
super({
scope: MyComponent.name,
initDefault: { enable: true, container: application },
bindings: {},
});
}
override async binding(): Promise<void> {
// Register repositories, services, controllers in correct order
this.application.repository(MyRepository);
this.application.service(MyService);
this.application.controller(MyController);
}
}Application Lifecycle
typescript
import { DefaultApplication } from '@nx/core';
export class Application extends DefaultApplication {
override preConfigure(): void {
// Execute BEFORE boot phase
this.configureServices();
this.configureComponents();
this.configureSecurity();
}
override configureComponents(): void {
super.configureComponents(); // Call parent to register core components
this.component(MyComponent); // Register custom components
}
override async postConfigure(): Promise<void> {
// Execute AFTER all components are bound
await this.initializeExternalServices();
}
}Bootstrap Helpers (from @nx/core)
typescript
// packages/*/src/index.ts - Application entry point
import { bootstrapApplication } from '@nx/core';
import { Application } from './application.js';
import { appConfig } from './common/app-config.js';
import { resolve } from 'node:path';
bootstrapApplication({
ApplicationClass: Application,
config: appConfig,
options: { bannerPath: resolve(__dirname, '../resources/banner.txt') },
});
// packages/*/src/migrate.ts - Migration entry point
import { bootstrapMigration } from '@nx/core';
import { Application } from './application.js';
import { getMigrationProcesses } from './migrations/processes/migration-process.js';
bootstrapMigration({
ApplicationClass: Application,
getMigrationProcesses,
});
// packages/*/src/common/app-config.ts - Centralized config
import { createAppConfig } from '@nx/core';
export const appConfig = createAppConfig();
// packages/*/src/migrations/processes/migration-process.ts
import { createMigrationProcessLoader } from '@nx/core';
export const getMigrationProcesses = createMigrationProcessLoader({
seedPaths: ['my-package-0001-seed-data', 'my-package-0002-seed-roles'],
importFn: path => import(`../processes/${path}.js`),
});Controller Factory (Auto CRUD)
typescript
import { controller } from '@venizia/ignis';
import { ControllerFactory } from '@venizia/ignis';
import { inject } from '@venizia/ignis';
import { MyRepository } from '../repositories/my.repository.js';
import { MyEntity } from '../models/entities/my.entity.js';
@controller({ path: '/my-entities' })
export class MyController extends ControllerFactory.defineCrudController({
repository: { name: MyRepository.name },
authenticate: { strategies: ['jwt', 'basic'] }, // Optional: require auth
controller: { name: 'MyController', basePath: '/my-entities' },
entity: () => MyEntity,
}) {
constructor(
@inject({ key: 'repositories.MyRepository' })
repository: MyRepository,
) {
super(repository);
}
// Auto-generated routes:
// GET /my-entities - List with pagination, filtering, sorting
// GET /my-entities/:id - Get by ID
// POST /my-entities - Create
// PATCH /my-entities/:id - Update
// DELETE /my-entities/:id - Delete (soft-delete if using SoftDeletableRepository)
// Override or add custom routes as needed
}Drizzle ORM Patterns
typescript
// Schema definition
import { pgTable, uuid, text, timestamp, decimal, integer } from 'drizzle-orm/pg-core';
export const products = pgTable('products', {
id: uuid('id').primaryKey().defaultRandom(),
name: text('name').notNull(),
price: decimal('price', { precision: 10, scale: 4 }).notNull(),
quantity: integer('quantity').default(0),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
modifiedAt: timestamp('modified_at', { withTimezone: true }).defaultNow(),
deletedAt: timestamp('deleted_at', { withTimezone: true }), // For soft-delete
});
// Repository with soft-delete
import { repository } from '@venizia/ignis';
import { SoftDeletableRepository } from '@nx/core';
import { PostgresCoreDataSource } from '@/datasources';
import { Product } from '../models/entities/product.entity.js';
@repository({ dataSource: PostgresCoreDataSource, model: Product })
export class ProductRepository extends SoftDeletableRepository<typeof products, Product, TProductPersist> {
// Built-in methods:
// - deleteById (sets deletedAt, not physical delete)
// - restoreById (clears deletedAt)
// - findById (excludes soft-deleted by default)
// - find (excludes soft-deleted by default)
}
// Transactions
await this.productRepository.dataSource.withTransaction(async tx => {
await this.productRepository.create({ data: productData, options: { transaction: tx } });
await this.inventoryRepository.create({ data: inventoryData, options: { transaction: tx } });
// Both succeed or both rollback
});Zod Validation with Drizzle
typescript
import { createSelectSchema, createInsertSchema } from 'drizzle-zod';
import { products } from '../schemas/product.schema.js';
// Auto-generate Zod schemas from Drizzle tables
export const ProductSelectSchema = createSelectSchema(products);
export const ProductInsertSchema = createInsertSchema(products);
// Custom validation
import { z } from 'zod';
export const CreateProductSchema = ProductInsertSchema.omit({ id: true, createdAt: true, modifiedAt: true }).extend({
name: z.string().min(2).max(255),
price: z.number().positive().max(9999999.9999),
quantity: z.number().int().nonnegative(),
});
export type CreateProductInput = z.infer<typeof CreateProductSchema>;Quick Links
Backend Packages
| Package | Documentation | Description |
|---|---|---|
| Core | Documentation | Shared foundation: base classes, 7 DB schemas, utilities |
| Identity | Documentation | Authentication, RBAC, email verification |
| Commerce | Documentation | Products, pricing engine, merchants |
| Sale | Documentation | Orders, checkout, MQ-Pay integration |
| Payment | Documentation | Payment orchestration, webhook dispatch |
| Signal | Documentation | WebSocket communication, ECDH encryption |
| Asset | Documentation | File storage, Minio S3 |
| Invoice | Documentation | E-Invoice generation, PDF |
| Gateway | Documentation | API Gateway (planned) |
Third-Party Integrations
| Integration | Documentation | Description |
|---|---|---|
| MQ-Pay | Documentation | Multi-provider payment system (VNPAY, Cash) |
| IIAPI | Documentation | VNPAY E-Invoice integration |
| T-VAN | Documentation | Vietnam Tax VAN connector |
Frontend Applications
| Application | Documentation | Description |
|---|---|---|
| Client | Documentation | Admin dashboard (28 screens) |
| POS Desktop | Renderer / Main | Tauri desktop app |
| Admin UI Kit | Documentation | Shared component library |
Next Steps
For New Developers
- Set up local environment: Install prerequisites (Bun, PostgreSQL, Redis)
- Build the monorepo: Run
make install && make build - Explore Core package: Read Core Documentation to understand shared infrastructure
- Run a service: Start Identity service with
make dev-identity, explore Swagger athttp://localhost:3001/docs - Read IGNIS patterns: Study dependency injection, component structure, controller factory examples above
For Integration Developers
- Review MQ-Pay integration: MQ-Pay Documentation for payment flow
- Understand event system: Study Redis Pub/Sub channels and BullMQ queue architecture
- Implement webhooks: Use
@nx/paymentwebhook dispatcher with exponential backoff retry - Test with sandbox: Use VNPAY test endpoints from
@nx/iiapiand@nx/t-vanintegrations
For Frontend Developers
- Set up client app: Run
make dev-clientand explore 28 screen modules - Use Admin UI Kit: Import components from
@nx-app/admin-ui-kit(38 shadcn components) - Integrate with backend: Use TanStack Query for API calls, Redux Toolkit for state management
- Build POS desktop app: Explore Tauri architecture in
sale-mainandsale-renderer
For DevOps Engineers
- Review infrastructure: PostgreSQL 7 schemas, Redis 5 databases, Typesense, Minio
- Set up Docker Compose: Use
infrastructure/deployments/develop/for local stack - Configure environments: Study
.env.examplefiles across packages - Monitor queues: Set up BullMQ dashboard for 24 queue monitoring
- Deploy services: Each package has Dockerfile, deploy independently or as monolith