A TypeScript backend system built with Bun, Hono, MongoDB/Mongoose, Redis, and BullMQ for usage-based billing, payment webhook processing, reconciliation workflows, and notification delivery.
This project is designed as a backend engineering portfolio project that models real industry backend infrastructure used in SaaS platforms, fintech systems, AI API products, notification platforms, developer APIs, and usage-based billing systems.
Modern software platforms often need to:
- Track customer usage.
- Enforce quotas and plan limits.
- Generate invoices from usage.
- Receive and process payment webhooks.
- Reconcile payment status when webhooks fail.
- Notify customers about usage, invoices, payments, and failed actions.
This project implements those backend modules in a modular and production-aware way.
Handles:
- API key creation.
- Usage event tracking.
- Usage counters.
- Quota checks.
- Invoice generation.
- Invoice status updates.
Example usage events:
api.request
email.sent
sms.sent
payment.processed
storage.gb
ai.tokens
document.processedHandles:
- Payment intent creation.
- Webhook receiving.
- Webhook signature validation.
- Duplicate webhook detection.
- Payment status updates.
- Webhook retry processing.
- Payment reconciliation.
Handles:
- Notification template creation.
- Email notifications.
- SMS mock notifications.
- In-app notifications.
- Notification delivery tracking.
- Failed notification retries.
| Layer | Technology |
|---|---|
| Runtime | Bun |
| Language | TypeScript |
| HTTP Framework | Hono |
| Database | MongoDB |
| ODM | Mongoose |
| Cache | Redis |
| Queue | BullMQ |
| Validation | Zod |
| Logging | Pino / JSON structured logging |
| Testing | Bun Test |
| Docs | Markdown + OpenAPI-ready structure |
| Architecture | Vertical Slice Modular Monolith |
This project uses a Vertical Slice Modular Monolith.
That means:
- The application is deployed as one backend.
- Each business domain is isolated into its own module.
- Each module owns its features, models, repositories, jobs, and types.
- Each feature is kept readable without forcing developers to jump through too many files.
Instead of forcing this pattern for every endpoint:
route -> controller -> validator -> service -> repository -> modelthis project uses feature files:
modules/billing/features/track-usage.feature.ts
modules/payments/features/receive-webhook.feature.ts
modules/notifications/features/send-notification.feature.tsEach feature file owns the main request flow, while shared persistence logic stays in repositories.
src/
├── main.ts
├── app.ts
│
├── shared/
│ ├── config/
│ │ ├── env.ts
│ │ └── config.ts
│ ├── database/
│ │ └── mongoose.ts
│ ├── redis/
│ │ └── redis.client.ts
│ ├── queue/
│ │ └── bullmq.ts
│ ├── logger/
│ │ └── logger.ts
│ ├── errors/
│ │ └── app-error.ts
│ ├── middleware/
│ │ ├── error.middleware.ts
│ │ ├── request-id.middleware.ts
│ │ └── validate.middleware.ts
│ └── utils/
│ └── response.ts
│
└── modules/
├── billing/
│ ├── billing.module.ts
│ ├── billing.model.ts
│ ├── billing.repository.ts
│ ├── billing.types.ts
│ ├── features/
│ │ ├── create-api-key.feature.ts
│ │ ├── track-usage.feature.ts
│ │ ├── get-current-usage.feature.ts
│ │ ├── generate-invoice.feature.ts
│ │ └── mark-invoice-paid.feature.ts
│ └── jobs/
│ ├── aggregate-usage.job.ts
│ └── generate-invoice.job.ts
│
├── payments/
│ ├── payment.module.ts
│ ├── payment.model.ts
│ ├── webhook-event.model.ts
│ ├── payment.repository.ts
│ ├── payment.types.ts
│ ├── features/
│ │ ├── create-payment-intent.feature.ts
│ │ ├── receive-webhook.feature.ts
│ │ ├── process-webhook.feature.ts
│ │ ├── retry-webhook.feature.ts
│ │ └── run-reconciliation.feature.ts
│ └── jobs/
│ ├── process-webhook.job.ts
│ └── reconciliation.job.ts
│
└── notifications/
├── notification.module.ts
├── notification.model.ts
├── notification.repository.ts
├── notification.types.ts
├── features/
│ ├── send-notification.feature.ts
│ ├── create-template.feature.ts
│ ├── retry-notification.feature.ts
│ └── get-notifications.feature.ts
└── jobs/
└── send-notification.job.tsbun installCreate .env from .env.example:
cp .env.example .envdocker compose up -dbun run devbun testThis project demonstrates:
- Usage metering.
- Usage-based billing.
- API key management.
- Payment webhook processing.
- Webhook idempotency.
- Payment reconciliation.
- Background jobs.
- Redis queues.
- Notification delivery.
- Retry logic.
- MongoDB/Mongoose persistence.
- Zod validation.
- Modular backend architecture.
- Structured logging.
- Error handling.
- Operational runbooks.
Every webhook must be verified before processing.
Duplicate webhook events must not double-process payments.
Use minor currency units.
Good:
amountInKobo = 500000Bad:
amount = 5000.50Use Redis for fast counters and queues for async aggregation.
Failed email/SMS delivery should support retry and status tracking.
Redis is only for temporary counters, locks, queues, and cache.
- API Documentation
- Architecture
- Database Schema
- Events & Workflows
- Operations Runbook
- Code Conventions
{
"scripts": {
"dev": "bun --watch src/main.ts",
"start": "bun src/main.ts",
"test": "bun test",
"lint": "biome check .",
"format": "biome format --write ."
}
}