Node.jsExpressMongoDBDockerAWS
Billing & Invoice Automation
Microservices platform for secure billing workflows
Personal Project·2024·2 min read
Microservices
Architecture
AWS + Docker
Deploy
Structured logs
Observability
Problem
Manual billing and invoicing processes are error-prone, hard to audit, and don't scale. I built a backend platform to automate secure billing workflows with proper authentication and transactional integrity.
Constraints
- Transactional integrity — Billing operations must be atomic and auditable
- Security — Authentication and authorization on all billing endpoints
- Scalability — Services must deploy independently and scale on AWS
- Operability — Production issues need to be diagnosable via structured logs
Architecture
Microservices-based backend with clear service boundaries:
- Auth Service — JWT-based authentication and role-based authorization
- Billing Service — Invoice generation, payment tracking, and transactional writes to MongoDB
- Notification Service — Event-driven hooks for billing state changes
- Infrastructure — Docker containers deployed on AWS (EC2, S3) with IAM-scoped access
ADR
MongoDB with multi-document transactions over MySQL
Flexible document schema for varied invoice formats. MongoDB transactions provided ACID guarantees for billing operations.
Alternative considered: MySQL — stronger for relational reporting but slower iteration on invoice schema changes
Before / After
Key Implementation
// Transactional invoice creation
async function createInvoice(invoiceData, userId) {
const session = await mongoose.startSession();
session.startTransaction();
try {
const invoice = await Invoice.create([{ ...invoiceData, createdBy: userId }], { session });
await AuditLog.create([{ action: 'INVOICE_CREATED', userId, invoiceId: invoice[0]._id }], { session });
await session.commitTransaction();
return invoice[0];
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
}
What I'd Do Differently
- Add idempotency keys on payment endpoints — Would prevent duplicate charges on retries
- Use AWS Lambda for notification service — Better fit for event-driven, infrequent workloads
- Implement integration tests earlier — Caught transaction edge cases late in development