Skip to content

Backend Packages Overview

This page documents the complete backend architecture of the BANA POS system. It covers all 15 packages in the packages/ directory, the shared PostgreSQL database with 10 schemas, event-driven infrastructure (Kafka + Redis Pub/Sub + BullMQ), security mechanisms, and the IGNIS framework patterns that unify the entire codebase.

Technology Stack

Core Technologies

TechnologyVersionPurpose
Bun>=1.3.8JavaScript/TypeScript runtime
TypeScript~5.9.3Static type system
IGNIS Framework0.0.7-7IoC container, DI, application lifecycle
IGNIS Helpers0.0.6-7Framework utility extensions
Hono4.11.3Lightweight HTTP framework
Drizzle ORM^0.45.1Type-safe SQL ORM
Drizzle Kit^0.31.8Schema migrations CLI
Drizzle Zod^0.8.3Drizzle-to-Zod schema bridge
Zod4.1.13Runtime schema validation
PostgreSQL16+Primary relational database
Redis7+Caching, Pub/Sub, BullMQ

API and Documentation

TechnologyVersionPurpose
@hono/zod-openapi1.2.0OpenAPI route definitions
@scalar/hono-api-reference^0.9.28Interactive API explorer UI

Supporting Libraries

TechnologyVersionPurpose
pg^8.16.3PostgreSQL client driver
bcrypt^6.0.0Password hashing (Identity)
dayjs^1.11.19Date/time manipulation
BullMQ^5.14.3Distributed job queues
dotenv-flow^4.1.0Environment variable management
nodemailer^7.0.12Transactional email

System Topology

All 15 Backend Packages

PackagePortRoleKey ResponsibilitiesPrimary Dependencies
@nx/core--FoundationBase classes, DB models across 10 schemas, utilities, auth config--
@nx/identity31010ServiceAuthentication, authorization, user management, RBACcore
@nx/commerce31020ServiceProducts, pricing engine, merchants, categories, CDCcore, asset, inventory, search
@nx/sale31030ServiceOrder lifecycle, checkout, kitchen orders, payment orchestrationcore, mq-pay
@nx/inventory31050ServiceStock tracking, purchase orders, vendor managementcore
@nx/finance31040ServiceWallets, income/expense tracking, financial categoriescore
@nx/pricing31070ServiceFare sets, tax engine, promotions, pricing rulescore
@nx/payment31080ServiceWebhook config, credential loading, payment dispatchcore, mq-pay
@nx/ledger31060ServiceHKD accounting ledger generation (PDF/XLSX), encryption, S3core
@nx/outreach31110ServiceNewsletter subscriptions, contact form inquiriescore
@nx/licensing31120ServicePolicy-based license management, certificate signing, validation pipelinecore
@nx/helpdesk31130ServiceMulti-channel customer support — tickets, SLA, auto-assignment, knowledge base, surveyscore
@nx/signal31090ServiceWebSocket with ECDH E2E encryption, Redis Pub/Subcore
@nx/invoice--ServiceE-invoice generation (IIAPI + T-VAN)core, iiapi, t-van
@nx/search--LibraryTypesense integration, filter conversion, CDC synccore
@nx/asset--LibraryMinio/disk file storage, MetaLink managementcore

Packages marked -- for Port operate as embedded libraries or event-driven workers rather than standalone HTTP services.

Package Architecture

Foundation Layer
🏗️
@nx/core
Base classes, 55 DB models across 7 schemas, utilities, auth strategies, bootstrap helpers
Shared Libraries — Embedded into services, no standalone HTTP server
📦
@nx/asset
Minio & disk file storage
🔍
@nx/search
Typesense search integration
📊
@nx/inventory
Stock tracking & purchase orders
Standalone Services — HTTP servers and/or event-driven workers
🔐
@nx/identity
Authentication & RBAC
🛒
@nx/commerce
Products & pricing engine
🧾
@nx/sale
Orders & checkout
💰
@nx/finance
Wallets & income/expense
💳
@nx/payment
Webhooks & credential dispatch
📡
@nx/signal
WebSocket & E2E encryption
📄
@nx/invoice
E-invoices (IIAPI & T-VAN)
🏷️
@nx/pricing
Fare sets, tax & promotions
📒
@nx/ledger
HKD ledger generation
📨
@nx/outreach
Subscribers & inquiries

Build Dependency Graph

The Makefile enforces this build order. A package cannot be built until all of its dependencies have been compiled.

Runtime Dependency Graph


Per-Package Reference

@nx/core -- Foundation

Purpose: Shared kernel providing base classes, all 55 database models across 7 PostgreSQL schemas, cross-cutting utilities, and authentication configuration. Every other package depends on @nx/core.

Key Exports:

ExportTypeDescription
DefaultApplicationClassBase application with auth, CORS, Swagger, health checks
SoftDeletableRepositoryClassRepository with soft-delete (sets deletedAt instead of physical delete)
PostgresCoreDataSourceDataSourceShared Drizzle + node-postgres data source
MigrationRepositoryRepositoryTracks migration status in the database
IdGeneratorUtilitySnowflake ID generation singleton
IdentityNetworkServiceServiceCross-service HTTP calls to the Identity service
useRequestContext()FunctionExtracts authenticated user, roles, and response normalizers
CryptoUtilityUtilityAES-256-GCM encryption/decryption for credentials
@loggedDecoratorPerformance measurement logging
bootstrapApplication()HelperApplication entry point factory
bootstrapMigration()HelperMigration entry point factory
createAppConfig()HelperCentralized application configuration builder
createMigrationProcessLoader()HelperDynamic migration process importer

Constants:

ConstantValues
FixedUserRolesSUPER_ADMIN (999), ADMIN (998), OPERATOR (997), ORGANIZER_OWNER (899), EMPLOYEE (898)
MerchantTypesDEFAULT, TICKET, FNB, THEATER
PaymentProvidersVNPAY_QR_MMS, SYSTEM
InvoiceProvidersT_VAN, IIAPI

TIP

Detailed documentation: @nx/core


@nx/identity -- Authentication and Authorization

Purpose: User authentication, authorization, RBAC, and employee management. Runs as a standalone HTTP service on port 3001.

Services:

ServiceResponsibility
AuthenticationServiceSign-in, sign-up, password change
UserServiceAtomic user creation (user + identifiers + profile + roles)
EmployeeServiceEmployee management with organizer/merchant mapping
MailVerificationServiceEmail verification with 6-digit codes and tokens

Authentication Strategies: JWT, Basic (via IGNIS AuthenticateComponent)

Identifier Schemes: USERNAME, EMAIL, PHONE_NUMBER, USER_NUMBER, NX_AUTH

Email Verification Limits:

  • 6-digit code: 10-minute expiry, 3 attempts max
  • 32-byte token: 24-hour expiry
  • Rate limit: 60-second cooldown, 5 resends/day, 15-minute lockout

TIP

Detailed documentation: @nx/identity


@nx/commerce -- Product Catalog and Pricing

Purpose: Product management, dynamic pricing engine, merchant onboarding, and category system. Runs on port 3002. Integrates asset storage, inventory tracking, and Typesense search.

Services:

ServiceResponsibility
ProductServiceAggregate creation (info, identifiers, variants)
ProductVariantServiceVariant management with pricing integration
FareServiceStatic/dynamic pricing with context-based rule evaluation
MerchantServiceMerchant onboarding with categories and sale channels

Pricing Engine:

ConceptDescription
FarePrice point with amount, status, time-based activation
FareRuleDynamic condition (quantity, date, custom context)
Rule OperatorsEQ, NE, GT, GTE, LT, LTE, IN, NIN
Rule TypesOVERRIDE (stops evaluation), DISCOUNT, MARKUP

Integrated Components: ApplicationAssetComponent, ApplicationInventoryComponent, ApplicationSearchComponent, NxTVanComponent

TIP

Detailed documentation: @nx/commerce


@nx/sale -- Order Management

Purpose: Sale order lifecycle from cart to completion, checkout validation, and payment integration via MQ-Pay. Runs on port 3003.

Order Lifecycle:

DRAFT
PROCESSING
PARTIAL
COMPLETED

Services:

ServiceResponsibility
SaleOrderServiceCreate orders, add items, cancel orders
CheckoutServiceDRAFT to PROCESSING transitions, validation
SaleOrderItemServiceBatch item updates with auto-merge
PaymentWebhookServiceHandle MQ-Pay payment status callbacks

Order Constraints: Max 9,999 per item quantity. Max 100 items per order.

Components: RedisComponent (cache + BullMQ + Pub/Sub), QueueComponent, ApplicationWebSocketComponent

TIP

Detailed documentation: @nx/sale


@nx/inventory -- Stock Management

Purpose: Multi-location inventory tracking, purchase order processing, and audit trails. Operates as an embedded library consumed by @nx/commerce.

Services:

ServiceResponsibility
InventoryServiceCreate/update inventory for product variants
PurchaseOrderServicePO workflow: DRAFT to PROCESSING to CONFIRMED to COMPLETED
PurchaseOrderItemServicePO line item management

Stock Quantities:

FieldMeaning
quantityOnHandTotal physical stock
quantityAvailableOn-hand minus reserved (available for sale)
quantityReservedAllocated for pending orders

Tracking Types (20 predefined):

  • Inbound: STOCK_IN, PURCHASE, TRANSFER_IN, RETURN_FROM_CUSTOMER, ADJUSTMENT_IN
  • Outbound: STOCK_OUT, SALE, TRANSFER_OUT, RETURN_TO_VENDOR, EXPIRED, LOST, DAMAGED
  • Neutral: INVENTORY_COUNT, ADJUSTMENT_NEUTRAL

Purpose: Real-time search powered by Typesense with automatic IGNIS filter-to-Typesense query conversion. Data flows from PostgreSQL via Debezium CDC.

Collections: products, organizers, merchants, categories, devices, sale-channels

Services:

ServiceResponsibility
SearchServiceCore search with filter conversion
BaseTypesenseSearchServiceAbstract base for custom search implementations

TypesenseConverter Operators: eq, neq, gt, gte, lt, lte, between, inq (IN), nin (NOT IN), and, or

Environment Variables: APP_ENV_TYPESENSE_API_KEY, APP_ENV_TYPESENSE_NODES (format: protocol:host:port, comma-separated)


@nx/asset -- Media Storage

Purpose: Dual-backend file storage (Minio S3-compatible + local disk) with metadata tracking via the MetaLink entity.

Storage Configuration:

BackendEndpointUse Case
Minio/assetsUploaded media (images, documents)
Local disk/resourcesStatic resources (templates, banners)

MetaLink Fields: bucketName, objectName, link (presigned URL), mimetype, size, etag, storageType, principalId, principalType

TIP

Detailed documentation: @nx/asset


@nx/finance -- Financial Tracking

Purpose: Income/expense tracking with wallet management. Event-driven -- listens to commerce and payment events to automatically create financial records.

Services:

ServiceResponsibility
FinanceWorkerServiceEvent and queue handler for all financial operations

FinanceWorkerService Methods:

MethodTriggerAction
handleCommerceInitialized()COMMERCE_INITIALIZED eventCreates default Cash wallet for new merchant
handlePaymentSuccess()PAYMENT_SUCCESS eventCreates INCOME transaction
handlePurchaseOrderReceived()PURCHASE_ORDER_RECEIVED queue jobCreates EXPENSE transaction

Wallet Types: CASH, BANK, EWALLET, CREDIT_CARD

Transaction Types: INCOME, EXPENSE, TRANSFER

Controllers:

ControllerPathType
FinanceWalletController/finance-walletsControllerFactory CRUD
FinanceCategoryController/finance-categoriesControllerFactory CRUD
FinanceTransactionController/finance-transactionsControllerFactory CRUD

@nx/payment -- Payment Orchestration

Purpose: Bridge layer between MQ-Pay and the application. Manages webhook configurations, loads and decrypts payment credentials, and dispatches webhook events with retry logic.

Services:

ServiceResponsibility
PaymentConfigurationServiceFetch and decrypt payment configs from Configuration table
WebhookDispatcherServiceFire-and-forget webhook dispatch with exponential backoff

Controllers:

ControllerPathType
WebhookConfigController/webhook-configsControllerFactory CRUD

Supported Providers: VNPAY_QR_MMS, VNPAY_PHONE_POS

Migration Seeds: payment-0001-seed-vnpay-qr-mms-configuration, payment-0002-seed-vnpay-phone-pos-configuration

TIP

Detailed documentation: @nx/payment


@nx/signal -- WebSocket Service

Purpose: Centralized real-time communication with end-to-end encryption. Stateless (no database, no migrations). Uses Redis Pub/Sub for cross-instance message delivery.

Services:

MethodDescription
broadcast({ topic, data })Send to all connected clients across all instances
sendToRoom({ room, topic, data })Send to all clients in a room
sendToClient({ clientId, topic, data })Send to a specific client (local or remote via Redis)
disconnectClient({ clientId })Force-close a client connection

REST API (base: /socket/websocket/clients):

MethodPathAuthDescription
GET/statusNoneServer readiness + client count
GET/JWT/BasicList connected clients
POST/broadcastJWT/BasicBroadcast to all clients
POST/rooms/:roomName/sendJWT/BasicSend to a room
POST/:clientId/sendJWT/BasicSend to specific client
POST/:clientId/disconnectJWT/BasicDisconnect a client

Encryption: ECDH P-256 key exchange, AES-256-GCM per-message encryption. WebSocket endpoint: /stream.

Redis Modes: Single instance (default) or Cluster mode via APP_ENV_WEBSOCKET_REDIS_MODE.

TIP

Detailed documentation: @nx/signal


@nx/invoice -- E-Invoice Generation

Purpose: Vietnam e-invoice integration through IIAPI (VNPAY viiAPI) and T-VAN providers. Supports VAT invoices, sales invoices, POS invoices, and more.

Dependencies: @nx/core, @nx/iiapi (third-party), @nx/t-van (third-party)

TIP

Detailed documentation: @nx/invoice


Layered Architecture

Every backend service follows the Controller-Service-Repository pattern enforced by the IGNIS framework.

Controller
Service
Repository
PostgreSQL
LayerResponsibilityIGNIS Base Class
ControllerHTTP transport, input validation via Zod, response formattingControllerFactory.defineCrudController() or custom
ServicePure business logic, orchestration, event handlingBaseService
RepositoryDatabase access abstraction, Drizzle ORM queriesSoftDeletableRepository or DefaultCRUDRepository
ComponentCross-cutting concerns: Redis, queues, external integrationsBaseComponent
DataSourceConnection pooling and Drizzle connector setupBaseDataSource

Request Flow

1. HTTP Request --> Controller (route handler)
2. Controller validates input with Zod schemas via @hono/zod-openapi
3. Controller calls Service layer via DI
4. Service executes business logic
5. Service calls Repository for data access
6. Repository executes Drizzle ORM queries against PostgreSQL
7. Response flows back through layers

Database Overview

All database models are defined in @nx/core under src/models/schemas/. Services share a single PostgresCoreDataSource connection.

7 PostgreSQL Schemas, 55 Models

SchemaModelsCountKey Entities
publicGeneral domain30User, UserCredential, UserProfile, Role, Permission, Product, ProductInfo, ProductIdentifier, ProductVariant, Merchant, Organizer, Category, Device, Terminal, SaleChannel, Configuration, Employee, Vendor, MetaLink, MerchantType, CategoryTemplate, etc.
pricingPricing engine7Fare, FareSet, FareRule, Cost, Tax, TaxSet, TaxType
allocationEvent seating4Venue, Section, Seat, Allocation
inventoryStock management8Inventory, PurchaseOrder, PurchaseOrderItem, InventoryTracking, InventoryTrackingType, etc.
saleOrders2SaleOrder, SaleOrderItem
financeFinancial tracking3FinanceWallet, FinanceCategory, FinanceTransaction
paymentWebhook config1WebhookConfig

Soft-Delete Pattern

All deletable entities use the SoftDeletableRepository from @nx/core. Instead of physical deletion, a deletedAt timestamp is set. Records can be restored with restoreById().

typescript
// Schema includes deletedAt column
deletedAt: (timestamp('deleted_at', { withTimezone: true }),
  // Repository extends SoftDeletableRepository
  @repository({ dataSource: PostgresCoreDataSource, model: Category })
  export class CategoryRepository extends SoftDeletableRepository<
    typeof Category.schema,
    TCategory,
    TCategoryPersist
  > {});

// deleteById sets deletedAt instead of removing the row
// restoreById sets deletedAt back to null
// Pass { shouldHardDelete: true } for physical deletion

Event-Driven Architecture

The system uses two complementary messaging patterns: Redis Pub/Sub for real-time event broadcasting and BullMQ for reliable asynchronous job processing.

Redis Pub/Sub Channels

ChannelPublisherSubscribersPurpose
PaymentSuccessPayment / SaleFinanceRecord income transactions on successful payment
SellerRegisteredIdentityCommerceInitialize merchant defaults on new seller signup
CommerceInitializedCommerceFinanceCreate default Cash wallet for new merchant

BullMQ Queue Systems

Each queue type uses 3 partitions (P01, P02, P03) for load distribution.

PackageQueue TypesPurpose
Commerce3 typesProduct indexing, category sync, merchant setup
Finance2 typesPURCHASE_ORDER_RECEIVED (expense recording), transaction processing
Inventory1 typeStock adjustment processing
Sale2 typesOrder expiration scheduling, payment confirmation
MQ-Pay2 types (scheduler, confirmation)Payment attempt expiration, IPN/webhook confirmation

Event Flow Example


Security

Authentication and Authorization

MechanismLocationDescription
JWT AuthenticationDefaultApplication.configureSecurity()Token-based auth for API access
Basic AuthenticationDefaultApplication.configureSecurity()Username/password via IdentityNetworkService
bcrypt Hashing@nx/identityPassword storage with bcrypt (^6.0.0)
Role-Based AccessAll controllers5 fixed roles with numeric precedence

Default Roles

RoleCodeScope
SUPER_ADMIN999Full system access
ADMIN998Administrative operations
OPERATOR997Operational management
ORGANIZER_OWNER899Organizer-scoped access
EMPLOYEE898Employee-scoped access

Encryption

MechanismAlgorithmUsage
WebSocket E2EECDH P-256 + AES-256-GCMSignal package -- per-client key derivation
Credential StorageAES-256-GCMCryptoUtility -- payment provider secrets
Password HashingbcryptIdentity package -- user credentials

IGNIS Framework Patterns

Application Lifecycle

Every service extends DefaultApplication which provides authentication, CORS, Swagger, and health check configuration out of the box.

typescript
// packages/sale/src/application.ts
import {
  createAppConfig,
  DefaultApplication,
  MigrationRepository,
  PostgresCoreDataSource,
} from '@nx/core';
import { CoreBindings, IApplicationInfo } from '@venizia/ignis';

export const appConfig = createAppConfig();

export class Application extends DefaultApplication {
  override getAppInfo(): IApplicationInfo {
    return {
      name: '@nx/sale',
      version: '0.0.0',
      description: 'Sale order management with payment integration',
      author: { name: 'Nexpando', email: 'contact@nexpando.com' },
    };
  }

  override getProjectRoot(): string {
    const projectRoot = __dirname;
    this.bind<string>({ key: CoreBindings.APPLICATION_PROJECT_ROOT }).toValue(projectRoot);
    return projectRoot;
  }

  override configureComponents(): void {
    super.configureComponents(); // HealthCheck + Swagger + Auth
    this.component(RedisComponent);
    this.component(QueueComponent);
  }

  override preConfigure(): void {
    super.preConfigure();
    this.dataSource(PostgresCoreDataSource);
    this.repository(SaleOrderRepository);
    this.service(SaleOrderService);
    this.service(CheckoutService);
    this.controller(SaleOrderController);
  }
}

Bootstrap Helpers

Application and migration entry points are standardized via @nx/core helper functions.

typescript
// packages/sale/src/index.ts -- Application entry point
import { bootstrapApplication } from '@nx/core';
import { resolve } from 'node:path';
import { appConfig, Application } from './application';

bootstrapApplication({
  ApplicationClass: Application,
  config: appConfig,
  options: { bannerPath: resolve(process.cwd(), 'resources/banner.txt') },
});
typescript
// packages/sale/src/migrations/processes/migration-process.ts
import { createMigrationProcessLoader } from '@nx/core';

export const getMigrationProcesses = createMigrationProcessLoader({
  seedPaths: ['sale-0001-seed-data', 'sale-0002-seed-channels'],
  importFn: path => import(`../processes/${path}.js`),
});

Dependency Injection

IGNIS uses constructor injection with the @inject() decorator. Binding keys follow the namespace.ClassName convention.

typescript
// Service with repository injection
import { BaseService, BindingKeys, BindingNamespaces, inject } from '@venizia/ignis';

export class CheckoutService extends BaseService {
  constructor(
    @inject({
      key: BindingKeys.build({
        namespace: BindingNamespaces.REPOSITORY,
        key: SaleOrderRepository.name,
      }),
    })
    private readonly _saleOrderRepository: SaleOrderRepository,

    @inject({
      key: BindingKeys.build({
        namespace: BindingNamespaces.SERVICE,
        key: SaleSocketEventService.name,
      }),
    })
    private readonly _saleSocketEventService: SaleSocketEventService,
  ) {
    super({ scope: CheckoutService.name });
  }
}

The BindingKeys.build() helper produces keys like repositories.SaleOrderRepository and services.SaleSocketEventService. You can also write binding keys as plain strings:

typescript
@inject({ key: 'repositories.SaleOrderRepository' })
private readonly saleOrderRepository: SaleOrderRepository,

Component Pattern

Components encapsulate cross-cutting concerns (Redis, queues, external services) and register bindings during the binding() lifecycle hook.

typescript
import { BaseApplication, BaseComponent, CoreBindings, inject, RedisHelper } from '@venizia/ignis';

export class RedisComponent extends BaseComponent {
  constructor(
    @inject({ key: CoreBindings.APPLICATION_INSTANCE })
    protected application: BaseApplication,
  ) {
    super({
      scope: RedisComponent.name,
      initDefault: { enable: true, container: application },
      bindings: {},
    });
  }

  override async binding(): Promise<void> {
    const cacheRedis = new RedisHelper({ name: 'cache-redis', host: 'localhost', port: 6379 });
    await cacheRedis.connect();
    this.application.bind({ key: BindingKeys.APPLICATION_REDIS_CACHE }).toValue(cacheRedis);
  }
}

ControllerFactory (Auto CRUD)

ControllerFactory.defineCrudController() generates a full CRUD controller with OpenAPI documentation, authentication, and standard REST endpoints.

typescript
import {
  ControllerFactory,
  controller,
  inject,
  BindingKeys,
  BindingNamespaces,
} from '@venizia/ignis';

const _Controller = ControllerFactory.defineCrudController({
  repository: { name: FinanceWalletRepository.name },
  authenticate: { strategies: ['jwt', 'basic'] },
  controller: {
    name: 'FinanceWalletController',
    basePath: '/finance-wallets',
    isStrict: { path: true, requestSchema: true },
  },
  entity: () => FinanceWallet,
});

@controller({ path: '/finance-wallets' })
export class FinanceWalletController extends _Controller {
  constructor(
    @inject({
      key: BindingKeys.build({
        key: FinanceWalletRepository.name,
        namespace: BindingNamespaces.REPOSITORY,
      }),
    })
    financeWalletRepository: FinanceWalletRepository,
  ) {
    super(financeWalletRepository);
  }

  // Override individual methods for custom logic (role-based filtering, etc.)
  @logged()
  override async findById(opts: { context: TRouteContext<Env> }) {
    // Custom implementation...
  }
}

Database Transactions

Repositories support transactional operations through the data source.

typescript
await this.repository.dataSource.withTransaction(async tx => {
  await this.repository.create({ data: orderData, options: { transaction: tx } });
  await this.itemRepository.create({ data: itemData, options: { transaction: tx } });
});

Environment Configuration

All packages use dotenv-flow for environment management. Variables follow the APP_ENV_* prefix convention.

Environment Files

FilePurposeGit Tracked
.env.exampleTemplate with all required variablesYes
.env.developmentDevelopment settingsYes
.env.testTest environmentYes
.env.localLocal overridesNo (gitignored)

Common Environment Variables

bash
# Application
APP_ENV_NODE_ENV=development
APP_ENV_APPLICATION_NAME=nx-sale
APP_ENV_APPLICATION_SECRET=<secret>
APP_ENV_SERVER_HOST=0.0.0.0
APP_ENV_SERVER_PORT=3003
APP_ENV_SERVER_BASE_PATH=/api

# Authentication
APP_ENV_JWT_SECRET=<jwt-secret>
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)
APP_ENV_CACHE_REDIS_HOST=localhost
APP_ENV_CACHE_REDIS_PORT=6379
APP_ENV_CACHE_REDIS_DB=0

# Redis (BullMQ Queues)
APP_ENV_BULLMQ_REDIS_HOST=localhost
APP_ENV_BULLMQ_REDIS_PORT=6379
APP_ENV_BULLMQ_REDIS_DB=1

# Redis (Pub/Sub)
APP_ENV_PUBSUB_SUBSCRIBER_REDIS_HOST=localhost
APP_ENV_PUBSUB_PUBLISHER_REDIS_HOST=localhost

# Minio (Assets)
APP_ENV_MINIO_HOST=localhost
APP_ENV_MINIO_API_PORT=9000
APP_ENV_MINIO_ACCESS_KEY=minioadmin
APP_ENV_MINIO_SECRET_KEY=minioadmin

# Typesense (Search)
APP_ENV_TYPESENSE_API_KEY=xyz
APP_ENV_TYPESENSE_NODES=http:localhost:8108

# Snowflake ID
APP_ENV_SNOWFLAKE_WORKER_ID=1
APP_ENV_SNOWFLAKE_EPOCH_CHECKPOINT=1704067200000

# Service URLs
APP_ENV_IDENTITY_SERVICE_URL=http://localhost:3001
APP_ENV_COMMERCE_SERVICE_URL=http://localhost:3002

Development Workflow

Build Commands

CommandDescription
make installInstall all dependencies (bun install)
make buildBuild everything in dependency order
make build-packagesBuild only packages/*
make build-3rdBuild only third-parties/*
make coreBuild @nx/core
make saleBuild @nx/sale
make commerceBuild @nx/commerce
make identityBuild @nx/identity
make financeBuild @nx/finance
make paymentBuild @nx/payment
make signalBuild @nx/signal

WARNING

Always use bun run rebuild within a package. Never run tsc directly -- path aliases (@/common, @/services) require the tsc-alias post-processing step.

Development Servers

CommandServicePort
make dev-sale-orderSale service3003
make dev-commerceCommerce service3002
make dev-identityIdentity service3001
make dev-financeFinance service--
make dev-paymentPayment service--
make dev-signalSignal (WebSocket)--

Per-Package Commands

bash
cd packages/sale            # Navigate to package
bun run rebuild             # Clean + build (tsc + tsc-alias)
bun run server:dev          # Start with .env.development
bun run test                # Run tests (requires .env.test)
bun run test:watch          # Watch mode
bun run lint:fix            # ESLint + Prettier auto-fix
bun run migrate:dev         # Apply database migrations

Testing

bash
# Run all tests for a package
cd packages/sale && bun run rebuild && bun run test

# Run a single test file
bun test --env-file=.env.test dist/__tests__/path/to/file.test.js

Linting

CommandScope
make lintEverything
make lint-packagesAll packages
make lint-3rdAll third-parties
make lint-sale-order@nx/sale only
make lint-finance@nx/finance only
make lint-payment@nx/payment only
make lint-signal@nx/signal only

Git Hooks

bash
make setup-tools    # Configure git to use .githooks directory
make pre-commit     # Run all linting checks (used by pre-commit hook)

API Standards

Route Conventions

MethodPatternPurpose
GET/resourcesList with filtering, pagination
GET/resources/:idGet single resource
GET/resources/countCount matching resources
POST/resourcesCreate resource
PUT/resources/:idFull update
PATCH/resources/:idPartial update
DELETE/resources/:idSoft delete (sets deletedAt)

OpenAPI Documentation

Every service exposes Swagger/OpenAPI documentation:

  • OpenAPI JSON: /doc/openapi.json
  • Scalar Explorer: /doc/explorer
  • Health Check: /health

Package Documentation

PackageLink
@nx/coreArchitecture, components, configuration, database, utilities
@nx/identityAuthentication, authorization, user management
@nx/commerceProducts, pricing, merchants
@nx/saleOrders, checkout, payments
@nx/paymentWebhook config, payment orchestration
@nx/signalWebSocket, encryption, real-time messaging
@nx/assetFile storage, MetaLink management
@nx/invoiceE-invoice generation
API GatewayGateway routing and middlewares

External References

ResourceLink
IGNIS Frameworkhttps://venizia-ai.github.io/ignis/
Honohttps://hono.dev
Drizzle ORMhttps://orm.drizzle.team
Zodhttps://zod.dev
BullMQhttps://docs.bullmq.io
Typesensehttps://typesense.org/docs

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.