Skip to content

Backend Packages Overview

This page documents the complete backend architecture of the BANA POS system. It covers all 18 packages in the packages/ directory, the shared PostgreSQL database with 14 schemas, event-driven infrastructure (Kafka domain events + Debezium CDC; Redis for cache, WebSocket fan-out and BullMQ), security mechanisms, and the IGNIS framework patterns that unify the entire codebase.

Technology stack

Versions are single-sourced - see Platform Facts → Application stack (generated from the root package.json overrides; the build lint blocks drift). The backend builds on IGNIS (DI framework) + Hono (HTTP) + Drizzle ORM / Zod (data + validation) on the Bun runtime.


System Topology

All 18 Backend Packages

PackagePortRoleKey ResponsibilitiesPrimary Dependencies
@nx/core--FoundationBase classes, DB models across 14 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/helpdesk31032ServiceMulti-channel customer support - tickets, SLA, auto-assignment, knowledge base, surveys (build currently broken)core
@nx/taxation31130ServiceTax info lookup, VN administrative units, tax groups, T-VAN integrationcore
@nx/signal31090ServiceWebSocket with ECDH E2E encryption, Redis Pub/Subcore
@nx/invoice1190ServiceE-invoice generation (IIAPI + T-VAN), base path /v1/apicore, iiapi, t-van
@nx/search--LibraryTypesense integration, filter conversion, CDC synccore
@nx/asset--LibraryMinio/disk file storage, MetaLink managementcore
@nx/gateway--InfraAPI gateway and Astro portal (routing, BFF, static portal)core

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

Package Architecture

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 151 database models across 14 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) - re-exported from @venizia/ignis
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
AppFixedRolesSUPER_ADMIN (999), ADMIN (900), OPERATOR (600), OWNER/organizer-owner (500), CASHIER (110), EMPLOYEE (100), CUSTOMER (10), GUEST (1)
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 31010.

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 31020. 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 31030.

Order Lifecycle:

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.

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.

14 PostgreSQL Schemas, 151 Entities

SchemaModelsCountKey Entities
helpdeskCustomer support30Agent, AgentGroup, AgentGroupMember, Article, ArticleCategory, AssignmentRule, SlaEscalation, Notification, NotificationTemplate, FeatureRequest, Compensation, etc.
publicGeneral domain26Product, ProductInfo, ProductIdentifier, ProductVariant, ProductOption, Merchant, Organizer, Category, Device, SaleChannel, Configuration, Setting, MetaLink, ReceiptTemplate, WebhookConfig, etc.
inventoryStock management17InventoryItem, InventoryLocation, InventoryStock, InventoryTracking, Material, MaterialRecipe, PurchaseOrder, PurchaseOrderItem, ProductionOrder, UnitOfMeasure, Vendor, VendorItem, etc.
saleOrders21SaleOrder, SaleItem, SaleCheck, SaleCheckItem, Customer, KitchenStation, KitchenTicket, KitchenTicketItem, PointTransaction, PosSession, PosSessionReport, Reservation, EntitlementPolicy, etc.
invoiceE-invoice9Invoice, InvoiceIssuance, InvoiceRequest, InvoiceProvider, InvoiceProviderConfig, InvoiceAuditTracing, InvoiceConfigMapping, MerchantInvoiceProfile, MerchantProfileEnrollment
pricingPricing engine9Fare, FareSet, Cost, Promotion, PromotionMethod, Rule, Tax, TaxSet, TaxType
identityAuth & users8User, UserCredential, UserIdentifier, UserProfile, UserConfiguration, Role, Permission, PolicyDefinition
ledgerHKD accounting8Ledger, LedgerIdentifier, LedgerJob, LedgerSnapshot, LedgerSnapshotEntry, LedgerTaxConfig, MerchantTaxConfig, TaxTier
financeFinancial tracking6FinanceAccount, FinanceCategory, FinanceTransaction, FinanceVoucher, FinanceVoucherSequence, PaymentIntegration
taxTax & VN admin6TaxGroup, TaxGroupItem, TaxInfo, VnAdministrativeUnit, VnProvince, VnWard
licensingLicense management5Activation, License, LicenseEvent, Policy, PolicyFeature
allocationEvent seating4AllocationLayout, AllocationUnit, AllocationUsage, AllocationZone
outreachSubscribers2Inquiry, Subscriber
paymentReserved namespace0-

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 controllers8 fixed roles with numeric precedence

Default Roles

RoleCodeScope
SUPER_ADMIN999Full system access
ADMIN900Administrative operations
OPERATOR600Operational management
OWNER (organizer-owner)500Organizer-scoped access
CASHIER110Cashier / POS staff
EMPLOYEE100Employee-scoped access
CUSTOMER10End customer
GUEST1Unauthenticated / global

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,
  VerifierApplication,
  MigrationRepository,
  PostgresCoreDataSource,
} from '@nx/core';
import { CoreBindings, IApplicationInfo } from '@venizia/ignis';

export const appConfig = createAppConfig();

export class Application extends VerifierApplication {
  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=31030
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:31010
APP_ENV_COMMERCE_SERVICE_URL=http://localhost:31020

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-saleSale service31030
make dev-commerceCommerce service31020
make dev-identityIdentity service31010
make dev-financeFinance service31040
make dev-paymentPayment service31080
make dev-signalSignal (WebSocket)31090

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@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.