ProNextJS
    Loading
    lesson

    Intro to Systems Architecture

    Jack HerringtonJack Herrington

    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.

    the to-do list app

    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.

    local architecture

    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.

    BFF

    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.

    External Architecture

    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.

    Transcript

    Welcome to the systems architecture portion of the course. So in this section, we're going to take a look at how Next.js works within the ecosystem of databases and microservices you may have in your project or in your job. So the way that we're going to do that is we're going to have multiple different implementations of a simple to-do list application. That's what we're looking at right here. This to-do list application has authorization, so we can sign in.

    In this case, I'm going to use test one and pass. I'll sign in. This is using NextAuth for the sign in. Now we get our to-do list, we can add a to-do list item. We can check or check off that we've done that item, and then we can delete that item if we want.

    It's pretty simple. In addition to that, there's also a priority over here. So what we're looking at is a bunch of different architectural elements. We're looking at authorization, how to go and authorize with the Next.js application itself, as well as send that authorization on to microservices that we might be connecting to depending on the systems architecture that we're looking at. And we talk between the client and the server in both authorized and unauthorized ways.

    The priorities here are an unauthorized request because they are not user-specific and they're not sensitive information. So we can just make an unauthorized request to get those priorities. The to-dos on the other hand are authorized. You only get your to-dos and then you can only make changes to your to-dos. So we need to manage that authorization either within the application or within the microservices or a combination of both.

    The primary way that we manage authorization and all of these examples is by an HTTP only cookie-based JOT. But there are examples that also show how to use a service token. Now the idea here is to give you a toolkit of a bunch of different systems architectures, and you can choose which one you want to use for your application and then use that as a template in your application. Now there's three basic groups of systems architecture. What we're going to do next is we're actually going to just walk through at a high level those systems architectures and then in the sections that follow in the course, you'll drill down into each one of those and see specific examples of each and when you might use those.

    The first architectural group is the simplest. It's the local architecture group. The idea is you've got a single deployed Next.js server. You have some resources, raw resources that connects to, in this case, a database. And you've got a client that then makes the request to the Next.js server directly.

    Let's see about the different protocols there. That client can connect to that Next.js server using a combination of different options. You could use REST, GraphQL, or TRPC using the API routes provided by Next.js, or you could use the built-in server actions functionality. In all those scenarios, the client is going to connect to the Next.js server directly, the Next.js server is going to connect to that database directly and then process that request. This local architecture group is ideal for small projects, startups, or admin panels inside of large companies.

    Let's take a look at our next architecture group that's a back-end for front-end or BFF group. So in this case, the Next.js server is acting as a back-end for the front-end. The client connects to the Next.js server. The Next.js server in turn talks to some microservices and those in turn talk to a database. This is going to be the most common group that we see.

    For communications protocols, the client is going to be using REST or GraphQL or TRPC or WebSockets just like we had in the local group, or it could use server actions. The Next.js server is then going to talk to those back-end services using REST or GraphQL or TRPC or WebSockets, and then those microservices are going to talk to the database, make any mutations or reads, return that to the Next.js server, which would in turn return it to the client. This is the most common scenario that we see. We see microservices built either in Rust or in Go or in Node. The Next.js server is the UI server and the client connects to that Next.js server, authorization is shared between the client and the Next.js server, and oftentimes passed through to the microservices.

    The next group is the external group. This is the most complex systems architecture you're going to see. It's the kind of thing that you're going to see at large companies. For example, your Next.js server might be vending mycompany.com, and you've got microservices that are on api.mycompany.com. On the initial request from the client to the server, that server is going to use React Server components to then make requests to those microservices.

    It would send that data back to the client and then anything coming off the client might go directly to the API, api.mycompany.com using REST or GraphQL or TRPC or WebSockets. There's two different flavors of authorization there. It could pass the session cookie and have the microservices also be on that same session, or it could send an access token off the client that is microservice specific. Now, the issue here is that we know that for performance reasons the Next.js App Writer aggressively caches. So if the client makes mutations using those microservices, then that Next.js server would be out of cache step with the microservice.

    So the option to update it would be that the microservice could then use webhooks or an event bus to alert to the Next.js server that particular cache elements need to be revalidated. There you go, three different systems architecture groups. Next up, we're going to go and drill into each one of those groups and show several different options in each one of those spaces. It's gonna be great. Let's get right into it.