2026 · EngineeringAbout 14 min readNovus Stream Solutions
One Next.js app, many sections: routing a hub-and-spoke portfolio from a single codebase
novusstreamsolutions.com serves a multi-property hub — portfolio, ventures, docs, product blog, changelog — from one Next.js App Router project using a components/sections pattern, rather than separate apps. The maintainability case for a solo dev.
Overview
A multi-product operation can structure its web presence two ways. It can build a separate application for every surface — one for the marketing site, one for the docs, one for the blog, and so on — each with its own deployment, its own dependencies, and its own copy of the shared design. Or it can serve all of those surfaces from a single application that has many sections. novusstreamsolutions.com takes the second path: the portfolio, the ventures page, the documentation, the product blog, the changelog, and the rest are all served from one Next.js App Router project, organized with a components/sections pattern, while the actual product applications live as separate spokes on their own subdomains. This post is the maintainability case for that choice, made specifically from the perspective of one person keeping the whole thing running.
The distinction worth being precise about up front is between the hub and the spokes. The hub — the marketing, reference, and narrative surface — is one app with many sections. The spokes — the background remover, the visualizer — are independent applications, because they are genuinely different software with different runtime needs. Hub-and-spoke is not "everything in one repo"; it is "one app for the surfaces that share a design and a purpose, separate apps for the products that do not." Getting that line right is what makes the architecture sane rather than a monolith.
The cost of many small apps
Splitting every surface into its own application sounds clean — separation of concerns, independent deployment — but for a small operation it multiplies the maintenance surface in ways that compound badly. Every separate app is a separate set of dependencies to keep updated, a separate deployment to configure and monitor, a separate place the shared design system has to be installed and kept in sync, and a separate set of cross-cutting concerns — metadata, analytics, the sitemap, the navigation — that now has to be implemented and maintained in parallel. A change to the site-wide navigation or the brand styling becomes a change you make several times, in several codebases, and hope you made identically. The separation that looked like cleanliness becomes duplication that looks like drift.
For a team with people to spread across those apps, that overhead is absorbable. For one person, it is a tax paid on every change, and the tax is not just time — it is the cognitive load of holding several codebases in your head and the risk that they fall out of sync. The marketing site ends up on a slightly different version of the design than the docs; the blog's metadata handling diverges from the changelog's; each small inconsistency is individually minor and collectively the reason a multi-app setup feels perpetually slightly broken. The architecture should reduce the number of things one person has to keep consistent, not multiply them.
How the single-app hub is organized
The hub is one Next.js App Router project, and its internal structure is what keeps a single app from becoming an unmanageable monolith. Each major surface is a route, and the substantial views are built as section components — the components/sections pattern — so the homepage, the portfolio, the ventures page, and the rest are composed from clear, self-contained pieces rather than tangled together. The routing is the App Router's file-based system: a route for the docs with a dynamic segment for each doc slug, a route for the product blog with a dynamic segment for each post, static routes for the standalone pages. One project, many routes, each route assembled from section components that are easy to find and reason about.
The payoff of this organization is that everything cross-cutting is defined once and applies everywhere automatically. There is one root layout, so the navigation and footer and global styling are written a single time and every page inherits them. There is one metadata configuration, so the robots directives and canonical-URL handling are consistent across the whole hub without per-page effort. There is one sitemap generator that reads the content data and includes every blog post and doc automatically. There is one Open Graph image route that every page points at. Add a new section and it inherits correct navigation, share images, and structured data for free, because those things are properties of the single app rather than things each surface has to reimplement.
Why the spokes stay separate
The other half of the architecture is knowing what does not belong in the hub. The background remover and the visualizer are not sections of the marketing site; they are substantial applications with their own runtime characteristics — heavy client-side machine learning, audio processing, their own performance and dependency profiles. Folding them into the hub would mean the marketing site carried the weight and complexity of the products, and a change to a product could destabilize the hub and vice versa. Keeping them as separate spokes on their own subdomains lets each one be the right kind of application for its job, deployed and iterated independently, without the hub and the products being chained together.
This is the discipline that keeps the single-app decision from sliding into a bad monolith. The rule is not "one app for everything"; it is "one app for the surfaces that genuinely share a design, a purpose, and a runtime profile, and separate apps for the things that do not." The hub surfaces share all three — they are the same brand, the same narrative-and-reference job, the same lightweight content-driven runtime — so they belong together. The products share none of those with the marketing site, so they stay apart. Drawing the boundary by what actually shares concerns, rather than by an abstract preference for splitting or merging, is what makes the structure hold up.
The components/sections pattern, concretely
The pattern that keeps a single app from becoming an unmanageable monolith deserves a concrete description, because it is the structural discipline that makes "one app, many sections" work. Rather than building each page as a sprawling, tangled file, the substantial views are composed from section components — self-contained pieces that each render one meaningful part of a page. A homepage is assembled from a hero section, a feature section, a portfolio section, and so on, each living as its own component that can be understood, edited, and reasoned about in isolation. The page is a composition of clear pieces rather than a monolith, which keeps the single app navigable even as it grows many surfaces.
This composition is what lets the codebase scale in surfaces without scaling in complexity per surface. Adding a new section to a page, or a new page composed of sections, is adding well-bounded components rather than enlarging a tangled whole, so the cognitive cost of each addition stays low. Section components are also reusable across surfaces where appropriate, so common patterns are written once and composed where needed rather than duplicated. The result is that a single application can serve a large hub while remaining something one person can hold in their head, because the complexity is partitioned into comprehensible pieces. The components/sections pattern is the difference between "one app" meaning a manageable composition and "one app" meaning an unmaintainable monolith, and it is what makes the single-app decision sustainable.
The inheritance payoff of one root layout
The single most leverage-creating property of the single-app hub is that everything cross-cutting is defined once in the root and inherited everywhere automatically. There is one root layout, so the navigation, the footer, and the global styling are written a single time and every page in the entire hub gets them without any per-page effort. There is one metadata configuration, so canonical-URL handling and crawler directives are consistent across every surface for free. There is one sitemap generator that reads the content data and includes every post and doc automatically, and one share-image route every page points at. Add a surface, and it inherits all of this by virtue of being part of the app.
This inheritance is precisely what a multi-app setup sacrifices and then has to rebuild through manual coordination. When every surface is its own app, the navigation, metadata rules, sitemap, and styling each have to be implemented and maintained separately, and kept in sync by discipline rather than by structure — which is where multi-app setups drift into inconsistency. The single app enforces consistency structurally: because the cross-cutting concerns are properties of the one app rather than things each surface implements, they cannot drift between surfaces, because there is only one of each. For a solo operator, this is the difference between consistency being automatic and consistency being a constant manual effort across several codebases. One root layout inherited everywhere is the architectural embodiment of "define it once."
Drawing the hub-and-spoke boundary well
The architecture lives or dies on where the line between hub and spoke is drawn, and getting that boundary right is what keeps the single-app decision from sliding into either a bad monolith or needless fragmentation. The rule is to group by what genuinely shares concerns: surfaces that share a design, a purpose, and a runtime profile belong in one app, while things that differ on those dimensions belong apart. The hub surfaces — marketing, portfolio, docs, blog, changelog — share all three: the same brand, the same narrative-and-reference job, the same lightweight content-driven runtime, so they belong together. The products differ on all three: different runtime needs, different purpose, heavy specialized processing, so they stay separate.
Drawing the boundary by actual shared concerns, rather than by an abstract preference for splitting or merging everything, is what makes the structure hold up under growth. A reflexive "split everything" produces the multi-app maintenance tax for surfaces that should have been together; a reflexive "merge everything" produces a monolith that chains unrelated software together. The hub-and-spoke discipline avoids both by asking, for each surface, whether it genuinely shares the hub's design, purpose, and runtime — folding it in if so, keeping it separate if not. This boundary judgment is the central architectural decision, and making it on the basis of real shared concerns is what lets the hub be a coherent single app and the products be appropriately independent, rather than forcing everything into one shape that fits some surfaces badly.
File-based routing as organization
The App Router's file-based routing is part of what keeps the single app organized, because the route structure mirrors the file structure, making the site's layout legible from the directory tree. A route for the docs with a dynamic segment for each doc slug, a route for the product blog with a dynamic segment for each post, static routes for standalone pages — each is a clear place in the file system, so finding the code for a given surface is a matter of navigating to the obvious location rather than searching a tangle. The routing being file-based means the app's structure is self-evident from how the files are arranged, which is a real aid to navigability in a single app serving many surfaces.
Dynamic segments are what let a single route serve an entire category of content without a route per item. One blog-post route with a dynamic slug segment serves every post, reading the right typed object for the requested slug; one doc route serves every doc the same way. This is what makes the single app scale to hundreds of content pages without hundreds of route definitions — the route is a template, and the content data fills it per request. The combination of file-based routing for structure and dynamic segments for content categories means the app can serve a large, varied hub through a small, comprehensible set of routes, each of which is easy to locate and reason about. The routing system is doing organizational work, keeping a multi-surface app navigable, not just mapping URLs to code.
The monorepo question, answered by concerns
A natural question is whether the hub and spokes should at least share a repository even if they are separate apps, and the answer follows the same principle that drew the hub-and-spoke line in the first place: organize by shared concerns. The hub is one app because its surfaces share design, purpose, and runtime; the spokes are separate apps because they do not share those with the hub. Whether the separate apps share a repository is a secondary question answered by whether they share enough development concerns to benefit from co-location, weighed against the coupling that shared repositories can introduce. The primary architectural decision — what is one app and what is separate — is settled by shared concerns, and the repository structure follows from practical development considerations rather than overriding them.
The deeper point is that these structural decisions should be driven by what actually shares concerns rather than by an abstract preference for consolidation or separation. A reflexive "everything in one repo" or "everything in its own repo" both ignore the actual question, which is what genuinely belongs together based on shared design, runtime, and development needs. The hub-and-spoke architecture answers that question deliberately at the application level — one app for the surfaces that share concerns, separate apps for those that do not — and the same reasoning, applied to repositories or any other structural choice, keeps the overall system organized around real relationships rather than abstract tidiness. Letting shared concerns drive the structure, at every level, is what keeps a multi-product operation's architecture coherent and maintainable rather than either monolithic or needlessly fragmented.
The solo-maintainability case
The whole argument comes down to a single question for a one-person operation: how many things do you have to keep consistent by hand? The single-app hub minimizes that number. The design system is installed once. The navigation is defined once. The metadata rules, the sitemap, the share images, the deploy configuration — each exists once and covers every hub surface. When the brand changes, you change it in one place and the entire hub follows. When you add a surface, it inherits the shared infrastructure rather than requiring you to rebuild it. The mental model is one codebase, which is a model one person can actually hold completely, rather than a federation of apps that no single person ever has fully in view.
That consolidation is what makes running a multi-property hub solo sustainable rather than a slow-motion consistency nightmare. The architecture is doing the work that a team would otherwise do through coordination: it is enforcing consistency structurally, so the solo maintainer does not have to enforce it through discipline across multiple codebases. Combined with the code-as-content model for the posts and the single deploy pipeline on Vercel, the result is a multi-product web presence that one person can keep coherent and current — which is the only kind of architecture that actually survives contact with a one-person operation. The companion posts cover the deploy workflow that ships this single app and the URL strategy that ties the hub to its spokes.