Architecture
Nuxt Crouton uses a two-layer architecture that separates generated code from the stable core library. This approach, combined with Nuxt's layer system, allows you to organize your code by business domain while maintaining flexibility and control.
The Two-Layer Architecture
The core principle behind Nuxt Crouton is the separation between your customizable generated code and the stable core library. This architecture gives you the freedom to modify generated files while benefiting from updates to the underlying framework.
┌─────────────────────────────────────┐
│ Generated Code (Yours) │
│ - Forms, Lists, Tables │
│ - You customize freely │
│ - Lives in YOUR project │
└─────────────────────────────────────┘
↓ uses
┌─────────────────────────────────────┐
│ Core Library (Stable) │
│ - Composables, utilities │
│ - Modal management, caching │
│ - Updates via npm │
└─────────────────────────────────────┘
Generated Code Layer
The generated code layer includes all the CRUD interfaces you create using the Crouton generator:
- Forms - Create and edit forms with validation
- Lists - Collection list views with filtering
- Tables - Data table components with sorting and actions
- Composables - Data fetching and mutation logic
- TypeScript Types - Fully typed interfaces for your collections
This code lives in your project and is yours to customize however you need. You can modify components, add new features, change styling, or completely refactor the generated code—it's all under your control.
Core Library Layer
The core library provides the foundational utilities and composables that your generated code uses:
- Data Operations -
useCollectionQuery(),useCollectionMutation(),useCroutonMutate() - Modal Management -
useCroutonModal(),useCroutonSlideover(),useCroutonDrawer() - Notifications -
useCroutonToast()for user feedback - Caching - Automatic cache invalidation built on Nuxt's
useFetch - Utilities - Helper functions for common tasks
The core library stays consistent and receives updates via npm. When you update the nuxt-crouton package, you get bug fixes and new features in the composables without affecting your customized code.
Why This Separation Works
This architecture solves a common problem in code generation tools: how to provide updates without breaking customizations.
With Nuxt Crouton:
- You own the generated code - No magic, no hidden abstractions. The generated files are just Vue components and TypeScript code you can read, understand, and modify.
- Core stays stable - The composables and utilities follow semantic versioning. Breaking changes are rare and clearly documented.
- Customizations persist - Your modifications to generated code won't be overwritten by updates to the core library.
- You can regenerate - If you want fresh templates, you can regenerate collections. Just back up your customizations first.
Domain-Driven Design with Nuxt Layers
Nuxt layers allow you to organize your collections by business domain rather than technical function. This creates clear boundaries, improves maintainability, and enables domain-specific deployments.
Why Use Layers?
Clear Boundaries - Each domain is isolated with its own components, composables, and API routes. Changes in one domain don't affect others.
Easier Maintenance - Related code stays together. When working on e-commerce features, everything you need is in the shop layer.
Independent Deployment - Deploy layers separately or together. This is particularly useful for larger applications or multi-tenant systems.
Reusability - Share layers across projects. Build a blog layer once, use it in multiple applications.
Layer Structure
A typical Nuxt Crouton project using layers looks like this:
layers/
├── shop/ # E-commerce domain
│ ├── components/
│ │ ├── products/
│ │ │ ├── List.vue
│ │ │ ├── Form.vue
│ │ │ └── Table.vue
│ │ ├── orders/
│ │ └── inventory/
│ ├── composables/
│ │ ├── useProducts.ts
│ │ ├── useOrders.ts
│ │ └── useInventory.ts
│ ├── server/
│ │ └── api/
│ │ └── teams/
│ │ └── [team]/
│ │ ├── products/
│ │ ├── orders/
│ │ └── inventory/
│ └── types/
│ ├── products.ts
│ ├── orders.ts
│ └── inventory.ts
│
├── blog/ # Content domain
│ ├── components/
│ │ ├── posts/
│ │ ├── authors/
│ │ └── comments/
│ ├── composables/
│ ├── server/
│ └── types/
│
└── admin/ # Admin domain
├── components/
│ ├── users/
│ ├── roles/
│ └── permissions/
├── composables/
├── server/
└── types/
When to Use Layers
Use layers when you have:
- Multiple business domains (shop, blog, admin)
- Large applications with clear domain boundaries
- Reusable functionality to share across projects
- Teams working on different domains
Don't use layers for:
- Simple applications with only a few collections
- Tightly coupled features that share lots of code
- Prototypes or early-stage projects
Start simple—you can always add layers later as your application grows.
Generating into Layers
To generate collections into a specific layer, use the --layer flag:
# Generate into the shop layer
npx crouton-generate shop products --fields-file products-schema.json
# Generate into the blog layer
npx crouton-generate blog posts --fields-file posts-schema.json
# Generate into the admin layer
npx crouton-generate admin users --fields-file users-schema.json
If you don't specify a layer, Crouton generates into your main application directory.
Layer Configuration
Each layer can have its own nuxt.config.ts file for layer-specific configuration:
// layers/shop/nuxt.config.ts
export default defineNuxtConfig({
// Layer-specific configuration
components: {
dirs: [
{ path: '~/components', prefix: 'Shop' }
]
}
})
This allows you to:
- Set up layer-specific component prefixes
- Configure layer-specific API routes
- Add layer-specific modules or plugins
- Override settings for specific domains
Best Practices
Organize by Domain, Not Technical Function
Good - Organized by business domain:
layers/
├── shop/ # All e-commerce features
│ ├── products/
│ ├── orders/
│ └── inventory/
└── blog/ # All content features
├── posts/
├── authors/
└── comments/
Bad - Organized by technical function:
components/
├── forms/ # All forms together
├── lists/ # All lists together
└── tables/ # All tables together
Keep Related Code Together
Everything related to a domain should live in that domain's layer:
- Components
- Composables
- API routes
- TypeScript types
- Utilities
- Tests
This makes it easy to find code, understand dependencies, and make changes without affecting other domains.
Use Clear Naming Conventions
- Layer names:
shop,blog,admin(lowercase, singular or plural based on domain) - Collection names:
products,orders,posts(plural) - Component names:
ProductList.vue,OrderForm.vue(PascalCase) - Composable names:
useProducts,useOrders(camelCase, plural)
Document Layer Dependencies
If one layer depends on another, document it clearly:
// layers/shop/README.md
# Shop Layer
E-commerce functionality for products, orders, and inventory.
## Dependencies
- Requires `admin` layer for user management
- Uses shared types from `core` layer
Related Resources
- Best Practices - Recommended patterns and practices
- Generation - Learn about code generation
- Nuxt Layers Documentation - Official Nuxt layers guide