Chapter 3: Designing the Frontend (Nuxt)
Excellent! With a solid database schema and a backend API designed with DDD/CQRS, it's now time to build the user interface with Nuxt.
Core Pages
We will focus on the two most important pages: the post list page and the post detail page.
1. Post List Page (Homepage)
This is the first page a user sees.
Rendering Strategy: The content (list of posts) changes when new articles are added but is the same for all users, and SEO is critically important. Therefore, SSR (Server-Side Rendering) is the ideal choice.
Implementation in Nuxt:
- File:
pages/index.vue - Data Fetching: We will use
useAsyncDataoruseFetch. These functions will run on the server during the initial load to fetch data. - Optimization: The API endpoint it calls will be a Query in our CQRS system, returning a "flat," lightweight list of DTOs.
pages/index.vuevue<template> <div> <h1>My Awesome Blog</h1> <div v-if="pending">Loading...</div> <div v-else> <div v-for="post in posts" :key="post.id"> <NuxtLink :to="`/posts/${post.slug}`"> <h2>{{ post.title }}</h2> </NuxtLink> <p>by {{ post.authorUsername }}</p> </div> </div> </div> </template> <script setup> // useFetch will automatically call the API on the server (for SSR) // and on the client (during page transitions). const { data: posts, pending } = await useFetch("/api/posts/latest") </script>- File:
2. Post Detail Page
This page displays the content of a single post and its comments.
Rendering Strategy:
- Post Content: Must be rendered on the server (SSR) for SEO and the fastest possible display.
- Comment Section: Can be loaded on the client (CSR) to avoid slowing down the initial render of the post. This is a Hybrid rendering pattern.
Implementation in Nuxt:
- File:
pages/posts/[slug].vue - Fetching Post Data: Use
useFetchto get the main content of the post on the server. - Fetching Comment Data: Create a
<CommentSection />component and fetch the comment data inside it on the client side.
pages/posts/[slug].vuevue<template> <div v-if="post"> <h1>{{ post.title }}</h1> <p>by {{ post.authorUsername }}</p> <div v-html="post.content"></div> <hr /> <h2>Comments</h2> <CommentSection :postId="post.id" /> </div> </template> <script setup> const route = useRoute() const { data: post } = await useFetch(`/api/posts/${route.params.slug}`) </script>- File:
3. Analysis of Optimization Decisions
- SSR for Main Content: Ensures a fast First Contentful Paint (FCP) and is optimized for SEO.
- Hybrid Rendering for Comments: Reduces the load on the server and improves FCP by only loading the main content initially. The comments section (often secondary) is loaded afterward.
- Code Splitting: Nuxt automatically splits the code for each page, so users only download the necessary JavaScript.
- Prefetching: When a user hovers over a
<NuxtLink>, Nuxt will automatically prefetch the data for that page in the background, making page transitions feel almost instantaneous. - Leveraging Backend CQRS: The frontend calls optimized Query endpoints that return only compact DTOs, reducing bandwidth and speeding up JSON parsing.
Conclusion:
We have designed a complete system from end to end. By combining a well-designed database, a powerful CQRS backend API, and a modern Nuxt frontend, we have created an application that not only has a good, maintainable architecture but also boasts extremely high performance at every layer.