FireUX · 7 min
What We Learned About Extending the Nuxt Spine
Our daily work revealed a simpler Nuxt architecture rule: keep the default spine and extend it under each Entity that owns real behaviour.

One of the useful things about reviewing our daily work is that small technical problems sometimes reveal a much larger design decision.
That happened while we were reorganising the FireUX runtime. Individual files were not the difficult part. The difficult part was answering a repeated question: when code belongs to an App, User, Kitchen, or Supplier, where should the complete capability live?
We already trusted Nuxt's structure. Pages belonged in pages, layouts in layouts, middleware in middleware, plugins in plugins, and server endpoints in server/api. We called this predictable framework shape the Nuxt spine.
Our new understanding was simpler: Nuxt's framework spine remained the right outer architecture. FireUX extends that familiar vocabulary inside substantial Entity ownership boundaries. The Entities do not become parallel miniature Apps.
The Nuxt spine was already doing its job
A spine is a stable shape that tells contributors where different kinds of work belong.
At the Nuxt boundary, the framework-defined directories have clear jobs:
pagesdefines routes.layoutsdefines persistent page composition.middlewarecontrols route behaviour.pluginsintegrates runtime services.server/apiexposes server endpoints.componentsandcomposablesprovide Nuxt-discovered UI and state.
FireUX adds models as its own conventional home for shared and Entity-owned contracts. Nuxt does not define or discover that directory for us. Keeping that distinction explicit lets a Nuxt contributor recognise the framework entrypoints while also understanding the smaller FireUX convention layered around them.
The problem appeared as FireUX grew
A flat technical structure works well when an App is small. As the number of Entities grows, however, root folders can accumulate unrelated files:
- App components beside User components
- Kitchen composables beside Supplier composables
- Shared helpers mixed with domain policy
- Server logic separated from the Entity that owns its rules
The filenames may still be valid, but ownership becomes difficult to see. A developer or AI coding agent can find ten files named Manager, Create, or useCollection, but those names do not reveal which Entity owns the behaviour or whether it is safe to reuse.
We initially treated this as a movement problem. If a file was in the wrong place, we could move it. But repeated moves exposed the deeper issue: we needed one ownership rule that would still make sense after the next Entity arrived.
Entities extend the default Nuxt spine
Our answer is to keep Nuxt's framework directories and extend their familiar technical vocabulary beneath each real Entity that owns behaviour.
At the root, runtime/pages and runtime/server/api remain Nuxt adapters. Root runtime/components and runtime/composables also remain part of the spine when they serve the system, coordinate multiple Entities, or belong higher in the hierarchy. FireUX keeps shared models and utilities at that level for the same reason. These are not temporary folders waiting to be emptied.
An Entity such as runtime/app or runtime/user extends the FireUX spine with components, composables, models, and server roles for work whose authority belongs to that Entity.
An Entity includes only the roles it actually owns. Empty symmetry is not the goal. A User may need components, composables, models, and server policy. A smaller Entity may need only a model and one server capability.
The important rule is that the same Nuxt-oriented vocabulary continues beneath an Entity name. This is especially useful for AI-assisted development because the filesystem communicates the ownership boundary that we would otherwise have to explain in every prompt.
A concrete Place example
The Place detail capability now reads as one bounded path through the repository:
runtime/pages/places/[id]/index.vueremains the Nuxt route adapter.runtime/server/api/places/[placeId].get.tsremains the HTTP and server-rendering adapter.runtime/place/components/Detail.vueowns the Place presentation.runtime/place/composables/usePlaceDetail.tsowns the loaded and live detail state.runtime/place/models/place.model.tsowns the canonical Place contract.runtime/place/server/identity/read.tsowns the trusted Place read.
The route and endpoint stay where Nuxt expects them. The meaningful Place implementation sits together under runtime/place, where its ownership can be understood without scanning every component, composable, model, and server utility in the package.
Keep Nuxt entrypoints thin
Entity structure does not mean moving framework entrypoints into arbitrary locations.
A page can be about a User while remaining in Nuxt's pages directory. Its job is to read the route, compose the page, and delegate meaningful User work to User-owned components and composables.
A server endpoint can operate on an App while remaining under server/api. The handler owns the HTTP boundary, then delegates validation, policy, and domain behaviour to the App's server capability.
This produces a clear dependency direction:
Nuxt entrypoint → Entity-owned implementation → shared App spine → framework primitives
The higher shared spine should not import an Entity merely to complete supposedly generic behaviour. Entities may use shared mechanics, but shared mechanics do not secretly depend on one Entity.
Why this structure works better with AI coding agents
Predictable ownership reduces the amount of repository context an AI agent needs before making a safe change.
It improves file discovery
A request about User identity can begin inside runtime/user. A request about App configuration can begin inside runtime/app. The agent does not need to scan every root component or infer ownership from naming conventions alone.
It creates smaller change boundaries
When model, UI, state, and trusted server behaviour share an Entity boundary, the agent can inspect the complete capability without treating the entire package as relevant.
It makes reuse more honest
Two Entities may both have collection composables, but that does not prove they need a universal collection engine. Their named entrypoints can reuse lower read mechanics while preserving different authority, filters, outcomes, and product language.
It makes verification easier
The expected checks follow the boundary. A filesystem move should preserve route registration, generated component names, server contracts, SSR, reactivity, and data authority. Guards can reject completed legacy paths once every consumer has moved.
This changed how we think about architecture optimised for AI. It is not about adding an ai folder or writing longer instruction files. It is about making architectural truth visible enough that an agent can form a small plan and prove that it stayed within it.
Avoid turning every feature into an Entity
Predictability can become ceremony if every concept receives the full folder tree.
A surface such as Console, Directory, Detail, or Admin may organise how an Entity appears without owning it. A capability does not need every technical role merely to look complete.
Before adding an Entity root, ask:
- Does this concept have its own identity and authority?
- Does it own behaviour across more than one technical role?
- Can we name the parent Entity if it is actually a child record or operation?
- Are we organising real product ownership or only making the tree look even?
The best architecture contains enough repetition to be predictable and enough restraint to remain truthful.
What the migration taught us
Extending an existing Nuxt spine into Entity scopes should be bounded and behaviour-preserving.
- Classify each file as a Nuxt entrypoint, shared mechanism, or Entity-owned implementation.
- Choose one complete capability instead of moving an entire package mechanically.
- Move its UI, state, models, and domain server logic together where applicable.
- Leave pages and API handlers at the Nuxt boundary and make them delegate inward.
- Update explicit imports and supported Nuxt registration in the same change.
- Verify routes, SSR, generated imports, data authority, and focused builds.
- Reject the old path only after all consumers have moved.
A migration should change ownership and discovery paths, not product behaviour. We also learned that a broad mechanical move is a warning sign. If a small ownership correction produces failures across unrelated surfaces, the boundary was not yet clear enough.
What we now do at FireUX
FireUX keeps Nuxt's framework spine for pages, layouts, middleware, plugins, and server endpoints. It also keeps root components, composables, models, and utilities where they genuinely serve the system or several Entities. Substantial Entities extend that spine under their own runtime scope for the behaviour they own.
This gives developers and AI coding agents a consistent way to locate authority while keeping App, User, Kitchen, Menu, Supplier, and other Entity capabilities independently editable.
You can see how this thinking fits the wider FireUX App approach, explore the current FireUX product operations, or read how we turn daily development work into reusable lessons.
The lesson from this daily work is simple: use the Nuxt spine as Nuxt designed it, then extend its familiar roles under each real Entity where ownership needs to become more specific. The result is not architecture for AI instead of people. It is one architecture that both can navigate, change, and verify with less guesswork.