├── .prettierrc ├── .gitignore ├── public └── favicon.ico ├── tsconfig.json ├── app.vue ├── .editorconfig ├── modules └── blog │ ├── composables │ └── useArticles.ts │ ├── pages │ └── blog.vue │ ├── components │ └── BlogArticleList.vue │ └── index.ts ├── package.json ├── .eslintrc.js ├── pages └── index.vue ├── nuxt.config.ts └── README.md /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .nuxt 4 | nuxt.d.ts 5 | .output 6 | .env -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/areindl/nuxt-structure-with-modules/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /modules/blog/composables/useArticles.ts: -------------------------------------------------------------------------------- 1 | export const useArticles = async () => { 2 | const articles = await fetch( 3 | 'https://jsonplaceholder.typicode.com/posts?_limit=5' 4 | ) 5 | .then((response) => response.json()) 6 | .catch((error) => { 7 | throw error 8 | }) 9 | 10 | return { 11 | articles, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare" 11 | }, 12 | "devDependencies": { 13 | "@types/node": "^20.10.6", 14 | "nuxt": "^3.11.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /modules/blog/pages/blog.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true 6 | }, 7 | parserOptions: { 8 | parser: '@babel/eslint-parser', 9 | requireConfigFile: false 10 | }, 11 | extends: [ 12 | '@nuxtjs', 13 | 'plugin:nuxt/recommended', 14 | 'prettier' 15 | ], 16 | plugins: [ 17 | ], 18 | // add your custom rules here 19 | rules: {} 20 | } 21 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | /* Modules are automatically imported from the modules directory 4 | See: https://nuxt.com/docs/guide/directory-structure/modules 5 | */ 6 | 7 | app: { 8 | head: { 9 | // Loading Tailwind CSS from CDN for the styling of the demo 10 | script: [{ src: 'https://cdn.tailwindcss.com' }], 11 | meta: [ 12 | { 13 | name: 'viewport', 14 | content: 15 | 'viewport-fit=cover, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no', 16 | }, 17 | ], 18 | }, 19 | }, 20 | 21 | devtools: { 22 | enabled: true, 23 | }, 24 | }) 25 | -------------------------------------------------------------------------------- /modules/blog/components/BlogArticleList.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /modules/blog/index.ts: -------------------------------------------------------------------------------- 1 | import { defineNuxtModule } from '@nuxt/kit' 2 | import { resolve, join } from 'path' 3 | import type { Nuxt } from '@nuxt/schema' 4 | 5 | export default defineNuxtModule({ 6 | meta: { 7 | // Usually the npm package name of your module - in this case a local modal 8 | name: 'blog-module', 9 | // The key in `nuxt.config` that holds the 10 | configKey: 'blog-module', 11 | // Compatibility constraints 12 | compatibility: { 13 | // Semver version of supported nuxt versions 14 | nuxt: '^3.9.0', 15 | }, 16 | }, 17 | setup(options: any, nuxt: Nuxt) { 18 | // Auto register components 19 | nuxt.hook('components:dirs', (dirs) => { 20 | dirs.push({ 21 | path: join(__dirname, 'components'), 22 | }) 23 | }) 24 | 25 | // Auto register composables 26 | nuxt.hook('imports:dirs', (dirs) => { 27 | dirs.push(resolve(__dirname, './composables')) 28 | }) 29 | 30 | // Auto register pages 31 | nuxt.hook('pages:extend', (pages) => { 32 | pages.push({ 33 | name: 'blog', 34 | path: '/blog', 35 | file: resolve(__dirname, './pages/blog.vue'), 36 | }) 37 | }) 38 | }, 39 | }) 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🙋🏽‍♂️ Domain-Driven Design & Nuxt - Using Nuxt Modules to structure large Nuxt Apps 2 | 3 | #### ⚡️ Talk at NUXT NATION by Anton Reindl 4 | 5 | In my talk in 2021, I introduced Domain Driven Design and talked about structuring apps according to domain modules. I and demonstrated how to use Nuxt 2 modules to create independent submodules including Vuex modules and routes. 6 | 7 | The original Nuxt 2 example can be found in a legacy [here](https://github.com/areindl/nuxt-structure-with-modules/tree/nuxt-v2). 8 | 9 | ### 🚀 Nuxt 3 10 | 11 | I reimplemented the example of "Anton's Biketours" in Nuxt 3. This example implements a fresh Nuxt 3 with an landing page ( `pages/index.vue` ) and one domain-module module, namely the blog-module imported from `.modules/blog` . The module has its own components, composable and pages folder to better separate the domain from the rest of the app. 12 | 13 | I also recommend to look at the [documentation](https://v3.nuxtjs.org). 14 | 15 | ## Scope 16 | 17 | ```bash 18 | npm install 19 | ``` 20 | 21 | ## Development 22 | 23 | Start the development server on [http://localhost:3000](http://localhost:3000) 24 | 25 | ```bash 26 | npm run dev 27 | ``` 28 | 29 | ## Production 30 | 31 | Build the application for production: 32 | 33 | ```bash 34 | npm run build 35 | ``` 36 | 37 | Checkout the [deployment documentation](https://v3.nuxtjs.org/docs/deployment). 38 | 39 | ### 📚 Resources 40 | 41 | * [x] [Slides of this Talk](https://github.com/areindl/nuxt-structure-with-modules/blob/c7e3ea69f3e5aa66ccefdb0089322e78ae243b51/static/slides.pdf) 42 | * [x] Blog post [Domain-Driven Design in Nuxt Apps](https://vueschool.io/articles/vuejs-tutorials/domain-driven-design-in-nuxt-apps/) by Filip Rakowski 43 | * [x] Recommended DDD-Books: 44 | + [x] Domain-Driven Design: Tackling Complexity in the Heart of Software - by Eric Evans 45 | + [x] Implementing Domain-Driven Design by Vaughn Vernon 46 | + [x] Domain-Driven Design Distilled by Vaughn Vernon 47 | --------------------------------------------------------------------------------