XRAPH/Writing/Packaging the first two weeks of a SaaS backend

Packaging the first two weeks of a SaaS backend

I had built the same multi-tenant scaffolding three times in a year and made different decisions each time. Ultimate Backend was an attempt to stop doing that.

Published
Nov 2019
Length
2 min read
Systems
1

#The repetition

Between 2017 and 2019 I started three products that all needed the same things before they could do anything specific to their own domain. Accounts and sessions. Organisations with members and invitations. A tenant boundary that every query respected. Billing attached to a plan. A gateway in front of several services. Deployment manifests.

None of that is interesting, and I had made materially different choices each time for reasons I could no longer reconstruct. The third time I noticed I was reading my own previous repository to remember how I had done the invitation flow.

#What went into it

Ultimate Backend is a NestJS codebase with the pieces already connected to each other . Microservices behind an Apollo Federation gateway, so each service owns its slice of the graph rather than a central schema owning everything. Command and query separation with an event store behind the write side. Tenant resolution in the request pipeline instead of in each resolver. Authentication and billing already aware of one another, since a subscription that does not know who the subscriber is has to be retrofitted later.

The service split follows the boundaries you would expect from the domain rather than from the team, which is worth stating because the reverse is the usual outcome .

#The choice I would make differently

Ultimate Backend is a template. You clone it, and from that moment your copy and the original diverge. Every improvement I made afterwards was unavailable to anyone who had already started, and every bug I fixed stayed broken in their copy unless they went looking.

Six years later I built the same instinct as a framework instead, where the shared parts are a dependency you upgrade. That is a better answer, and it took me a long time to see it, because a template is much easier to write. A template can assume anything about the application. A framework has to be right about the seams.

#What people actually used

The repository picked up more interest than I expected, and the pattern of questions was informative. Almost nobody asked about the federation setup. Most questions were about tenant isolation and about the event store, and enough of them were about the event store alone that I pulled that module out into its own package.

That is a reliable signal. When people repeatedly ask how to use one part of a template without the rest, the part wants to be its own library. Microservice literature tends to frame this as a question of service boundaries , and the same reasoning applies to the packages inside a single codebase.

#Was it worth writing

Yes, for a reason I did not anticipate. Publishing forced me to justify every default in a README, and several of them turned out to have no justification beyond habit. The tenant resolution design in particular changed because writing down why it lived in the resolver made it obvious that it should not.

References

  1. [1]Rex Raphael and contributors, Ultimate Backend: Multi-Tenant SaaS Starter with CQRS and GraphQL Microservices, GitHub, 2019https://github.com/juicycleff/ultimate-backend
  2. [2]James Lewis, Martin Fowler, Microservices: A Definition of This New Architectural Term, martinfowler.com, 2014https://martinfowler.com/articles/microservices.html
  3. [3]Sam Newman, Building Microservices: Designing Fine-Grained Systems, O'Reilly Media, 2nd edition, 2021
  4. [4]Melvin E. Conway, How Do Committees Invent?, Datamation, vol. 14, no. 5, pp. 28-31, 1968