Welcome to the systems architecture section of this course! In this section, we'll look into how Next.js interacts with databases and microservices within a project.
To illustrate these concepts, we'll be using a simple to-do list application as a running example throughout this section.
This application, aside from its simplicity, incorporates key elements.
The app includes authorization. You can sign in using credentials like 'test1' for username and 'pass' for the password.
We'll also cover different data fetching strategies. Priorities are fetched without authorization because the data is public and non-sensitive, while to-dos require authorization, meaning you only see and manage your own to-dos.
This variety provides a toolkit of architectural approaches you can adapt for your own projects. Our primary authorization method will be JWT within HTTP-only cookies, but we'll also touch upon using service tokens.
Systems Architecture Groups
We can categorize the systems architectures we'll be exploring into three main groups:
1. Local Architecture
This is the simplest form, ideal for small projects, startups, or even internal admin panels within larger companies. Here's the breakdown:
- Single Deployment: One Next.js server handles everything.
- Direct Database Connection: The server directly interacts with the database.
Communication Protocols:
- Client-Server: REST, GraphQL, tRPC (using API routes), or built-in Server Actions.
- Server-Database: Direct connection.
This setup keeps things straightforward, but it may not be suitable as your application grows.
2. Backend-for-Frontend (BFF)
This pattern is extremely common and introduces a layer of microservices between your frontend and database.
- Next.js as BFF: The server acts as an intermediary.
- Microservices: Handle specific tasks and communicate with the database.
Communication Protocols:
- Client-Next.js: Similar options as local architecture.
- Next.js-Microservices: Typically REST, GraphQL, tRPC, or WebSockets.
We see this a lot with languages like Rust, Go, or Node.js powering the microservices. Authorization is often managed between the client, Next.js server, and potentially passed to the microservices.
3. External Architecture
This group represents the most complex scenarios you might encounter in large organizations, where your frontend and backend are distinct and potentially separated by domains (e.g., mycompany.com
and api.mycompany.com
).
- React Server Components: Used to fetch data from microservices on the initial request.
- Direct Client-Microservice Communication: For subsequent interactions, especially mutations, the client often communicates directly using methods like REST, GraphQL, etc.
Authorization: This gets interesting:
- Shared Session: Session cookies can work across the Next.js server and microservices.
- Access Tokens: Microservice-specific tokens issued to the client.
Caching Challenges:
Next.js's aggressive caching can clash with client-side mutations via microservices. To solve this, we can use webhooks or an event bus to signal the Next.js server about data changes requiring cache revalidation.
Up Next
Over the next few lessons, we're going to dive deeper into each of these architecture groups. We'll explore various options and implementations for each, giving you practical examples and insights into when to choose one approach over another.