One Database, Many Customers: How We Keep Data Apart

By Sharon C
Jun 29, 2026 4 min read 6 views
One Database, Many Customers: How We Keep Data Apart
VoVo serves many organizations from one shared database. Here is how every record knows its owner, how queries stay inside the right account and where we add extra care.
Share:

There are two broad ways to build software that serves many customers. You can give every customer their own database, or you can store every customer's data in one shared database and keep it apart with careful design. Our platform, VoVo, uses the shared approach. It lets us improve every module for everyone at once, keeps costs reasonable for small organizations and makes features like one login across several accounts possible.

The trade-off is that separation becomes our job rather than the database's. Here is how we do it.

Every record carries its owner

Almost every table that holds customer data has a column naming the account it belongs to. Many also name the specific website within that account, because one account can run several sites. A blog post, a form submission, an order, a member, an uploaded image: each knows exactly whose it is.

That column is not optional decoration. It is how every other protection works. When we design a new table, adding the owner column and its index is part of the first draft, not something added later.

Automatic filters for everyday queries

Our data models use a shared building block that does two things automatically. When a record is created inside an account, it stamps the account's identifier onto it. When records are read, it adds a filter so only the current account's rows come back.

Because the filter is automatic, a developer writing an ordinary screen does not have to remember it every time, and an ordinary mistake does not leak data. That is the first line of defense.

Explicit scoping where automation stops

Automatic filters depend on knowing which account is active. In a web request that is clear. In other places it may not be:

  • Background jobs running long after the request ended
  • Command-line tools used by our team
  • Data seeders that publish content as part of a release
  • Scheduled tasks that work across many accounts

In those paths we do not trust the automatic filter to be active. Code names the account and website explicitly in every query and every write. If a script is meant to change something for one account, the condition that limits it to that account is written out in full.

Quote card: Sharing a database is a design choice. Keeping every row in its own account is a daily discipline.

Unique per account, not unique everywhere

Shared tables need careful uniqueness rules. Two different churches can both have a page called "about." So a page address must be unique within an account and website, not across the whole platform. Our database constraints are built that way, combining the owner columns with the field that must be unique. We explore that in composite unique keys.

People are the exception

User logins are shared across accounts on purpose, because one person can belong to several organizations. That means people need special handling everywhere they appear in a list or search. Our article on scoped user lists explains the pattern.

Reviews look for the leaks

Automatic protections are strong, but reviews are where we catch the unusual cases: a report that joins two tables and forgets the owner condition on the second one, a cache that stores an answer without naming the account, a download link that checks the file but not who is asking for it. Our review checklist asks about each of these specifically, and our standards document gives every contributor the same rules.

Testing the walls

We also check isolation from the outside. During browser checks we sign in as one account and confirm we cannot reach another account's records by changing an address, guessing an identifier or searching for a name we know exists elsewhere. A wall that has never been pushed on has not really been tested.

Why we accept the responsibility

A shared database asks more of us as engineers. In return, every customer benefits from every improvement immediately, and organizations of every size can afford a full platform. We think that is a good trade, as long as we treat separation as the most important feature we never advertise. It begins with tenant-first thinking.

Frequently asked questions

Is a shared database less secure than separate databases?

Not inherently. Both can be secure or insecure. A shared database requires consistent owner columns, automatic filtering and careful handling of background work, which is why we treat those as non-negotiable.

What happens when one person belongs to two accounts?

They have one login and a separate membership in each account. Each account only sees its own data, and lists of people are always built from the current account's memberships.

How do you stop scripts from changing the wrong account?

Scripts, jobs and seeders name the account and website explicitly in every query and stop with an error if the account does not match what they expect.

Comments (0)

No comments yet. Be the first to share your thoughts!