Why This Decision Mattered
Billing systems have zero tolerance for inconsistency. A customer being charged twice or not at all is unacceptable. We needed:
- Atomic transactions across multiple tables
- Strong referential integrity
- Complex queries for reporting
- Audit trail for compliance
The MongoDB Experience
We had used MongoDB for 3 years. It worked well for:
- User profiles (document-oriented)
- Product catalogs (flexible schema)
- Activity logs (append-heavy)
But we struggled with:
- Multi-document transactions (added in 4.0 but still awkward)
- Reporting queries (aggregation pipeline is powerful but complex)
- Data consistency (no foreign keys meant orphaned records)
PostgreSQL Evaluation
We ran a 2-week spike to evaluate PostgreSQL:
- Modeled the subscription domain relationally
- Implemented key billing operations
- Tested transaction behavior under failure
- Evaluated query performance for reporting
The results were compelling. Complex billing queries that took 50+ lines of aggregation pipeline became 10-line SQL queries.
Migration Path
We didn’t migrate existing MongoDB services. Instead:
- New services with relational data use PostgreSQL
- Existing MongoDB services remain (if working well)
- Shared data accessed via APIs, not direct DB access
Team Learning
We invested in PostgreSQL training:
- Internal workshops on SQL and PostgreSQL features
- Pair programming during initial development
- Documentation of patterns and best practices
After 6 months, the team is comfortable with both databases and can choose the right tool for each use case.