Logic64
Blog
Architecture

How to Structure a SaaS Project (Architecture-First)

How to structure a SaaS project: the core layers, multi-tenancy, data flow, and deployment — decided before code, not discovered during it.

3 min read

Start with the boundaries, not the code

Most SaaS projects are not structured — they accrete. A route gets added here, a table gets added there, and six months in, nobody can say where a given decision was actually made or why. The fix is not more discipline while coding. It is deciding the shape of the system before the first file is written.

That shape does not need to be exhaustive on day one. It needs to name the boundaries: which parts of the system are distinct, what crosses between them, and who — or what — is responsible for each one.

The core layers every SaaS project needs

LayerResponsibility
PresentationWhat the user sees and interacts with — the frontend.
API / backendReceives requests, enforces business rules, talks to everything else.
PersistenceWhere state actually lives — the database and its schema.
Auth / identityWho the requester is, and what they are allowed to do.
Background processingWork that should not block a request — queues, scheduled jobs.

None of these are exotic. The value is not in the list — it is in deciding, explicitly, where each one's responsibility ends, before two of them start overlapping by accident.

Multi-tenancy: decide it early

A SaaS product almost always means multiple customers sharing infrastructure, and that is a decision worth making deliberately rather than by default. A shared database with tenant-scoped rows is simpler to operate at small scale. Separate schemas or separate databases per tenant buy stronger isolation at real operational cost.

The expensive version of this decision is not making it late — it is not making it at all, and discovering three different tenant-scoping patterns already live in three different tables.

Data flow and service boundaries

A request should have one predictable path: client to API, API enforces auth and business rules, API talks to persistence, response goes back. Anything asynchronous — an email, a report, a webhook — hands off to background processing instead of blocking that path.

Where this breaks down in practice is exceptions: a frontend that talks to the database directly "just this once," a background job that duplicates a rule already enforced in the API. Each exception is small. The accumulation is what makes a codebase impossible to reason about.

Diagram of the Logic64 request flow: Frontend to Backend to Queue to Engine, with the SSE stream flowing back and the separate CLI acknowledgement request
One predictable request path, plus the async legs it doesn't block on — this is that pattern in a real system.

Deployment and environment structure

Decide environments — development, staging, production — and what is allowed to differ between them before you need to debug why something behaves differently in one. Configuration should be environment-specific by design, not by accident, and secrets should never be the thing that differs between an environment that works and one that does not.

Architecture-first vs. figuring it out as you go

The alternative to deciding this up front is deciding it implicitly, one commit at a time — which is exactly how the layers above end up blurred. An architecture-first approach makes the same decisions, just deliberately and once, reviewed before any code depends on them.

Logic64 applies this directly: Plan locks the stack and boundaries — including decisions like these — before Build generates a workspace against that lock. The docs/okf/ knowledge tree that comes out the other side is the boundary decisions above, written down once, instead of reconstructed from the codebase after the fact.

Frequently asked questions

What is the first architectural decision to make for a SaaS project?

The boundaries: what are the distinct layers — presentation, API, persistence, background processing — and where does each one end and the next begin. Everything else, including specific technology choices, is easier to decide once those boundaries exist.

Should I use a shared database or separate databases for multi-tenancy?

It depends on your isolation and scale requirements, and it is a decision worth making deliberately rather than by default. A shared database with tenant scoping is simpler to operate at small scale; separate schemas or databases per tenant buy stronger isolation at the cost of operational complexity. Either is defensible — an undecided default is not.

Where should authentication live in a SaaS architecture?

As a boundary every request crosses before it reaches business logic, not scattered checks inside individual routes. Deciding this once, up front, is what keeps a later security review from finding it implemented three different ways in three different places.

How does an architecture-first approach differ from figuring it out as you build?

The decisions get made once, deliberately, and reviewed before code depends on them — instead of getting made implicitly, differently, by whichever developer or AI session happens to touch that part of the system first.