KB
Get in touch
All projects
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:

  1. Auth Service — JWT-based authentication and role-based authorization
  2. Billing Service — Invoice generation, payment tracking, and transactional writes to MongoDB
  3. Notification Service — Event-driven hooks for billing state changes
  4. 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