How I structure my next.js applications

Web Dev Cody
18 Jun 202423:18

Summary

TLDRThe video script discusses the importance of code structure and architecture in software development, particularly for a Next.js starter kit. The speaker advocates for a layered architecture approach, inspired by 'Clean Architecture' by Uncle Bob, to maintain separation of concerns and enhance maintainability. They detail the use of controllers, use cases, entities, and the importance of not mixing Next.js specific code with business logic. The script also covers authorization checks, data persistence, and the benefits of this structured approach for scalability and team collaboration.

Takeaways

  • 📅 The video creator emphasizes the importance of regular content updates to maintain relevance with the YouTube algorithm.
  • 🏗️ The discussion centers on the architecture of the creator's Next.js starter kit, drawing inspiration from 'Clean Architecture' by Uncle Bob.
  • 🔍 The video explains the concept of layered architecture, including presentation, business, persistence, and database layers, to enhance code maintainability.
  • 🛠️ The creator advocates for separating business logic from Next.js specific code, suggesting that this leads to cleaner and more manageable projects.
  • 📝 The script introduces the idea of controllers, use cases, entities, and their roles in structuring code to avoid dependencies that complicate maintenance.
  • 🔑 The video creator discusses the use of server actions in Next.js as a form of controller and stresses keeping Next.js specific code within these boundaries.
  • 🔄 The process of updating group information is used as an example to illustrate the flow from presentation layer to business logic and persistence layer.
  • 🛡️ The importance of authorization checks within the business layer is highlighted to ensure that only authorized users can perform certain actions.
  • 🗂️ The script touches on the use of data transfer objects (DTOs) to prevent information leakage between layers and maintain a clean architecture.
  • 📁 The creator prefers a directory structure that collocates related components and logic to simplify maintenance and onboarding of new developers.
  • 🔍 The video ends with a discussion on the benefits of decoupling business logic from presentation, making it easier to refactor or change the application's approach in the future.

Q & A

  • What is the main concern of the video's author regarding their YouTube channel?

    -The author is concerned about maintaining relevance on their YouTube channel by regularly publishing content, as they have not published a video for about a week.

  • What architectural concept does the author discuss in the video?

    -The author discusses 'clean architecture,' a concept from a book by Uncle Bob, which emphasizes structuring code in layers with specific rules to avoid dependencies that can make a project unmaintainable.

  • What are the four layers mentioned in the layered architecture approach discussed in the video?

    -The four layers mentioned are the presentation layer, business layer, persistence layer, and database layer.

  • Why does the author recommend against making direct database calls in React server components?

    -The author believes that making direct database calls in React server components is not a long-term maintainable solution and suggests using a layered architecture for better control and separation of concerns.

  • What is the role of 'controllers' in the context of the author's discussion on code architecture?

    -In the context of the author's discussion, 'controllers' or 'server actions' act as the presentational layer that handles the interaction with the user and invokes business logic without mixing with Next.js specific code.

  • What does the author mean by 'use cases' in their code architecture?

    -In the author's code architecture, 'use cases' refer to the business logic that is invoked by actions and is responsible for processing data and applying business rules, independent of the presentation layer.

  • How does the author implement authentication and rate limiting in their server actions?

    -The author uses a library called Zsa to create middlewares for authentication and rate limiting, ensuring that only authenticated users can perform actions and that endpoints are protected against abuse.

  • What is the purpose of the 'persistence layer' in the author's architecture?

    -The persistence layer is responsible for providing methods to store and query data from the data store, abstracting away the specifics of the database or ORM being used.

  • Why does the author recommend collocating components and related files in the same directory?

    -The author recommends collocating components and related files in the same directory to make the project easier to maintain and understand, as it provides a clear association between related parts of the application.

  • What is the potential issue with not following a structured architecture like clean architecture or layered architecture?

    -Not following a structured architecture can lead to business logic being spread throughout the application, making it difficult to maintain, understand, and refactor, as well as increasing the risk of inconsistent logic across different parts of the application.

Outlines

00:00

📅 Consistent Content Publishing for Algorithm Relevance

The speaker begins by addressing the lapse in video publishing on their YouTube channel and the importance of regular content to maintain algorithmic relevance. They introduce the topic of discussing code structure, specifically the architecture of their Next.js starter kit. The speaker invites viewers to engage in a discussion about the merits or drawbacks of their approach, which is inspired by 'clean architecture' principles as outlined in a book by Uncle Bob. Key concepts include controllers, use cases, entities, and structuring code in layers to avoid dependencies that could hinder maintainability. The speaker also contrasts their approach with common practices of directly integrating database calls within React server components, advocating for a more layered and maintainable architecture.

05:02

🏛 Implementing Layered Architecture in Next.js Starter Kit

In this paragraph, the speaker delves into the specifics of implementing a layered architecture in their Next.js project. They describe the use of server actions as a presentation layer, emphasizing the importance of keeping Next.js specific code separate from business logic. The speaker illustrates this with an example of an admin modifying the text on an info page, explaining how server actions are used to handle user inputs. They also discuss the use of a library called Zsa for creating middlewares that handle authentication and rate-limiting, ensuring secure and efficient access to authenticated endpoints.

10:03

🛠️ Business Layer and Use Cases in Clean Architecture

The speaker continues by explaining the business layer in the context of clean architecture, where business rules and logic are separated from presentation and data persistence concerns. They discuss the use of use cases or interactors to handle specific business requirements, such as updating group information with proper authorization checks. The speaker also touches on the concept of role-based authorization, where different user roles have varying levels of access and capabilities within the application. The paragraph concludes with a brief mention of business entities, which encapsulate business rules but are not deeply explored due to the simplicity required for the starter kit project.

15:04

🗃️ Persistence Layer and Data Access in Application Architecture

This paragraph focuses on the persistence layer of the application, which is responsible for interacting with the database to store and retrieve data. The speaker describes methods for updating group information and the use of a repository pattern to abstract database operations. They also highlight the importance of keeping the business layer unaware of the specific database technologies or libraries used, which promotes flexibility and maintainability. The speaker provides a code example using Drizzle ORM to demonstrate how data access is implemented in their project.

20:05

📁 Directory Structure and Project Organization

The speaker discusses their approach to project organization and directory structure, advocating for colocation of related files to simplify maintenance and onboarding of new team members. They argue against having a monolithic components folder, preferring to keep components and related server actions within the same directory as the page or route they pertain to. The speaker also addresses the handling of React server components and the potential use of caching to optimize data retrieval. They suggest the possibility of creating a 'loaders' directory for functions that are called multiple times within the application to streamline data fetching and caching.

🔧 Refactoring and Maintainability in Software Architecture

In the final paragraph, the speaker emphasizes the maintainability benefits of keeping the business logic separate from Next.js specific code. They argue that this separation simplifies potential refactoring, such as transitioning from server actions to API endpoints, without affecting the core business logic. The speaker also warns against spreading business logic throughout the application, which can lead to inconsistencies and increased difficulty in future changes. They conclude by inviting viewers to share their coding approaches and engage in a discussion about best practices in Next.js development.

Mindmap

Keywords

💡YouTube algorithm

The YouTube algorithm refers to the set of rules that the platform uses to decide the visibility and ranking of videos in user feeds. In the context of the video, the speaker is concerned about maintaining relevance on the platform by regularly publishing content, as the algorithm may deprioritize channels that are inactive.

💡Nextjs starter kit

A Nextjs starter kit is a pre-configured set of tools, templates, and best practices to help developers quickly start building web applications with Next.js, a framework for server-rendered React applications. The video discusses the architectural structure of the speaker's own Nextjs starter kit.

💡Clean Architecture

Clean Architecture is a software design pattern that emphasizes separation of concerns and independence of the system's components. The book by Uncle Bob advocates for structuring code into layers with clear boundaries. The video references this concept as the basis for the speaker's code architecture.

💡Controllers

In software design, controllers are components that handle the logic for processing user input and managing the flow of data between the user interface and the business logic. The script mentions controllers as part of the clean architecture, where they act as an interface adapter in the presentation layer.

💡Use cases

Use cases in software engineering represent specific scenarios or actions a user can perform within a system. The speaker discusses use cases as a layer in the clean architecture, where they encapsulate business rules and are invoked by controllers to handle specific functionalities like updating group info.

💡Entities

Entities in the context of clean architecture represent the core business objects that contain the business logic and rules. Although the speaker mentions them, they opt to avoid using entities in their starter kit for simplicity, instead focusing on the use cases to represent business rules.

💡Layered architecture

Layered architecture is a design approach that divides software into distinct layers, each with a specific responsibility. The video describes a layered architecture with presentation, business, persistence, and database layers, which helps in maintaining a clean separation of concerns.

💡Server actions

Server actions in Next.js are functions that run on the server and can respond to client-side actions. The speaker uses server actions as part of the presentation layer, keeping Next.js specific code to the left of a conceptual line to maintain separation from the business logic.

💡Zod

Zod is a TypeScript-first schema validation library used for validating the structure of data. The script mentions Zod for ensuring that the data passed into actions matches a specific type structure, which helps in maintaining data integrity and predictability.

💡Persistence layer

The persistence layer in software architecture is responsible for the storage and retrieval of data from a database. The speaker discusses this layer as an abstraction that handles data operations without the business layer needing to know the specifics of the database technology used, such as Drizzle ORM or direct database interactions.

💡Collocation

Collocation in software development refers to the practice of placing related code together in the same directory or module to improve maintainability and readability. The speaker prefers to collocate related components and actions within the same directory as the page or route they are associated with, rather than using a separate components folder.

Highlights

Publishing a video regularly is important for maintaining relevance with the YouTube algorithm.

Discusses the structure and architecture of the code for a Next.js starter kit.

Emphasizes the importance of using clean architecture principles in coding projects.

Introduces the concept of layered architecture, including presentation, business, persistence, and database layers.

Recommends avoiding direct database calls in React server components for better long-term maintainability.

Advocates for separating Next.js-specific code from business logic to maintain clean separation of concerns.

Highlights the use of controllers and use cases to manage different layers in the code.

Mentions using Zod for authenticated actions and middleware creation.

Explains the process of validating and parsing input data to ensure it matches the expected structure.

Discusses the role of business logic in authorization checks and role-based access control.

Illustrates the persistence layer's role in handling data storage and queries.

Mentions the repository pattern and personal preferences for code readability.

Stresses the importance of abstracting ORM usage away from business logic.

Describes the benefits of maintaining a common architecture pattern within a development team.

Highlights the use of data transfer objects (DTOs) to prevent leakage of unnecessary data between layers.

Advocates for collocating components and server actions within the same directory for easier project maintenance.

Explains the process of loading data in React server components and the potential use of cache methods.

Emphasizes the maintainability and refactorability of separating Next.js-specific code from business logic.

Highlights the risks of spreading business logic across multiple layers and the importance of centralizing it.

Transcripts

play00:00

so I think it's been like a week since

play00:01

I've published a video on my channel and

play00:02

I figured I should probably get

play00:03

something published out there before the

play00:05

YouTube algorithm thinks I'm no longer

play00:07

relevant so I want to talk about the

play00:09

structure of my code and how I kind of

play00:11

architect the code for my nextjs starter

play00:13

kit um you can kind of take what you

play00:15

want from this you can in the comments

play00:17

you can say that this is dumb you can

play00:19

say hey I agree with what you're doing

play00:21

maybe we can have a discussion about it

play00:22

and just learn from it a lot of the

play00:24

stuff I'm going to be showing in this

play00:25

video do come from an idea called clean

play00:28

architecture there's a book called clean

play00:29

AR chitecture written by Uncle Bob we

play00:32

apply this or we try to apply this as

play00:34

best as possible at work so we have

play00:36

something called like controllers use

play00:37

cases entities and you kind of structure

play00:40

your code in different layers and

play00:42

there's certain rules that you follow so

play00:44

that you don't have dependencies that

play00:46

cause your project to be a little bit

play00:48

more unmaintainable there's also like a

play00:50

layered architecture approach where you

play00:52

basically have like a presentation layer

play00:54

a business layer a persistence layer and

play00:55

a database layer this is also kind of

play00:58

what I'm doing on my starter kit which

play01:00

I'll kind of walk you through the

play01:02

different layers that I have applied I

play01:04

highly recommend that you do something

play01:06

like this on your projects like I've

play01:07

seen a lot of videos and tutorials where

play01:09

people are just like doing uh Prisma

play01:11

calls or drizzle calls or SQL calls

play01:13

calls directly in their react server

play01:15

components when the page loads and

play01:17

although that works I just don't think

play01:19

it's a very long-term maintainable

play01:21

solution uh what you want to do is just

play01:24

apply some type of layered architecture

play01:26

so that you have more control over like

play01:29

who can read the data who can manipulate

play01:31

the data and you can kind of test your

play01:33

business logic separately from all the

play01:36

nextjs code okay so let's just draw a

play01:38

little bit so in my project I'm trying

play01:40

to do a layered architecture type of

play01:42

thing right so we have nextjs and to

play01:44

help kind of exemplify what we're doing

play01:46

um we are going to follow a use case

play01:49

where I'm an admin or an owner of this

play01:51

group and I want an admin or owner to be

play01:54

able to modify the text on the info page

play01:58

like over here if I say hello world

play02:00

I save this I want other users who are

play02:03

not admins or uh owners to be able to

play02:06

see this text when they go and view this

play02:08

group directly so in terms of that use

play02:11

case what we just did in xjs is what you

play02:14

probably know is I'm using server action

play02:16

so let's just go ahead and say we have a

play02:18

server action I'll make a little box

play02:20

here I'll say server action and this is

play02:24

one of the layers I'll call this a

play02:25

presentational layer you may also call

play02:27

this a controller uh it does doesn't

play02:30

really matter the point is you want to

play02:32

keep all your nextjs specific code to

play02:35

the left of this line right you don't

play02:37

want react cach methods or unstable cach

play02:41

methods bleeding over onto the right

play02:43

side of the line you don't want

play02:44

revalidate path or revalidate tag

play02:47

bleeding over cuz now at this point you

play02:50

don't have nice clean uh separations

play02:53

between like what's in charge of the

play02:54

presenting and the controlling of your

play02:57

your code and what's in charge of the

play02:59

actual business logic so that leads me

play03:00

to my next point is if I consider a

play03:03

server action kind of like a controller

play03:06

if you were to look at this layer

play03:08

architecture the presentational layer

play03:10

would probably be like react or your

play03:11

react server components that render out

play03:13

the page when you invoke an action again

play03:17

that's kind of calling a presentational

play03:18

layer uh controller again if you were to

play03:21

look at the clean architecture Circle

play03:23

your controllers in your presenters are

play03:24

all in this interface adapters right so

play03:27

the user needs to do something they are

play03:28

going to invoke some type of controller

play03:31

invoke some type of adapter and in our

play03:33

case I'm basically going to say that a

play03:36

server action is a form of a controller

play03:39

again feel free to call me out in the

play03:40

comments if anything I'm explaining

play03:42

doesn't seem right but this is just how

play03:44

I kind of think about it so when a user

play03:46

clicks on the save changes that invokes

play03:50

a server action which again is still in

play03:52

this nextjs layer over here so let's

play03:54

kind of look at the code I have a page

play03:57

and I click on a button and it calls an

play04:00

action let's try to figure out where

play04:01

that's happening I have an action here

play04:03

called update group info action now

play04:06

something that I'm doing in my codebase

play04:08

is I want to make sure that everything

play04:09

that goes in and out of this action is

play04:13

parsed right I want to make sure that

play04:15

the stuff that's sent in it matches a

play04:17

certain type of structure I want to make

play04:19

sure the group ID sent in is a number

play04:21

there is an info that's sent in that is

play04:23

defined and once I get that information

play04:26

I can kind of trust it and start running

play04:29

some controller type of code and again

play04:32

notice that my actions this is where I

play04:34

keep revalidate path or revalidate tag

play04:36

or redirect all of the nextjs SL uh

play04:41

react specific code I would probably

play04:43

keep it inside of This Server action now

play04:46

if you're curious how I'm using an

play04:48

authenticated action I'm using a library

play04:50

called Zsa which allows you to basically

play04:53

create middlewares where for example I

play04:56

have this authenticated action I create

play04:58

a procedure and anytime I want to create

play05:01

a action that requires authentication

play05:04

all I do is I just run some code make

play05:07

sure that the you know the cookie set

play05:08

the session is set I also do some rate

play05:11

limiting just so that all my

play05:13

authenticated endpoints are just rate

play05:15

limited by that user ID I don't want

play05:16

people trying to maliciously abuse my

play05:19

systems and then I return the user right

play05:22

so again this is still all in the the

play05:24

controller presentational layer if we

play05:27

were to go back to this diagram now at

play05:29

some point you'll see that after I've

play05:31

done the parsing of the input which

play05:33

comes from a form or whatever I'm

play05:35

calling something called a use case this

play05:37

is just my nomac claure of like how I

play05:39

like to denote this stuff and this comes

play05:41

heavily from the clean architecture book

play05:44

so all of my actions invoke use cases

play05:46

and these use cases leads us to the next

play05:49

point I want to make out and that is the

play05:52

business uh business layer a business

play05:56

layer and then inside of this business

play05:57

layer Theory clean architecture believe

play06:00

that he calls them use cases or slash

play06:02

interactors okay let's just look at this

play06:04

little diagram real quick and you'll see

play06:07

he calls the little orange red circle

play06:09

use cases and then that points to

play06:12

application business rules okay so from

play06:15

my perspective what is a business rule

play06:17

what are concerns of the business logic

play06:20

well the action all it's in charge of is

play06:24

basically taking in figuring out what

play06:26

the authentication methods are maybe

play06:28

you're using JWT Maybe using session

play06:30

storage doesn't matter the business

play06:32

layer does not care about that all it

play06:34

cares about is that you're passing in

play06:35

the correct data um that it would expect

play06:38

so in our case this update group info

play06:40

use case this needs a group ID and an

play06:44

info and it also takes in an

play06:46

authenticated user again this thing

play06:49

doesn't

play06:50

know if we're using jts or sessions it's

play06:53

just give me a user object and I'm going

play06:56

to use that user information to figure

play06:57

out if a user should have access to

play07:00

update the group info so what this does

play07:03

is basically we're going to pass over

play07:04

some user information the group ID

play07:07

whatever and we're going to invoke a use

play07:10

case again that I called um update group

play07:13

info use case I might as well go back to

play07:15

this one and change this one to have a

play07:19

name just call it uh this action here so

play07:22

this action is going to invoke this

play07:24

business layer use case interactor and

play07:27

this is where you want to run some type

play07:28

of authorization check so in my group

play07:30

finder application I have a lot of role

play07:32

base authorization checks in place you

play07:34

can be an owner or an admin of a group

play07:37

or you can just be a member of a group

play07:39

and if you're an owner or an admin an

play07:42

owner can actually go and delete the

play07:43

entire group an admin just has the

play07:45

ability to modify you know some info

play07:48

they have the ability to come in here

play07:50

and maybe promote other users to an

play07:52

admin or demote users Etc but the owner

play07:55

has you know very special privileges

play07:57

that you have to encode this in business

play07:59

logic somewhere and so how this works is

play08:02

basically this use case is where you're

play08:05

going to run your you know authorization

play08:08

checks is this user an

play08:11

owner or an

play08:13

admin and then if they are you want to

play08:16

be able to allow them to update this

play08:18

information so let's look at the code

play08:19

because it's actually super super simple

play08:21

so basically we first we fetch the group

play08:23

by group ID if there is no group I throw

play08:25

an error and again you should not be

play08:27

throwing any type of nextjs specific

play08:29

erors here you should not be calling

play08:30

redirects or anything keep it simple

play08:33

throw errors or return some type of like

play08:35

error objects if you need to and then

play08:38

finally I do another check that says if

play08:40

I'm not the group

play08:41

owner go ahead and throw an error that

play08:44

has unauthorized and then finally if

play08:46

everything passes we just go ahead and

play08:48

update that group info using that info

play08:51

that was passed in now

play08:52

technically there is more to clean

play08:54

architecture if you look at this uh

play08:56

diagram there's something called

play08:57

entities and I'm a voiding entities just

play09:00

to keep it simple for my you know oneman

play09:02

Team starter kit project but at some

play09:04

point you may want to encode business

play09:06

rules to say info can only be 1,000

play09:11

characters right that is business rules

play09:14

that the business people can Define and

play09:16

I know when I say business like I'm just

play09:17

talking about myself because this is

play09:18

like a oneman project but when you work

play09:20

on a bigger team like there's people who

play09:22

are going to Define how large how much

play09:25

data some of the stuff can be what is

play09:27

the shape of this data is it a first

play09:30

name does it need to be all capitalized

play09:31

does it need to be proper case stuff

play09:33

like that and so sometimes what you want

play09:35

to do is you want to

play09:36

encode this information in something

play09:39

called business entities and those

play09:40

entities can have validation rules

play09:42

themselves okay now I'm not going to

play09:44

dive into that because again I'm not

play09:45

doing that so basically I just call a

play09:47

method called update group and this is

play09:50

something called a persistence method so

play09:52

this is going to lead us to the next

play09:55

section of our

play09:57

code which is the

play10:00

persistence layer okay let's look at the

play10:03

layered architecture we are over here we

play10:04

just talked about the business layer a

play10:06

little bit now we're at the persistence

play10:08

layer and the persistence layer's goal

play10:11

is to basically give you methods that

play10:13

you can invoke for storing and quering

play10:16

data from your data store whether that

play10:19

be in postgress whatever so we

play10:22

have a method over here called update

play10:24

group so I'll just go ahead and make a

play10:27

little box here that's going to call

play10:29

update group going to pass in some

play10:32

information such as a group ID some info

play10:34

and there's different ways you can do

play10:36

this a lot of people use something

play10:36

called like a repository pattern where

play10:39

they'll have like an object called I

play10:41

don't know groups and they'll say update

play10:44

and they'll pass it whatever information

play10:46

something like this I think is just not

play10:47

as friendly to read versus something

play10:49

update group that's just a personal

play10:50

preference it doesn't really matter but

play10:52

the point is is that this layer is

play10:54

responsible for knowing how to write to

play10:57

your database which in case I'm using

play10:59

drizzle but something I want to point

play11:01

out is that you don't even know I'm

play11:03

using drizzle right the business layer

play11:05

has no concept of what omm you're using

play11:08

what libraries you're using to actually

play11:10

persist the data all it knows is that I

play11:12

need to call some method to save this

play11:16

group information okay that's been

play11:18

abstracted away about how that actually

play11:20

happen so let's dive into this code real

play11:22

quick and you'll see over here I have a

play11:23

data access folder with like all of my

play11:25

tables I basically have like a one to

play11:27

one mapping of a file maps to a table in

play11:29

my SQL database you can structure this

play11:31

however you want but the way I typically

play11:34

do this is I have a groups file and this

play11:37

has an update group function which

play11:39

allows you to basically take any group

play11:41

ID and just update any parts of it right

play11:44

and so now you start seeing some code

play11:45

that might look familiar to you which is

play11:47

drizzle orm so if you go up to the top

play11:49

here you'll see that I am just defining

play11:51

like a drizzle orm connection and in

play11:54

fact let me uh how do I go to the

play11:56

definition here I think just scroll up

play11:58

you'll see I'm import reporting this

play11:59

from DB which is bringing us some

play12:02

drizzle orm stuff okay so this

play12:04

persistence method this persistence

play12:06

layer knows about how to write and

play12:09

persist the data which if you look at a

play12:11

layered architecture now we're hitting

play12:13

the database layer something that we're

play12:15

using a library we're using knows how to

play12:16

write data let's just go ahead and put

play12:19

another line here and this is going to

play12:22

write directly to a database just draw a

play12:25

database symbol here here we go I'll

play12:28

just say post CR cuz that's what I'm

play12:30

using so if we zoom out a little bit

play12:32

this is the pattern that I try to follow

play12:34

in my applications this is just the way

play12:36

I've been brought up when learning how

play12:37

to code people smarter than me have

play12:39

taught me this pattern I believe them

play12:41

and I follow the pattern too not blindly

play12:44

but I do try to follow it because I

play12:46

personally think it is useful and some

play12:48

of the reasons why I think following

play12:49

either clean architecture or layered

play12:51

architecture or some type of pattern is

play12:53

a everyone on the team has a common

play12:57

pattern they can follow if you're

play12:59

working on a project by yourself I mean

play13:00

you could just raw dog SQL right in your

play13:03

react server components if you want to I

play13:05

probably would not recommend that

play13:07

because if your project actually takes

play13:08

off and grows you're going to want to

play13:10

bring in other engineers and you want a

play13:11

Common Language such as an architecture

play13:14

that you can all discuss things about

play13:17

now some teams and projects like to make

play13:19

this even more complex which I'm on the

play13:22

fence of a lot of teams will in between

play13:23

these two layers they'll do something

play13:25

called data transfer objects DT's to

play13:28

make sure that that certain things Never

play13:30

Bleed over so what I mean by bleeding

play13:32

over so for example in the business

play13:33

layer let's say you're fetching

play13:35

something from a persistence layer an

play13:37

update group happens to update the group

play13:38

but then return the whole group object

play13:40

back you could potentially have a bunch

play13:43

of extra fields on this database that

play13:45

you haven't actually like cleaned up you

play13:47

could have like a group name a group

play13:50

display name all this other stuff and

play13:52

you may not want that to ever bleed over

play13:55

into your business layer right so

play13:56

sometimes what you do is you'll create a

play13:58

data transfer object so that all the

play14:00

data is basically sent through a mapper

play14:03

to make sure that you only return the

play14:05

things that are necessary from your

play14:06

persistence layer back to your business

play14:08

layer and again you want to make sure

play14:11

that you only return certain things from

play14:12

your business layer back to your

play14:14

controller or back into your react

play14:16

server component so that you don't leak

play14:19

sensitive information or information

play14:20

that's not a concern of any of these

play14:23

layers one good example I've seen is a

play14:26

lot of people who use

play14:27

mongodb a lot of the times the unique

play14:30

identifier for your IDs is underscore ID

play14:34

and they'll start bleeding that over

play14:35

into every single layer and you can

play14:37

actually look at the network request and

play14:39

see oh all the entities have an

play14:41

underscore ID on them I'm going to

play14:42

assume you guys are using mongodb right

play14:45

because that wasn't really abstracted

play14:47

way too well and using something like a

play14:49

dto could potentially help prevent

play14:52

leaking information about your you know

play14:55

your underlying architecture of your

play14:56

system maybe you don't want people to

play14:58

know you're using mongus because that

play14:59

opens up more security attack vectors of

play15:02

how they can abuse your system stuff

play15:04

like that so that's kind of how I would

play15:06

recommend at least Loosely following a

play15:08

project the only dto stuff I end up

play15:11

doing is with these actions over here I

play15:14

can actually put an output as well and I

play15:17

can specify what the output needs to

play15:19

look like so over here I can say well I

play15:21

just want this to return uh nothing I

play15:24

could say void of undefined or something

play15:27

and that at least gives me a little bit

play15:29

of certainty because Zod is going to

play15:31

verify that whatever I'm returning here

play15:34

it has to match undefined if I were to

play15:36

accidentally take the group that was

play15:38

being returned here and let's just say

play15:40

like name is Bomb notice that I get some

play15:43

typescript errors that says hey you're

play15:44

returning some information where you're

play15:47

not supposed to be returning that

play15:48

because you specified an output of

play15:50

undefined something like that can be

play15:52

very useful and again this is basically

play15:54

a dto it's just that we're using

play15:56

libraries that just automatically do it

play15:58

for us and I don't want to waste a bunch

play16:00

of time writing the mapping code and

play16:01

writing the you know the dto objects

play16:04

with classes and stuff like that now

play16:06

before I wrap up this video I do want to

play16:07

kind of touch a little bit about the

play16:09

directory structure of my projects I try

play16:11

to collocate as much as possible so if

play16:14

I'm looking at a page right here for

play16:16

example I'm on my group info page

play16:19

everything that's on this page should be

play16:21

in the same directory as my info route I

play16:24

don't like having a separate components

play16:26

folder where I put all those and then I

play16:27

import them I want all my components

play16:30

that are related cooc in this directory

play16:33

because I think it makes the project a

play16:35

little easier to maintain at work we do

play16:38

an approach where we have like every

play16:39

folder holds different things and those

play16:43

things are separated by type and you end

play16:46

up having this folder that has 500

play16:47

different files in them and there's like

play16:50

no good way to really understand like

play16:52

what is using this thing okay so if it's

play16:55

not an actual res sharable reusable

play16:57

thing then I would just co-locate it for

play17:00

example typically server actions have

play17:02

stuff like this like revalidate path

play17:05

which means they are very tightly

play17:06

coupled to the page or the route that

play17:08

you're on so it makes sense to just put

play17:10

your actions directly in this folder

play17:12

also with components this is the only

play17:14

place in this entire application I have

play17:16

a component that looks like this so I'm

play17:18

not going to go and find my components

play17:20

directory and put it in there I'm going

play17:21

to collocate it so that if I were to

play17:23

onboard someone under this project they

play17:25

can also have a common Convention of

play17:27

like okay I'm looking at the info page I

play17:30

have 95% certainty that every component

play17:32

that's needed for this info panel is

play17:35

probably going to be right in this

play17:36

directory now another thing I'm still

play17:38

trying to figure out how I want to

play17:40

approach this is basically the react

play17:42

server components when you hit a page

play17:44

you typically need to load some data and

play17:47

so the way I've been doing this is I'll

play17:49

just call these use cases directly here

play17:52

uh before I call the use case I will

play17:54

basically get the current user from a

play17:56

session and then I'll call the use case

play17:58

and pass it the current session and then

play18:01

also I'll pass it the um whatever

play18:03

arguments I might need and that'll

play18:04

return some data for me right and I can

play18:06

kind of throw errors inside the react

play18:07

server component where I could actually

play18:09

have like an errors. TS file handle that

play18:12

and display something else uh the kind

play18:14

of exemplify what I mean so this is the

play18:17

info page it takes in a group ID which

play18:19

is loaded from the URL

play18:21

here and I basically take that and I

play18:24

call a git Group by ID use case let's

play18:27

just go ahead and go through the whole

play18:28

flow real quick quick so you understand

play18:30

uh I call this git Group by ID use case

play18:33

in our case I probably should do some

play18:35

type of authorization here to make sure

play18:37

that there's no like sensitive data

play18:39

being leaked but um again you can kind

play18:41

of search for a lot of these groups

play18:43

publicly and that ends up calling a get

play18:45

group by ID method which calls a drizzle

play18:47

method to get the first group by an ID

play18:50

okay and I don't see any issues with

play18:53

doing this as long as you just you know

play18:55

make sure you're passing in the user

play18:56

sessions again because you're calling

play18:58

the user case during your react server

play19:00

component and all the business logic is

play19:02

kind of encapsulated there now something

play19:05

that I might at some point end up

play19:07

needing is when you have a complex tree

play19:11

and you have to call the same method

play19:13

multiple times and your react server

play19:15

components typically what you want to do

play19:17

is you want to call a cache method which

play19:19

is built into next I'm sorry this is

play19:21

built into react actually um so you can

play19:23

bring in a cache method from react and

play19:26

you can wrap your functions with this

play19:28

cach uh Factory function

play19:31

basically so that as it's traversing

play19:34

your react server components if it finds

play19:36

the same method it's just going to use

play19:38

the cach results for that individual

play19:41

request that your user made and then on

play19:43

the next request if a new user comes in

play19:45

it hits the same page brand new cache

play19:47

it's not going to reuse the old stuff

play19:49

okay so in some cases you may want to do

play19:51

that and I might actually add a new

play19:54

folder here or a new file called loaders

play19:57

and then I would probably just make a

play19:58

new fun function here called like uh get

play20:00

group by ID

play20:02

loader and then you could basically um

play20:05

you want to do this I could just set

play20:08

this to

play20:09

Cache like this and then I could just

play20:12

have this taken a group

play20:15

ID like so and then you know call the

play20:18

use case in here goad and say return get

play20:20

group by ID use case something like that

play20:23

and then also in the loader you could

play20:24

potentially get the user so get current

play20:26

user like this

play20:30

so that I can pass it in and then

play20:33

obviously we want this to be an

play20:34

asynchronous function so we do this if

play20:37

for some reason you do end up needing to

play20:38

like wrap stuff with the nextjs like try

play20:41

to avoid going into your use cases your

play20:43

business layer and like trying to add

play20:45

cache methods here because again you're

play20:47

breaking that Convention of like now

play20:48

you're running a react code inside your

play20:51

business

play20:52

layer okay so technically if I were to

play20:55

do that refactoring I could just call

play20:56

that loader here and then I can get that

play20:58

information information and use it so I

play21:00

want to talk on one last Point as to why

play21:02

I think this is more maintainable and

play21:04

the reason is if you keep all your

play21:07

nextjs code out of your business layer

play21:11

what you can do is if down the road you

play21:13

decide that you know what server actions

play21:15

isn't it server actions is just not

play21:18

worth doing it's causing more headache I

play21:20

rather go back to use form Hooks and go

play21:22

back to uh react query and just hit API

play21:25

in points well the refactoring involv D

play21:28

with doing that is very simple cuz now

play21:31

you have this method that it doesn't

play21:33

care about anything in nextjs it doesn't

play21:35

care about how you're doing you know how

play21:38

you're handling your your actions or

play21:41

your post requests or mutations it just

play21:44

needs some input and so if later on you

play21:46

decide you know what I actually want to

play21:49

make this an API inpoint well it's super

play21:52

easy to just you know have a new API

play21:55

inpoint

play21:56

directory and invoke this which makes it

play21:59

very convenient and a lot more

play22:00

maintainable another thing that I've

play22:02

seen in some projects is if you don't do

play22:04

some type of approach like this you end

play22:07

up taking your business layer like your

play22:09

your your logic that defines like when a

play22:12

user should read something and you end

play22:14

up putting it everywhere like you'll

play22:16

take this you know there's some certain

play22:18

rules that you need to check to see if a

play22:19

user has access to get a group and

play22:21

you're like oh I'll put it in this react

play22:22

server component and then later on

play22:24

you'll decide that you need that same

play22:26

logic and you'll put it in another place

play22:28

maybe you also need in front of the API

play22:30

but then you have all this business

play22:32

logic that's kind of sprinkled out

play22:33

throughout your controller and your

play22:34

presentational layer which you'll start

play22:37

updating in one place and you'll forget

play22:38

to update it somewhere else and then

play22:40

it's just a big headache because you've

play22:42

basically spread out all of your really

play22:44

your core logic throughout your entire

play22:47

application it becomes very very hard to

play22:48

maintain and understand and also to even

play22:51

switch off of in the future if you

play22:54

needed to for some reason um anyway I've

play22:56

been rambling for a while now so I hope

play22:58

you guys enjoyed watching this video

play23:00

again I just wanted to get something out

play23:01

there for you all and hopefully this

play23:03

elevates your knowledge in coding a

play23:06

little bit again leave a comment if

play23:08

there's a different approach you like to

play23:10

make your applications if you're using

play23:12

nextjs with server actions let me know

play23:14

we can have a discussion about it other

play23:16

than that have a good day happy quing

Rate This

5.0 / 5 (0 votes)

Etiquetas Relacionadas
Next.jsClean ArchitectureCode StructureLayered ArchitectureMaintainabilityServer ActionsReact ComponentsUse CasesBusiness LogicData Persistence
¿Necesitas un resumen en inglés?