SaaS-Anwendungsentwicklung
Next.js-SaaS-Entwicklung
Mandantenfähige SaaS-Produkte auf dem App Router - Mandantentrennung und Autorisierung vor dem ersten Screen entschieden, prüfungsfestes Billing und ein Dashboard, das auf dem Server bleibt.
Ein SaaS-Produkt ist keine Website mit Login. Drei Entscheidungen der ersten zwei Wochen - wie Mandantentrennung funktioniert, wo Autorisierung lebt und wie der Billing-Zustand die Anwendung erreicht - bestimmen, was die nächsten zwei Jahre kosten, und alle drei sind teuer umzukehren, sobald es Produktionsdaten gibt.
Wir bauen solche Produkte, und wir beginnen bei diesen dreien.
Mandantentrennung, vor dem ersten Screen entschieden
Es gibt drei Modelle, und das richtige hängt von Ihren Kunden ab, nicht von der Mode.
| Modell | Isolation | Wann es richtig ist |
|---|---|---|
| Gemeinsame Tabellen, Mandantenspalte | Im Code durchgesetzt | Die meisten Produkte. Am günstigsten im Betrieb und in der Migration. |
| Schema pro Mandant | Von der Datenbank durchgesetzt | Regulierte Kunden, Export oder Restore pro Mandant. |
| Datenbank pro Mandant | Vollständig | Wenige, große, vertraglich isolierte Kunden. |
Das gemeinsame Modell ist meistens richtig, und sein einziges echtes Risiko ist eine Query, die den Mandantenfilter vergisst. Das ist kein Disziplinproblem für Code Reviews - es wird mit Row-Level Security in der Datenbank gelöst, sodass ein fehlender Filter nichts zurückgibt statt der Daten eines anderen.
Autorisierung an den Daten, nicht an der Route
Middleware, die entscheidet, wer eine Seite sehen darf, ist ein Tor, und Tore sind nützlich. Autorisierung sind sie nicht. Server Actions und Route Handler lassen sich direkt aufrufen - eine Prüfung, die nur in der Middleware lebt, ist eine Prüfung, die der Endpunkt nicht hat.
Jede Query trägt Mandant und Handelnden. Jede Mutation leitet die Berechtigung neu aus der Session ab, statt einem Argument zu trauen. Die Begründung steht in Server Actions und die drei üblichen Fehler, und in einem mandantenfähigen Produkt ist der Fehlerfall kein Bug, sondern ein Kunde, der die Daten eines anderen liest.
Billing, das eine Prüfung übersteht
Stripe ist der Standard, und die Integration ist nicht der schwere Teil. Schwer ist, dass Ihre Anwendung und Stripe dieselbe Tatsache halten - worauf dieser Kunde Anspruch hat - und dass sie sich widersprechen werden.
Wir behandeln Stripe als Quelle der Wahrheit und die Kopie der Anwendung als Cache, der aus Webhooks neu aufgebaut wird, mit idempotentem und signaturgeprüftem Handler. Ansprüche werden bei jedem Request aus Ihrer eigenen Datenbank gelesen, denn Stripe im Request-Pfad aufzurufen macht die Verfügbarkeit Ihres Produkts von deren abhängig.
Anteilige Abrechnung, Testphasen, Sitzplatzänderungen und fehlgeschlagene Zahlungen sind jeweils ein Zustand, den die Oberfläche ausdrücken muss. Was ein überfälliger Account noch darf, ist eine Produktentscheidung - wir fragen danach, statt eine zu erfinden.
Das Dashboard-Problem
SaaS-Dashboards sind die Stelle, an der App-Router-Anwendungen langsam werden.
Der Reflex ist ein use client ganz oben, weil es Charts und Filter gibt -
und dann geht der ganze Dashboard-Baum an den Browser.
Tabellen, Kopfzeilen, Navigation und Datenholen bleiben auf dem Server. Chart, Filter und Datumswähler sind Client-Inseln. Der URL-Zustand trägt die Filter, was eine gefilterte Ansicht teilbar und den Zurück-Button korrekt macht. Und keine Seite wartet auf ihr langsamstes Widget - jedes streamt hinter seiner eigenen Grenze, was der Punkt an loading.tsx ist kein Spinner ist.
Was wir nicht tun
Wir starten nicht von einem SaaS-Boilerplate. Diese kodieren Entscheidungen zu Mandanten, Auth und Billing, die für ein anderes Produkt getroffen wurden, und sie später aufzutrennen kostet mehr als die zwei Wochen, die sie gespart haben.
Wir bauen auch nicht das Admin-Panel zuerst. Es ist der Teil, den Gründer am liebsten spezifizieren, und der Teil, den Kunden nie sehen.
