Skip to content

Ledger Service

@nx/ledger generates official Vietnamese HKD (Hộ Kinh Doanh — household-business) accounting ledgers. A Kafka-driven worker fetches business data, renders PDF (Typst) + XLSX (ExcelJS), encrypts with AES-256-GCM, and uploads to S3. It also owns the ledger lifecycle (DRAFT → FINALIZED → revise), snapshot-based draft editing, and per-merchant annual configuration. Upstream data comes from @nx/core repositories (finance/sale/merchant); files are stored via @nx/asset MetaLinks.

1. Quick Reference

PropertyValue
Package@nx/ledger
CodeSVC-00060-LEDGER
TypeMicroservice
RuntimeBun
Base ClassVerifierApplication
Locationpackages/ledger
Base Path/v1/api/ledger
Dev Port31060
Container Port3000 (external 31060)
Snowflake ID6
DB Schemaledger (6 tables)
Binding Namespace@nx/ledger
Application Rolesapi and/or worker (APP_ENV_APPLICATION_ROLES, default both)

2. Purpose & Scope

IncludedExcluded
6 HKD ledger forms (S1a, S2a–S2e)Tax calculation / declaration engine
Async generation pipeline (fetch → render → encrypt → upload)Payment / invoicing (owned by @nx/payment, @nx/invoice)
PDF (Typst) + XLSX (ExcelJS) renderingLedger Zod schemas + Drizzle models (live in @nx/core)
Ledger lifecycle DRAFT → FINALIZED → revise (new version)File storage transport (delegated to @nx/asset MetaLink/S3)
Snapshot-based draft editing + staleness trackingSubmission to tax authority (SUBMITTED status reserved, not built)
Per-merchant annual config + tax-declaration levels
Stalled-job recovery sweep + manual retry/regenerate
Real-time WebSocket job-status notifications (worker role)

3. Tech Stack

External:

LibraryPurpose
@venizia/ignisIoC container, DI, BaseService, BaseRestController, ControllerFactory
@venizia/ignis-helpersLogger, Kafka helpers, Redis, WebSocketEmitter, AES, BunS3Helper
@platformatic/kafkaKafka producer + consumer client
@myriaddreamin/typst-ts-node-compilerTypst NodeCompiler — PDF rendering
exceljsXLSX workbook builder
hono + @hono/zod-openapi + @scalar/hono-api-referenceHTTP server, OpenAPI, /doc explorer
drizzle-orm + pgDB access via PostgresCoreDataSource
lodashUtilities

Internal:

PackagePurpose
@nx/coreVerifierApplication, ledger schemas/models, repositories, KafkaTopics, LedgerStatuses/LedgerIdentifiers/PeriodTypes, LedgerErrors, WS channels/rooms
@nx/assetMetaLinkService / MetaLink — S3 upload + download of generated files

4. Project Structure

packages/ledger/
├── src/
│   ├── application.ts            # VerifierApplication subclass; role-gated wiring
│   ├── index.ts                  # bootstrapApplication()
│   ├── migrate.ts                # bootstrapMigration()
│   ├── common/                   # constants, keys, rest-paths, roles, environments, types
│   ├── components/               # kafka (producer+consumer), recovery, websocket, stress-test*
│   ├── controllers/              # actions, crud, batch, merchant-ledger-config
│   ├── datasources/              # PostgresCoreDataSource
│   ├── helpers/                  # S3Helper (lazy BunS3Helper singleton)
│   ├── migrations/processes/     # 4 seed migrations
│   ├── models/                   # zod request/response schemas
│   ├── repositories/             # re-exports from @nx/core
│   ├── services/                 # business logic + fetchers/ + generators/
│   └── utilities/                # buildPeriodString, filename helpers
├── resources/templates/          # 6 Typst .typ + common.typ + previews/
├── package.json
└── tsconfig.json

* stress-test/ exists but is not registered in application.ts.

5. Architecture

Detail: see Architecture.

6. Domain Snapshot

Full ERD + per-entity tables: see Domain Model.

7. Surface Summary

REST controllers — full reference rendered live from /v1/api/ledger/doc/openapi.json (Scalar viewer at /doc, gateway portal). Registered only in api role; order matters (LedgerActionsController first to avoid /:id shadowing).

ControllerBase pathSurface
LedgerActionsController/ledgersgenerate, status, retry, download, check-exists, search, snapshot pull/get, acknowledge-change, finalize, regenerate, revise
LedgerController/ledgersCRUD (merchant-scoped find/findById/findOne/count)
LedgerBatchController/ledgersstatus/batch, generate/batch, retry/batch
MerchantLedgerConfigController/merchant-ledger-configscreate, list (by merchant+year), update, confirm

Async surface — full reference in API Events:

DirectionChannelCount
InboundKafka ledger.generate1 topic (worker role)
OutboundKafka ledger.generate1 topic (enqueue + recovery re-enqueue)
OutboundWebSocket1 topic (observation/ledger/job/status, worker role)
BullMQnone

8. Components

Registered in configureComponents() (skipped entirely when RUN_MODE=migrate). WebSocket is registered only in worker role; the Kafka consumer + recovery sweep are registered unconditionally but self-skip unless the worker role is active.

ComponentFilePurpose
KafkaProducerComponentsrc/components/kafka.component.tsInitializes the producer, binds APPLICATION_KAFKA_PRODUCER
KafkaConsumerComponentsrc/components/kafka.component.tsWorker-only; N consumers on ledger.generateLedgerWorkerService.handleGeneration()
RecoveryComponentsrc/components/recovery.component.tsWorker-only; periodic stalled-job sweep → reset PENDING + re-enqueue
LedgerWebSocketComponentsrc/components/websocket.component.tsWorker-only; Redis-backed WebSocketEmitter, registers LedgerNotificationService

9. Services

Always registered (api or worker) unless marked worker-only.

ServiceFileOne-liner
LedgerServiceservices/ledger.service.tsLedger record CRUD/queries (findExisting, findOrCreateDraft, saveSummary)
LedgerJobServiceservices/ledger-job.service.tsLedgerJob state machine: setProcessing/Completed/Rejected, stalled-job sweep
LedgerSnapshotServiceservices/ledger-snapshot.service.tsDraft editing: pull, getSnapshot, acknowledgeChange, finalize, revise
MerchantLedgerConfigServiceservices/merchant-ledger-config.service.tsPer-merchant per-year config; resolves required types, validates batch
MetaLinkServiceservices/meta-link.service.tsS3/MetaLink upload + decrypted download (via @nx/asset)
LedgerQueueServiceservices/ledger-queue.service.tsEnqueue orchestration: enqueue, batch, retry, regenerate
LedgerDataFetcherServiceservices/ledger-data-fetcher.service.tsPer-type fetcher facade (validate, fetch)
S1aHkdDataFetcherServiceservices/fetchers/S1a-HKD — real data (finance/sale/merchant/tax-info)
S2xHkdDataFetcherServiceservices/fetchers/S2a–S2e — fixture data (placeholder)
LedgerDataFixtureServiceservices/ledger-data-fixture.service.tsReads pre-generated fixture JSON
StorageEncryptionServiceservices/storage-encryption.service.tsAES-256-GCM encrypt/decrypt
LedgerWorkerService (worker)services/ledger-worker.service.tsPipeline orchestrator; touches LedgerJob.status only
PdfGeneratorService (worker)services/generators/pdf-generator.service.tsTypst NodeCompiler PDF renderer (lazy singleton)
XlsxGeneratorService (worker)services/generators/xlsx-generator.service.tsExcelJS workbook builder
LedgerNotificationService (worker)services/ledger-notification.service.tsWS job-status emitter (registered by WS component)

10. Repositories

All re-exported from @nx/core; bound in configureRepositories(). The package owns no schemas.

RepositoryTable / SourceNotes
LedgerRepositoryledger.LedgerOwned
LedgerJobRepositoryledger.LedgerJob+ setProcessing, setCompletedIfProcessing, stalled-job query
LedgerSnapshotRepositoryledger.LedgerSnapshotOwned
LedgerSnapshotEntryRepositoryledger.LedgerSnapshotEntryOwned
MerchantLedgerConfigRepositoryledger.MerchantLedgerConfigOwned
TaxDeclarationLevelRepositoryledger.TaxDeclarationLevelOwned
MetaLinkRepository@nx/assetGenerated-file links
MerchantRepository, TaxInfoRepository, VnProvinceRepository, VnWardRepository@nx/coreCross-package read (S1a header)
FinanceTransactionRepository, SaleOrderRepository@nx/coreCross-package read (S1a entries)
PermissionRepository, RoleRepository, PolicyDefinitionRepository@nx/coreAuthorization seeds

11. Entry Points

FilePurpose
src/index.tsService entry → bootstrapApplication()
src/migrate.tsMigration entry → bootstrapMigration()
src/application.tsApplication extends VerifierApplication

12. Configuration

Env vars + roles + seeded data: see Configuration.

13. Operations

Deployment + observability + security + runbook: see Operations.

Concepts — why/how:

Reference — lookup:

Features — deep dives:

Decisions:

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