Jayant © 2026Hyderabad, India
Jayant
HomeAboutWorkWritingResumeContact
Discuss a product
Field note / Published17 July 2026

How I Split One Next.js App into Four Products

The architecture decisions that separated Portfolio, Studio, Admin, and Auth while keeping shared contracts and one clear data model.

5 min read1,024 words
  • Architecture
  • Next.js
  • Monorepo
  • System Design
Cover illustration for How I Split One Next.js App into Four Products
Inside this note
  1. 01The boundary appeared in the responsibilities
  2. 02The current runtime shape
  3. 03Share contracts, not application behavior
  4. 04One database does not mean one data boundary
  5. 05Auth was a migration, not a redirect
  6. 06What the split improved
  7. 07The tradeoff I accept

The system started as one Next.js application because that was the fastest honest architecture for the first version. One repository, one deployment, and almost no ceremony between a public portfolio and the experiments behind it.

Then the products stopped sharing the same needs.

Public editorial pages wanted excellent crawlability and a small client surface. Authenticated tools needed protected data and product navigation. Content operations required stronger authorization. Sign-in, recovery, MFA, and identity management carried risks that did not belong in every application.

The problem was no longer folder organization. One runtime was pretending to be four products.

The boundary appeared in the responsibilities

I wrote the product responsibilities before moving code:

ProductOwnsTrust model
PortfolioPublic identity, Work, Writing, Resume, contactPublic and indexable
StudioProducts, utilities, personal workspacesPublic discovery plus protected user data
AdminCMS, users, access, deployment operationsAuthenticated and capability-gated
AuthSign-in, recovery, MFA, profile, account securityConservative identity boundary

Once each surface could be described in one sentence, the split became a product decision rather than an architectural fashion.

Drawing architecture diagram…

The boundary also reduced accidental coupling. A Studio proxy change no longer decides whether Portfolio can be crawled. An Admin dependency does not enter the public application. Auth return rules have one owner.

The current runtime shape

The monorepo now contains four independent Next.js 16 and React 19 applications. They deploy separately, use application-specific environment contracts, and own their routes and navigation.

They still share one codebase because several contracts are genuinely common:

  • @jayantgoyal/identity owns the framework-neutral person and product identity.
  • @jayantgoyal/web-brand owns web-facing labels and synchronized asset paths.
  • @jayantgoyal/web-urls builds canonical origins and rewrites cross-product URLs.
  • @jayantgoyal/web-seo centralizes metadata and indexability helpers.
  • @jayantgoyal/web-auth carries Supabase SSR, cookies, session, safe-return, profile, password, and logout contracts.
  • @jayantgoyal/web-ui supplies the shared Studio, Admin, and Auth shell.
  • @jayantgoyal/portfolio-contracts keeps Portfolio reads and Admin writes aligned.

Portfolio deliberately owns its editorial component system. It does not import the application shell used by operational products because the public reading experience has a different visual and runtime responsibility.

Drawing architecture diagram…

Share contracts, not application behavior

The most important monorepo rule is directional: applications may import packages, but never another application’s source.

A shared package earns its place when multiple products depend on a stable contract. It does not absorb a behavior merely because two files look similar.

For example, @jayantgoyal/web-auth knows how to create a server client and validate a safe return URL. Studio and Admin still decide who may enter a route. @jayantgoyal/web-ui can render a sidebar shell, but each product owns its navigation tree. @jayantgoyal/portfolio-contracts defines a Writing record, while Admin owns editing and Portfolio owns public presentation.

That division keeps reuse useful without turning packages into a fifth hidden application.

One database does not mean one data boundary

The four products use the same Supabase project because identity is global and cross-product contracts are real. Ownership is expressed through explicit schemas rather than separate databases created for appearance:

  • foundation contains private reusable database primitives;
  • iam contains profiles, access, roles, capabilities, policy, and audit data;
  • iam_private contains private authorization predicates and provisioning helpers;
  • studio owns Studio product data;
  • portfolio owns public CMS, Writing, LinkedIn planning, and contact-abuse state.

Every application selects the intended schema. Row-level security and server authorization preserve resource boundaries. Service-role access is limited to authorized operational routes and never enters Portfolio or Auth.

This was a deliberate middle ground: independent product deployments without artificial identity duplication or cross-database coordination.

Auth was a migration, not a redirect

Moving authentication into its own application affected cookies, callbacks, recovery links, preview hosts, return URLs, and stale sessions. A folder move could not prove those seams were safe.

The cutover extracted shared session contracts first, preserved compatibility entry routes while ownership moved, validated allowed return origins, and tested cross-application behavior before making Auth the interactive owner. The current state is clear: Auth owns sign-in, recovery, MFA, providers, profile, and account security; product entry URLs are redirects, not competing implementations.

That sequence took longer than changing a destination URL. It made the risk visible and reversible.

What the split improved

The result is not simply a cleaner tree.

  • Portfolio performance can be measured without authenticated product code in its graph.
  • Studio can protect user data while leaving product discovery public.
  • Admin can use operation-specific capabilities for privileged work.
  • Auth has one conservative security boundary.
  • Deployments can fail or roll back independently.
  • Shared contract changes still receive monorepo-wide type and architecture checks.

The architecture also made later stories easier to explain. The CMS now has an obvious edit owner and public read owner. Bundle budgets can be enforced per application. A content field has one path from schema to Admin to Portfolio.

The tradeoff I accept

Four applications create more configuration: four deployments, environment contracts, domain mappings, route inventories, and shared-package consumers. The system therefore needs automated checks around identity, URLs, SEO, service-role use, architecture, and documentation.

That cost is worthwhile because it buys a boundary I can name and test. A change has an owner. A product has an audience. Shared code has a reason to be shared.

The architecture became larger only after the product responsibilities became different. That order matters.

Explore Studio, see Admin, or read how the CMS crosses the Admin and Portfolio boundary.

Article details
Written by
Jayant
Reading time
5 minutes
Last updated
12 September 2026
Get in touch
From the workbench

Notes from the work

More on how the software was built. Written by Jayant, Software Engineer.

Get in touch
Continue reading

What Product Ownership Looks Like in Practice

Six decisions that turn a feature into a dependable product: problem, trust, data, experience, failure handling, and operations.

Next note