You are as knowledgeable as the senior dev I work with. I look at people like you and wonder if I will ever reach this level, I feel I'm too low iq to ever evolve this far.
I am more Angular for Enterprise level architectures, mainly because of its module and integrated DI. For Solid on frontends (or flutter) I do : DataSource (API/DB/Cache) Repository (Data fetching strategy) UseCase (must be a user action) Controller (State management)
@@invictuz4803 The repository is the only one that can talk to the database. So that if you change your database, you just need to update the repository. Repository will guarantee output in a certain format but input can be updated if anything db related updates
@@invictuz4803 repository is also responsible for the cache management. So it may use local datasource's or remote datasource's. Domain/Core layer may only ask for a refresh as it is a User conceirn but does not know what it means on infrastructure side. As repository is outside the application core, and close to infrastructure, it knows, for example, connectivity status.
when it will come to unit test this approach will show all its might and benefits. It should be sooo easy to mock any dependencies and it will look very clean.
If you have the time to spend feel free to do this amount of abstraction, it's only over engineered if you have other priorities / work is paying you to deliver value and other features need to be delivered. 100% there are benefits of this.
Very nice! I definitely agree dependency injection is the way to go on large apps. Specifically inversion of control. Seems like we've gone full circle on PHP now :D Good times ahead
IoC is key on large projects, as technical debts, due to coupling, can quickly become unmanageables. Lazy resolving can have a huge impact on performances. Of course, if you plan to test unit or use TDD, it become mandatory. As an architect, making large apps is also planning for maintenance and refactoring.
Great video as always, a request, a video on setting up ci/cd with different envs (dev, stage, prod) from scratch for a react/next project would be total bomb at this point...
It's similar to what we do in the .Net projects. Initialy it is slower to get everything together, but in the long run it's much easier to maintain and fix bugs.
I'm using the app router a lot lately too, i like to store the components (the things that the page depends on as you said) in a _components folder, just so you can properly see the page.tsx and inside each folder you have things separated.
yeah I was doing that at one point as well, but now I just dump everything in the same folder unless it gets filled. Having parallel routes also help prevent needing _components
@@WebDevCody Yeah, but correct me if I'm wrong, but I didn't see a case where you had a page folder, and then a component you extracted into that folder. You only had pages in the subfolders... e.g. you didn't have something like app > dashboard > _components , some-page.tsx.
@@untalentedwebdev parallel routes involve adding a @routeA @routeB directory where you can split your page into multiple sub components, so imagine a dashboard with 2 charts, each chart could be a parallel route which allows you to colocate your components inside those parallel route directories
This was super helpful and even better that you included the source code for this. Would love to see an example of this using Vertical Slice Architecture using features instead of use cases. Keep up the amazing work!
I do the same thing. I usually use pages directory so I can't colocate files in the pages folder. So I mirror the route structure of pages inside my components/pageComponents directory. This allows me to colocate any related components in the ./Subcomponents folder right next to any component. Also, I just name the component file as index.tsx. That way it picks up the name of the parent folder, which is the name of the component. Super clean
Do you use Ctrl + P to search your files? I feel like searching index.tsx would be such a pain. You could always search the folder name, sure, but it might return results from the pages folder as well.
Something you might noticed. If we are to add data access to other entities we need to add another data-access file to talk to db specifically for that entity... almost duplicate code. I usually use generics to pass entity type. Thou not sure if it's possible with drizzle api.
Congrats for learning and sharing clean code and good practices to the community There somethings that I saw on your code and I would like to point out Careful to not use the entities as object literals. At 19:56 in line 127 you could have used a method for this, .increment() You should allow only methods of an entity to change it's internal state. Avoid setters, using setters you can easily break the internal state by passing an invalid value. Keep the business rules of an entity inside the 'rich methods' Also, there is a miss conception about entities internal state, you have a method validate, that should not exist A entity INSTANCE should be enough to know that that thing has valid state The validations should lie on the constructors, and on the methods, they have the responsability of keeping the valid state, otherwise they should throw So if you got in hands an instance of something, you can trust that thing has a valid state 14:20 yes that belongs to the entity. If you use only internal entity values, ask yourself what are you validating, then name your method like so. That validation could be way more verbose, and you would prefer to abstract that
@@WebDevCody No, say you have a class (Value Object on this case) that represents a month of the year. One of the internal value is the monthNumber, that has to be a number between 1 to 12. If you wanna set a default value when instantiate, the validation if(monthNumber12) throw should be in the constructor a nextMonth() method should contain some logic like if(this.monthNumber===12) throw It's a change of mindset, you don't post validate the action you performed, you can't even perform the action if it leads to a bad state this way you ensure the instance ALWAYS have a valid internal state, you should never worry about the internal state validity of an instance This is a DDD concept, and I learned it in Portuguese, so I may struggle to translate some terms properly
I do something very similar and love it. I have a seperate components folder for every different route(or page) of my app, like about, home, workouts, dashboard, profile, etc
Awesome! I think it's better to validate entities using the constructor rather than having the client call a validate method and create another instance instead of modifying the existing entity. I prefer naming the class without using the term "entity." For example, "Item" can be considered as the entity, and "ItemData" can be used to manage data from infrastructure like a database or third-party API.
@Web Dev Cody here are my thoughts for what they are worth (if worth at all): Your approach goes somewhat on the opposite of what is recommended on next.js app router docs. Next.js has file based routing and so the approach of separating files by some imperative definition of "responsibility/what-it-does" can cause problems with the router and add layers of complexity that could be avoided by following the recommended approach of grouping files by their "domain/feature". @Theo has a take on this that I recommend you giving some thought. As I understand your approach goes in line with what the marked is used to do and have lots of theory behind, but I tend to look to the side that the modern days are pushing us into a different scenario on which grouping files by domain makes more sense on the long run.
hot take: where you put files in your project has very little impact on maintainability - consistency is more important. Also, the core of what determines if something is maintainable is the interdepencies of the modules and trying to abstract layers where needed so that refactoring isn't a pain in the future. Additionally, if your team agrees unit testing is important, this also plays a role in how you structure some of your modules. I know you think the next docs are great, but point me to anywhere in the docs where they mention how to add automated testing over your next app - they don't. Take that information as you will, but I take it as a sign of they don't care about testing nor explaining good ways to structure your code to make it easier to test. At work I have a project that groups all files by "type". We have directories with over 200 files of type "Action", 200 files of type "UseCase". It's the last thing I think of when I say "man, this system is complex".
@@WebDevCody Not a hot take at all, as I see this is where the market agrees, and I do to. Most teams just decide on which file/folder structure works for them based on their own abstractions and this was and will continue to be what works for most teams. And yes I may be biased towards the docs, but don't want to be bound by it, I'm just trying to understand the intent behind the docs, so I can avoid working against this intent. Also I may be biased about not giving the same value to automated tests as most of the market does, and this may be one of my mistakes. Thanks for expanding on your point of view, this definitely helps me understand the topic better.
@@wesleycoder sure thing, also remember I work full time on a project with 500k lines of code and 10 devs working full time for 5 years, so my perspective on “good architecture” is scoped around that. If you are a one man team and don’t care about unit tests, then I think their docs are fine
you should add some unit tests to really highlight the advantages of this approach. Although personally I feel like this is a relic of the old days where unit tests were all the rage. Doing a variable name change/adding a new property for an entity, for example, is gonna be a nightmare. You'll have to change it in so many places to keep it in sync.
That's why you have mapping from persistence / application / dto layers, so that yo ucan limit the blast radius of this kind of changes. If you don't use clean arch and stuff like that it's actually HARDER to refactor, especially if those changes include business logic. But this comes at the cost of more boilerplate and abstraction layers, so adopting those practices really depends on the project and the team. If the team isn't comfortable with clean arch, it will slow them down significantly and leave a bitter taste in their mouth.
Yes! I was thinking the same. Being self-taught, this (in depth clean architecture) is something that is really difficult to find (and make sense of). I'd be happy to pay for a course or comprehensive tutorial on this.
I'm still trying to figure it out after 5 years of using it professionally. There are some aspects to clean architecture I'm rethinking because I find it sometimes adds little benefit with a lot more complexity
Great example, however it gets very complicated very quickly... Listen to what I encountered in a not-that-extensive project. How would you go about if you had multiple roles where each role can interact differently with the entities? Specifically what I recently had in a project, lets say we have a User entitiy with 2 different roles, but now we have a use case - if the user is an Admin then he can update their finance settings - where the finance settings are only a few fields of the User entity. Do you now create a separate Admin and User entitiy, or is it just one User (general user) entity? Because, although it's the same table, the "User" role should not have access to most of the attributes in that table. And from what I tried, if we put it all in a single entitiy, it's hard to separate what each role can do. In that same codebase we also had a case which we can maybe translate to your example. Lets say the entitiy for Item can now be created only by admins, but can be viewed and added to their cart by Users. Same question, are those 2 separate Item entities now or just one? On simple examples like what you have shwn it works great, but as more features are required this strongly feels like it would need kind of inheritance if we wanted to be consistent... but then we fall into the pit that is OOP and we kind of lose the beauty of React...
I love clean architecture (even in the front with TDD so you don't manually test stuff all the time) I love to couple it with redux too as part of my core. Do you have stuff like that too or videos in preparation around that ? I'd love to see how you tackle it yourself since the few videos I've seen of you are of top quality
Is this a good approach? /src/app/home -> page & layout /src/app/components/home -> components related to home /src/app/services/home -> API services used on home
So here's my question... say I want to apply clean architecture and have things in modules... like items, users, etc. In your items schema definition, you have a usersId which references the users.id field in the users schema. So this requires importing the users schema from a different module. In a modular file structure approach, how would you import this users.id into your items schema definition?
It might be me doing things wrong. But as far as js/ts goes most of clean architecture concepts will cause you more issues than it would solve. An example would be using entities classes but then you fall into the issue of state serialization, passing entities from server components to client components. A lot of redundant code... Etc
I don't think entities should be leaving your business layer into your next code, you should be using a DTO for that. It's the same as if you were invoking an API to get data; you only get back primitives.
Love the content cody! Would you guys say tRPC is still a valuable option for the backend? I feel like it gets difficult to manage when the project gets bigger
I really appreciate this video and personally learning a good amount from it so thank you. Wouldn't it make more sense to instantiate an initial class that has all the context of different methods and then use that in your actions instead of always importing all the methods into context in each server action? Re: @9:53 ish
We do that at work and we ended up with a file with hundreads of methods on it which causes tons of issues with performance when running unit tests over our interactors. Every time jest runs it has to reimport hundreds of modules and it takes like 7 seconds to initialize a test file. This would also apply to performance issues with next, if every route needs an object with hundreds of imported modules, every page will become huge
Great video! You mentioned clean architecture - could you recommend any additional reading materials on this topic? I read 'Clean Code' quite some time ago. I'm guessing many of these concepts are derived from that book, right?
Clean code is a decent one if you don’t follow it like gospel. I don’t read too many books honestly, I find just having a conversation with chatgpt the best approach to get past the fluff they often add into books
Love your content! One thing I've noticed is that your code blocks has some colored stuff indicating where it starts and ends, is that a vscode extension?
imo this kind of things are just madness in any “frontend” project. If you really care and need this level of abstractions and separation of concerns, just have that in a completely separate API and use the endpoints in your frontend instead of talking directly to a DB
Hm I dunno about this Cody. If you know from the get-go that you'll need this level of architecture for your logic (DTOs, repositories, entities etc.) I would probably put it in NestJS straight away and have NextJS be a dumb frontend. In any other case, I would start out with some good-enough seperation of concerns in NextJS and just migrate the logic to NestJS if necessary.
Is it possible to make it so your server actions have no access to the code inside of the data access directory besides a newed up singleton that conforms to a specific interface? Architecture is nice, when people actually follow it. I think making it so users don't have any access to the data access layer from the server actions is a good way to remind them that they need to follow architectural principles.
I am wondering if once you start using SST it makes sense to build your apis directly in api gateway and lambda. It’s marginally more complex but there is way more flexibility and you can easily have them access the AWS services too.
Nothing was mentioned about type definitions (at least at the point I'm at) but I'm noticing most files have type definitions directly in them. So are you defining types exactly where they are needed? or is there any central location of types that are used in multiple locations? Wondering how that fits in with separation of conerns
Everything looks pretty straight forward except the DTO part. It just feels a redundant abstraction upon ORM. The ORM itself is already a DTO. I felt like the DTO part makes more sense if you are doing SQL calls without any ORMs. I actually saw similar pattern when people create a center API entity on top of every API calls. Sometimes it's good, but most of times are really bad. But I do feel your internal struggle when you mentioned the DTO.
Great video. Vid request. Can you do a video on controlled and uncontrolled components? This is one of those react concepts I have never really understood.
no, that's a normal reaction. Like I said, this is for projects where you might have lots of developers pushing features and modifying existing logic. As the system gets larger (imagine 5 years of code), it's very easy for devs to start copying + pasting logic from one server action into another and then your business logic is duplicated everywhere in your code base. So then when your product owner says "I want to allow decrement to be negative (to act as debt), your team copied and pasted the decrement logic in many different places and now it's a pain to figure out where it all needs to be updated and fixed. Obviously this is a simple example, but at work we have a "CourtCase" entity with over 70 fields, and many of those fields are cross dependent on other fields. So without following this type of pattern, a developer might modify modify a field on and object and saves it to the database then your database might have a bunch of invalid data because no one was actually validating the entities in your project, then when a user tries to load the court case to view details about it everything crashes because your database has invalid data. hard to explain in text but there are good reasons to do this stuff
@@WebDevCody Thanks for the detailed explanation, this is the first video I've seen yours so i assumed you were targeting beginners but it seems your audience is more junior developers with some experience.
@@geraldseydoux2252 he has some extremely good videos for beginners if you scroll through his channel. lately has moved to some little more advanced stuff. anyways whatever your level might be i suggest you to have a look at his channel cause the topics he deals with are a lot and very interesting and his way of teaching is one of the best i have ever seen on youtube (especially his approach of live troubleshooting and analysis).
Thanks for the great content! One question about the use cases. I've noticed in every use case you perform authorization check. Does this type of logic generally part of every use case? Also, considering that usually one would also authorize user on a routing level, isn't that redundant? Thanks
Ultimately you’ll end up adding some type of authentication or authorization check somehow. I find the code much easier to follow if you keep it redundant and imperative compared to trying to find a cute middleware or decorator pattern which obscures the core business logic of the use case.
Having “entities” (classes with methods, etc.) is going towards an OOP approach vs a more FP approach, correct? I find them interesting and seemingly useful, but I haven’t come across them or used them in my own React-based applications yet. I have essentially the “dto” objects passed around in my app (after an adapter/mapper converting BE format to FE format), and then use separate “helper” functions as needed (rather than methods on a class). What are your thoughts on the different approaches?
I think clean architecture mainly applies mainly to your backend / database logic. Applying this to react would probably mean using redux or mobx. Keep all logic and state out of your ui components would be the way to apply clean arch to the react layer
@@WebDevCody I’ve recently shifted to breaking my react components into a “data handling” sort of layer/wrapper and then a UI/View later, which seems to be a step in the right direction (seems to allow for easier testing, ability to utilize StorybookJS easier, etc., as I can then use just the UI/View component for those things). I’d enjoy a video on that concept (vs having it all within a single component/file, which is the more natural way for a beginner to structure things I think)
I feel like you shouldn't make setters and just make the variable public unless you need to manipulate what you're going to set it to, AND you don't need to manipulate the return value for the getter (if you have one), I also think that you should use `get X` (actual JS/TS getter instead of `getX` (method) and `set X` (JS/TS setter) instead of `setX` (method).
Normally, you could just add getters to private fields, although "setters" should be a method that has business logic inside with validation, although fields shouldn't be public to avoid mutation on that object anywhere, only in the methods with logic that needs those fields. For example in the video: Near 10:30, there should be, item.Increment, and inside that method validation and logic that increments.
This video came at such a perfect time, thank you for making it!. I'm currently refactoring my code to include a data access layer after reading the security blog post from next.js and I was really confused about how to set it all up. At 9:15, I'm a little confused where you pass in the data layer as context in the use cases. That seems to be used with server actions, how would you do this if you're only using api routes with next.js? You were seperating your server action from your business logic, how would you seperate your api from your business logic?
in next you'd have your router handlers, and you'd need to invoke your use cases from your router handlers. It's the same deal, take your request body / query / params and pass them as arguments to your use case, and inject any dependencies into the use case from your request handler
Any reason to not have a _components folder under each route to keep it clean? I prefer having only the special Next.js files under the actual route and everything else in ignored folders.
Hey Cody maybe you've answered this in another video but after finishing a pages router project, i have decided to start a new project with T3 and the app router and I have a question. Do you think that trpc is still needed or is there a better way to build endpoints now
@@WebDevCody I've been working on it the past week and I slowly came to the realization that tRPc is not needed, as what I ended up doing now is using the tRPc queries and mutations inside of the server actions which so I guess why not directly write the code in the server actions.
I am a bit lost when it is client and when it is server. For example when creating a new item, the pantry-tracker/src/app/dashboard/_actions /create-item-action.ts is this running on client or server? I am following it till updateItem in src/data-access/items.ts but it is not clear to me. Can you please explain this? Looking good man.
server actions run on the server. next abstracts the code by allowing the server action to be invoked via a POST request, and calling the server actions from the client component does an HTTP POST request for you under the hood.
@@WebDevCody Thanks a lot for this amazing video! Could you please tell me what do you use for displaying charts in a dashboard? Have you tried Tremor and shadcn/ui together?
Hi Im a junior dev who studied C# in the past. This architecture feels extremely familiar with dotnet. Is the "use-case" equivalent to "services" in dotnet?
Usually no, because that means your use case isn’t a single use case anymore. Usually I’d just make business helper functions that two use cases can reuse if needed
Why when you added cheese in a first seconds of your video, your all table including tabs updates? It should update only a row not a all component. Take a look on a profiler it something weird going on there
I never had a real developer job with a team but at college something that always bugs me is.. - if I am a front end dev and need to check if a user account registration was successfully sent to database, or if a user modified something on his account (so rewriting information to database), how can I test this functions and see that was successfully rewritten but without modifying the real database? I have this question because I am working in a project for an apprenticeship services and they use Firebase, atm I am using Firebase emulator to test my functions and anything related to database and even authentication, but how do I test in the real one?
I am asking this because normally a person developing an app is always deleting information from database after writing, vice -versa. Firebase has the emulator, but if a dev is working is a project that was already there and in production, how can you test this things?
@@WebDevCody but could you, one day, make a video about how would a developer test codes that is related to write on database without affecting the mai database that is on production. It is really hard to explain what I am trying to say as English is not my first language Cody. Sorry for taking your time
Awesome! Really love the part about SST, Docker, and Workflows. Infra is usually being glossed over.
People should throw money at you for this content to be honest.
Have you thrown already?
He is one of the main reasons I got my first job as a self taught dev
You are as knowledgeable as the senior dev I work with. I look at people like you and wonder if I will ever reach this level, I feel I'm too low iq to ever evolve this far.
you'll get there
Wow just the right time. I'm learning Next.js by creating a personal project and this is what I've been looking for.
I am more Angular for Enterprise level architectures, mainly because of its module and integrated DI.
For Solid on frontends (or flutter) I do : DataSource (API/DB/Cache) Repository (Data fetching strategy) UseCase (must be a user action) Controller (State management)
Is the repository's purpose just to map DTOs to models for the use cases? What else does it do for Data fetching strategy?
@@invictuz4803 The repository is the only one that can talk to the database. So that if you change your database, you just need to update the repository. Repository will guarantee output in a certain format but input can be updated if anything db related updates
@@invictuz4803 repository is also responsible for the cache management. So it may use local datasource's or remote datasource's. Domain/Core layer may only ask for a refresh as it is a User conceirn but does not know what it means on infrastructure side. As repository is outside the application core, and close to infrastructure, it knows, for example, connectivity status.
when it will come to unit test this approach will show all its might and benefits. It should be sooo easy to mock any dependencies and it will look very clean.
I actually leveled up, few months ago I would not understand a thing about what you said. I'm glad that I can follow your videos ❤
If you have the time to spend feel free to do this amount of abstraction, it's only over engineered if you have other priorities / work is paying you to deliver value and other features need to be delivered. 100% there are benefits of this.
i really like videos about this those advanced topics (clean architecture, DDD), would love to see more of it!
This is the way! Funny thing is this is how I do backend code in C#. Been getting in to nextjs and was like I need to organize this lol
Very nice! I definitely agree dependency injection is the way to go on large apps. Specifically inversion of control. Seems like we've gone full circle on PHP now :D Good times ahead
IoC is key on large projects, as technical debts, due to coupling, can quickly become unmanageables. Lazy resolving can have a huge impact on performances. Of course, if you plan to test unit or use TDD, it become mandatory. As an architect, making large apps is also planning for maintenance and refactoring.
I dont really think thats over engineering, you didnt cross the line. It's worth the readability, loved it!
Great video as always, a request, a video on setting up ci/cd with different envs (dev, stage, prod) from scratch for a react/next project would be total bomb at this point...
It's similar to what we do in the .Net projects. Initialy it is slower to get everything together, but in the long run it's much easier to maintain and fix bugs.
I'm using the app router a lot lately too, i like to store the components (the things that the page depends on as you said) in a _components folder, just so you can properly see the page.tsx and inside each folder you have things separated.
yeah I was doing that at one point as well, but now I just dump everything in the same folder unless it gets filled. Having parallel routes also help prevent needing _components
@@WebDevCody Yeah, but correct me if I'm wrong, but I didn't see a case where you had a page folder, and then a component you extracted into that folder. You only had pages in the subfolders... e.g. you didn't have something like app > dashboard > _components , some-page.tsx.
@@CadisDiEtrama000 yeah I didn’t have that in this example, I just dump any components at the page directory usually.
@@WebDevCody parallel routes meaning you just put the components.tsx into the same folder as the page.tsx?
@@untalentedwebdev parallel routes involve adding a @routeA @routeB directory where you can split your page into multiple sub components, so imagine a dashboard with 2 charts, each chart could be a parallel route which allows you to colocate your components inside those parallel route directories
thats why i name it as shared instead of components and place the components needed in the app but in a directory based with index.tsx
This was super helpful and even better that you included the source code for this. Would love to see an example of this using Vertical Slice Architecture using features instead of use cases. Keep up the amazing work!
😀
I do the same thing. I usually use pages directory so I can't colocate files in the pages folder.
So I mirror the route structure of pages inside my components/pageComponents directory. This allows me to colocate any related components in the ./Subcomponents folder right next to any component.
Also, I just name the component file as index.tsx. That way it picks up the name of the parent folder, which is the name of the component. Super clean
Do you use Ctrl + P to search your files? I feel like searching index.tsx would be such a pain. You could always search the folder name, sure, but it might return results from the pages folder as well.
Something you might noticed. If we are to add data access to other entities we need to add another data-access file to talk to db specifically for that entity... almost duplicate code. I usually use generics to pass entity type. Thou not sure if it's possible with drizzle api.
Congrats for learning and sharing clean code and good practices to the community
There somethings that I saw on your code and I would like to point out
Careful to not use the entities as object literals. At 19:56 in line 127 you could have used a method for this, .increment()
You should allow only methods of an entity to change it's internal state. Avoid setters, using setters you can easily break the internal state by passing an invalid value. Keep the business rules of an entity inside the 'rich methods'
Also, there is a miss conception about entities internal state, you have a method validate, that should not exist
A entity INSTANCE should be enough to know that that thing has valid state
The validations should lie on the constructors, and on the methods, they have the responsability of keeping the valid state, otherwise they should throw
So if you got in hands an instance of something, you can trust that thing has a valid state
14:20 yes that belongs to the entity. If you use only internal entity values, ask yourself what are you validating, then name your method like so. That validation could be way more verbose, and you would prefer to abstract that
So you call validate at the end of all your methods that mutate entity state basically?
@@WebDevCody No, say you have a class (Value Object on this case) that represents a month of the year. One of the internal value is the monthNumber, that has to be a number between 1 to 12.
If you wanna set a default value when instantiate, the validation
if(monthNumber12) throw
should be in the constructor
a nextMonth() method should contain some logic like
if(this.monthNumber===12) throw
It's a change of mindset, you don't post validate the action you performed, you can't even perform the action if it leads to a bad state
this way you ensure the instance ALWAYS have a valid internal state, you should never worry about the internal state validity of an instance
This is a DDD concept, and I learned it in Portuguese, so I may struggle to translate some terms properly
I do something very similar and love it. I have a seperate components folder for every different route(or page) of my app, like about, home, workouts, dashboard, profile, etc
Awesome! I think it's better to validate entities using the constructor rather than having the client call a validate method and create another instance instead of modifying the existing entity. I prefer naming the class without using the term "entity." For example, "Item" can be considered as the entity, and "ItemData" can be used to manage data from infrastructure like a database or third-party API.
I agree
@Web Dev Cody here are my thoughts for what they are worth (if worth at all):
Your approach goes somewhat on the opposite of what is recommended on next.js app router docs.
Next.js has file based routing and so the approach of separating files by some imperative definition of "responsibility/what-it-does" can cause problems with the router and add layers of complexity that could be avoided by following the recommended approach of grouping files by their "domain/feature".
@Theo has a take on this that I recommend you giving some thought.
As I understand your approach goes in line with what the marked is used to do and have lots of theory behind, but I tend to look to the side that the modern days are pushing us into a different scenario on which grouping files by domain makes more sense on the long run.
hot take: where you put files in your project has very little impact on maintainability - consistency is more important. Also, the core of what determines if something is maintainable is the interdepencies of the modules and trying to abstract layers where needed so that refactoring isn't a pain in the future.
Additionally, if your team agrees unit testing is important, this also plays a role in how you structure some of your modules. I know you think the next docs are great, but point me to anywhere in the docs where they mention how to add automated testing over your next app - they don't. Take that information as you will, but I take it as a sign of they don't care about testing nor explaining good ways to structure your code to make it easier to test.
At work I have a project that groups all files by "type". We have directories with over 200 files of type "Action", 200 files of type "UseCase". It's the last thing I think of when I say "man, this system is complex".
@@WebDevCody Not a hot take at all, as I see this is where the market agrees, and I do to.
Most teams just decide on which file/folder structure works for them based on their own abstractions and this was and will continue to be what works for most teams.
And yes I may be biased towards the docs, but don't want to be bound by it, I'm just trying to understand the intent behind the docs, so I can avoid working against this intent.
Also I may be biased about not giving the same value to automated tests as most of the market does, and this may be one of my mistakes.
Thanks for expanding on your point of view, this definitely helps me understand the topic better.
@@wesleycoder sure thing, also remember I work full time on a project with 500k lines of code and 10 devs working full time for 5 years, so my perspective on “good architecture” is scoped around that. If you are a one man team and don’t care about unit tests, then I think their docs are fine
Finally, someone I agree with regarding the ui components dependencies 🤣
you should add some unit tests to really highlight the advantages of this approach. Although personally I feel like this is a relic of the old days where unit tests were all the rage.
Doing a variable name change/adding a new property for an entity, for example, is gonna be a nightmare. You'll have to change it in so many places to keep it in sync.
That's why you have mapping from persistence / application / dto layers, so that yo ucan limit the blast radius of this kind of changes. If you don't use clean arch and stuff like that it's actually HARDER to refactor, especially if those changes include business logic.
But this comes at the cost of more boilerplate and abstraction layers, so adopting those practices really depends on the project and the team. If the team isn't comfortable with clean arch, it will slow them down significantly and leave a bitter taste in their mouth.
I don't think it is overengineering, especially for large apps. Very nice content!
Something on top, you test the business logic far easier this way. You just need to pass mocks as dependencies.
yup exactly, so if you're big on unit testing, it helps.
Great content! Would you consider making tutorial similar to this?
Yes! I was thinking the same. Being self-taught, this (in depth clean architecture) is something that is really difficult to find (and make sense of). I'd be happy to pay for a course or comprehensive tutorial on this.
This is so underrated!!
can you make a full tutorial using this structure? also, can you share with us an open source github repo using this structure so we can learn from?
I like FSD (Feature Sliced design) approach, which is more clean:)
thank you mate
i am on the correct path
I would love if you can make a video on how to properly deploy a NextJS app on GCP App Engine. Something that nobody made a proper video on.
I like putting those components related to page.tsx under _components for each directory
Damn you really have this figured out. Any good resources on this topic? The Clean Architecture book went a bit over my head 😅
I'm still trying to figure it out after 5 years of using it professionally. There are some aspects to clean architecture I'm rethinking because I find it sometimes adds little benefit with a lot more complexity
Good job babe!
Alright I guess I need to make my video defending clean architecture
You should
To exapnd on this base video idea, if you added a products table. Would you also need to add a product entity/data-access/use-cases/etc ?
Nice structure 👌🏼
Nice - been doing something similar with my code for years.
Great example, however it gets very complicated very quickly... Listen to what I encountered in a not-that-extensive project.
How would you go about if you had multiple roles where each role can interact differently with the entities? Specifically what I recently had in a project, lets say we have a User entitiy with 2 different roles, but now we have a use case - if the user is an Admin then he can update their finance settings - where the finance settings are only a few fields of the User entity. Do you now create a separate Admin and User entitiy, or is it just one User (general user) entity? Because, although it's the same table, the "User" role should not have access to most of the attributes in that table. And from what I tried, if we put it all in a single entitiy, it's hard to separate what each role can do.
In that same codebase we also had a case which we can maybe translate to your example. Lets say the entitiy for Item can now be created only by admins, but can be viewed and added to their cart by Users. Same question, are those 2 separate Item entities now or just one?
On simple examples like what you have shwn it works great, but as more features are required this strongly feels like it would need kind of inheritance if we wanted to be consistent... but then we fall into the pit that is OOP and we kind of lose the beauty of React...
would love a video on something more complex 😁😁
I love clean architecture (even in the front with TDD so you don't manually test stuff all the time)
I love to couple it with redux too as part of my core. Do you have stuff like that too or videos in preparation around that ? I'd love to see how you tackle it yourself since the few videos I've seen of you are of top quality
Hey Cody. Do you have a video on how to deploy a NextJS application on other platforms like gcp and etc.
I've never used GCP, but I'm assuming you could just run it on a VM
Is this a good approach?
/src/app/home -> page & layout
/src/app/components/home -> components related to home
/src/app/services/home -> API services used on home
What's your view on using prisma vs drizzle for ORM. Your content is priceless ❤️
they are both good, I'm trying to use drizzle for now
So here's my question... say I want to apply clean architecture and have things in modules... like items, users, etc. In your items schema definition, you have a usersId which references the users.id field in the users schema. So this requires importing the users schema from a different module. In a modular file structure approach, how would you import this users.id into your items schema definition?
I’m not too sure what you’re asking, but since entities live in the same architecture layer, they can import each other directly if needed
It might be me doing things wrong. But as far as js/ts goes most of clean architecture concepts will cause you more issues than it would solve. An example would be using entities classes but then you fall into the issue of state serialization, passing entities from server components to client components. A lot of redundant code... Etc
I don't think entities should be leaving your business layer into your next code, you should be using a DTO for that. It's the same as if you were invoking an API to get data; you only get back primitives.
Love the content cody! Would you guys say tRPC is still a valuable option for the backend? I feel like it gets difficult to manage when the project gets bigger
can you create a tutorial for tanstack table sir? with some advanced api usage really stuck with it
I really appreciate this video and personally learning a good amount from it so thank you. Wouldn't it make more sense to instantiate an initial class that has all the context of different methods and then use that in your actions instead of always importing all the methods into context in each server action? Re: @9:53 ish
We do that at work and we ended up with a file with hundreads of methods on it which causes tons of issues with performance when running unit tests over our interactors. Every time jest runs it has to reimport hundreds of modules and it takes like 7 seconds to initialize a test file. This would also apply to performance issues with next, if every route needs an object with hundreds of imported modules, every page will become huge
@@WebDevCody YES this is the exact issue with using a ton of classes. I appreciate that you addressed this. Thanks again.
Where do you suggest I learn more about this Clean Architecture?
Great video! You mentioned clean architecture - could you recommend any additional reading materials on this topic? I read 'Clean Code' quite some time ago. I'm guessing many of these concepts are derived from that book, right?
Clean code is a decent one if you don’t follow it like gospel. I don’t read too many books honestly, I find just having a conversation with chatgpt the best approach to get past the fluff they often add into books
Awesome,
Love your content! One thing I've noticed is that your code blocks has some colored stuff indicating where it starts and ends, is that a vscode extension?
rainbow indents I think the extension is called
should change the title to “applying over engineering to my next.js project”
😉
Very good content regardless, is cool to see how even you where uncomfortable with the amount of code/abstractions at the end 😂
imo this kind of things are just madness in any “frontend” project. If you really care and need this level of abstractions and separation of concerns, just have that in a completely separate API and use the endpoints in your frontend instead of talking directly to a DB
Hm I dunno about this Cody. If you know from the get-go that you'll need this level of architecture for your logic (DTOs, repositories, entities etc.) I would probably put it in NestJS straight away and have NextJS be a dumb frontend. In any other case, I would start out with some good-enough seperation of concerns in NextJS and just migrate the logic to NestJS if necessary.
Is it possible to make it so your server actions have no access to the code inside of the data access directory besides a newed up singleton that conforms to a specific interface? Architecture is nice, when people actually follow it. I think making it so users don't have any access to the data access layer from the server actions is a good way to remind them that they need to follow architectural principles.
Maybe a lint rule could do it
I am wondering if once you start using SST it makes sense to build your apis directly in api gateway and lambda. It’s marginally more complex but there is way more flexibility and you can easily have them access the AWS services too.
Where can I read more about clean architecture concept?
I'd start with the book "clean architecture". chatgpt is really useful as well
Nothing was mentioned about type definitions (at least at the point I'm at) but I'm noticing most files have type definitions directly in them. So are you defining types exactly where they are needed? or is there any central location of types that are used in multiple locations? Wondering how that fits in with separation of conerns
I’d put the types in the same file of the function that owns the type.
Everything looks pretty straight forward except the DTO part. It just feels a redundant abstraction upon ORM. The ORM itself is already a DTO.
I felt like the DTO part makes more sense if you are doing SQL calls without any ORMs. I actually saw similar pattern when people create a center API entity on top of every API calls. Sometimes it's good, but most of times are really bad. But I do feel your internal struggle when you mentioned the DTO.
Yeah I guess converting the drizzle return value to a dto might not be necessary
@@WebDevCody but I think if you want to remove Drizzle later, then the DTO might make the migration easily.
Great video. Vid request. Can you do a video on controlled and uncontrolled components? This is one of those react concepts I have never really understood.
am i the only one that thinks thats a crazy amount of code to implement something so simple?
No. Desperately rewatching to understand what I missed, but I am still not understanding the benefit, over just using Drizzle and zod on the server.
no, that's a normal reaction.
Like I said, this is for projects where you might have lots of developers pushing features and modifying existing logic. As the system gets larger (imagine 5 years of code), it's very easy for devs to start copying + pasting logic from one server action into another and then your business logic is duplicated everywhere in your code base. So then when your product owner says "I want to allow decrement to be negative (to act as debt), your team copied and pasted the decrement logic in many different places and now it's a pain to figure out where it all needs to be updated and fixed. Obviously this is a simple example, but at work we have a "CourtCase" entity with over 70 fields, and many of those fields are cross dependent on other fields. So without following this type of pattern, a developer might modify modify a field on and object and saves it to the database then your database might have a bunch of invalid data because no one was actually validating the entities in your project, then when a user tries to load the court case to view details about it everything crashes because your database has invalid data.
hard to explain in text but there are good reasons to do this stuff
@@WebDevCody Thanks for the detailed explanation, this is the first video I've seen yours so i assumed you were targeting beginners but it seems your audience is more junior developers with some experience.
@@geraldseydoux2252 he has some extremely good videos for beginners if you scroll through his channel. lately has moved to some little more advanced stuff.
anyways whatever your level might be i suggest you to have a look at his channel cause the topics he deals with are a lot and very interesting and his way of teaching is one of the best i have ever seen on youtube (especially his approach of live troubleshooting and analysis).
Hi this is very helpful. Great Content! Would it be okay to ask what theme are you using in your VSCODE and also the fonts? It look so clean.
bearded theme and bearded icons
Thanks for the great content! One question about the use cases. I've noticed in every use case you perform authorization check. Does this type of logic generally part of every use case? Also, considering that usually one would also authorize user on a routing level, isn't that redundant? Thanks
Ultimately you’ll end up adding some type of authentication or authorization check somehow. I find the code much easier to follow if you keep it redundant and imperative compared to trying to find a cute middleware or decorator pattern which obscures the core business logic of the use case.
Having “entities” (classes with methods, etc.) is going towards an OOP approach vs a more FP approach, correct?
I find them interesting and seemingly useful, but I haven’t come across them or used them in my own React-based applications yet. I have essentially the “dto” objects passed around in my app (after an adapter/mapper converting BE format to FE format), and then use separate “helper” functions as needed (rather than methods on a class).
What are your thoughts on the different approaches?
I think clean architecture mainly applies mainly to your backend / database logic. Applying this to react would probably mean using redux or mobx. Keep all logic and state out of your ui components would be the way to apply clean arch to the react layer
@@WebDevCody I’ve recently shifted to breaking my react components into a “data handling” sort of layer/wrapper and then a UI/View later, which seems to be a step in the right direction (seems to allow for easier testing, ability to utilize StorybookJS easier, etc., as I can then use just the UI/View component for those things).
I’d enjoy a video on that concept (vs having it all within a single component/file, which is the more natural way for a beginner to structure things I think)
so clean architecture is basically creating layers in your application through abstracting the code?
Couldn't pay me to write Dto's by hand when developing full stack Typescript when there's tools like tRPC
Can you only remove one item at a time from the pantry? Your code looks like if you have 1 apple you can still remove 5 giving you -4 apples?
I’d have to check. I haven’t touched this project in a whilw
I feel like you shouldn't make setters and just make the variable public unless you need to manipulate what you're going to set it to,
AND you don't need to manipulate the return value for the getter (if you have one),
I also think that you should use
`get X` (actual JS/TS getter instead of
`getX` (method) and
`set X` (JS/TS setter) instead of
`setX` (method).
Normally, you could just add getters to private fields, although "setters" should be a method that has business logic inside with validation, although fields shouldn't be public to avoid mutation on that object anywhere, only in the methods with logic that needs those fields.
For example in the video:
Near 10:30, there should be, item.Increment, and inside that method validation and logic that increments.
Is there a way to implement the new shared/wrapper layout.tsx in a NextJs v14 project but still use the old NextJs "pages" folder instead of "app"?
Not sure
Have you caved in and started using the
/app directory?
yeah I've been using it
@@WebDevCody I'm still hardcore on this t3 vibe. Was fiddling with the app router for a few months but just not a fan of the new system designs.
This video came at such a perfect time, thank you for making it!. I'm currently refactoring my code to include a data access layer after reading the security blog post from next.js and I was really confused about how to set it all up.
At 9:15, I'm a little confused where you pass in the data layer as context in the use cases. That seems to be used with server actions, how would you do this if you're only using api routes with next.js? You were seperating your server action from your business logic, how would you seperate your api from your business logic?
in next you'd have your router handlers, and you'd need to invoke your use cases from your router handlers. It's the same deal, take your request body / query / params and pass them as arguments to your use case, and inject any dependencies into the use case from your request handler
Thanks so much, that makes sense!@@WebDevCody
So is this a something that only senior frotend should know or even middle? I want to know your opinion
Great
Any reason to not have a _components folder under each route to keep it clean? I prefer having only the special Next.js files under the actual route and everything else in ignored folders.
That works fine. I may try doing that as well
Also bro can you make how you handle errors in nextjs
will you show us how to do this in express api?
Maybe my question is a little dumb but is this approach considered functional programming or object oriented programming?
waiting for devops from you
So is the front end and backend both here in one code base?
Hey Cody maybe you've answered this in another video but after finishing a pages router project, i have decided to start a new project with T3 and the app router and I have a question. Do you think that trpc is still needed or is there a better way to build endpoints now
I still think tRPc is a great solution, but recently I’ve been just using server actions and RSC to fetch data for my pages
@@WebDevCody I've been working on it the past week and I slowly came to the realization that tRPc is not needed, as what I ended up doing now is using the tRPc queries and mutations inside of the server actions which so I guess why not directly write the code in the server actions.
I am a bit lost when it is client and when it is server. For example when creating a new item, the pantry-tracker/src/app/dashboard/_actions
/create-item-action.ts is this running on client or server? I am following it till updateItem in src/data-access/items.ts but it is not clear to me. Can you please explain this?
Looking good man.
server actions run on the server. next abstracts the code by allowing the server action to be invoked via a POST request, and calling the server actions from the client component does an HTTP POST request for you under the hood.
What theme do you use? And font?
DevCody Stack
All my projects are build with Nextjs Frontend and nestjs server for backend. How much will you rate this setup?
Nest is fine
what component libary do you use ?
probably shadcn ui
shadcn ui
@@WebDevCody Thanks a lot for this amazing video!
Could you please tell me what do you use for displaying charts in a dashboard?
Have you tried Tremor and shadcn/ui together?
@@WebDevCody 👍👌
Hi Im a junior dev who studied C# in the past. This architecture feels extremely familiar with dotnet. Is the "use-case" equivalent to "services" in dotnet?
I’m not sure I haven’t used dotnet
use case/user story is equivalent to services or domain layer
Same storing the same components inside components folder... and page depended components inside that page route _components folder
Hey Cody! Could you please provide the names of the icons and theme extensions, if possible? I appreciate the content, man!
bearded theme stained blue with bearded icons extensions
Doesn't having components inside the folder make the Next treat it as a page?
No, it only cares about specifically named files
Could this be applied to DDD as well?
what's the file icon you use sir?
I notice that you are throwing errors in your use case layer. Where in your code do you catch these?
Probably in the server action if I’m using next-safe-actions of zsa
how do you get an online postgress?do you use planetscale?
for postgres, I often reach for supabase or railway, for mysql I use planetscale
@@WebDevCody thank you😊
have you experienced calling a use-case from another use-case? i'm just curious if that is allowed in clean architecture
Usually no, because that means your use case isn’t a single use case anymore. Usually I’d just make business helper functions that two use cases can reuse if needed
@@WebDevCodyif I'm getting it correctly, you basically calling those use-cases in another function?
Hello!
Can you share your vscode profile? Really like your vscode Settings!
Thanks very much!
Why when you added cheese in a first seconds of your video, your all table including tabs updates?
It should update only a row not a all component. Take a look on a profiler it something weird going on there
all of the tabs are using the same state with a filter which is why they all updated I think
The last bit feels like overengineering. If it is an enterprise system, it should not be doing the backend in the front anyway :D
touché 🤣
I never had a real developer job with a team but at college something that always bugs me is..
- if I am a front end dev and need to check if a user account registration was successfully sent to database, or if a user modified something on his account (so rewriting information to database), how can I test this functions and see that was successfully rewritten but without modifying the real database?
I have this question because I am working in a project for an apprenticeship services and they use Firebase, atm I am using Firebase emulator to test my functions and anything related to database and even authentication, but how do I test in the real one?
I am asking this because normally a person developing an app is always deleting information from database after writing, vice -versa.
Firebase has the emulator, but if a dev is working is a project that was already there and in production, how can you test this things?
Sorry for being stupid, I am just noob
I’ve never used firebase or the emulator you are talking about, so I’m not too sure.
@@WebDevCody but could you, one day, make a video about how would a developer test codes that is related to write on database without affecting the mai database that is on production. It is really hard to explain what I am trying to say as English is not my first language Cody.
Sorry for taking your time