Loved the explanations and everything. Quick thing to add and probably a lot of users do it differently. Using your service as the "Business layer" doesn't mean you write bad code or not clean code. My humble opinion is that, "controllers" should take care only for input and output of the application, nothing else. Service is actually the business part and the clean code should be there. You have to think also about the architecture and controllers should not do business logic. Ofc repository,dao or whatever for the data layer. Keep with the great stuff :)
Yes, I agree, too. I also think it is fine if we let service handle its business logic instead of laying it on controller. I don't want to split my controllers just because it is too long to handle like he recommends for the SRP :(
yes! this is very important from an architecture's perspective, let's say for example ports and adapters architecture, where you should encourage such decoupling that even you can switch the controller implementation (from example moving from a rest controller to a kakfa topic subscriber) and the business logic should stay the same.
I thought it was a bit abstract but he ended up saying it should be handled in another layer, rather than the service (this layer can communicate between different services and will be called from the controller). But he gave an example from the controller so you feel it's not quite right. But I understand what he thinks.
@@justfromnowhere well then what is the correct answer for this? if you have more class to just serve a small function, your project will become very very big.
5 місяців тому+4
@@TMANandMAISON991 why not write everything in one big file with goto statements? roflmao
Fantastic way of teaching these concepts and one I have hoped to see for years now: real world issues people create constantly, why they are mistakenly created, how to avoid them, and how best practices (SOLID, etc.) guide us to avoid making such mistakes along the way to creating more composable code. Thank you for the video!
Excelente video. Just a comment, at 4:09 ordersService should only receive a DTO, how the service implements the solution is not concern to the controller.
I am confused with example on GitHub for open closed principle. Should we call registerPaymentGateway in controller? Will this object (paymentGateway) always have only 1 payment method at a time? I thought paymentGateways (the one in paymentService) should have all our relative payments options inside the object, so that when we call processPayment it checks payment method against the one we have. I am just confused the way it’s organised in your GitHub repo) I didn’t even see where you are calling processPayment Method in your code
hey @CoderOne, don't you think that on 4:00 your controller does too much of logic? I've been taught, that controllers should only transfer the data from view to services and back. What would you do if there will be addtitional logic between creating order and sending it? For example, let's imagine you must create orders without restrictions, but send action should be applied only after some filtering. would you add this logic right in your controller between two service calls?
@@vinimaciel I don't think so, because sometimes, we also need to check the other service call is fine or have errors to process next steps . Do you have ideas when we face this case ?
Don't get the implements against extends part. "extends" is not bad thing and it still covers Liskov principle. If you want to force one (or several) of methods to be implemented in children, you just make the base class abstract and make those methods abstract as well (just like you did in open-close example). "implements" is good when you combine multiple interfaces in one class implementation. But "extends" allows you exactly *extend* something (not re-implement from scratch). Good overview though.
Thanks for that video. How do you recommend injecting all implementations of services in one array with the dependency inversion principle? In your Scenario you have 2 Service implementations and let's say you want to inject List services. One method is: Module factory methods or the use Value in the module file. But it becomes more complicated when the service implementations are in different modules - i do not want to import all the modules, which implement them, just for defining an array of services. What i do currently is a service provider module with an abstract ServiceProvider class with a registerService(service: T), and a getServices():T[] method from a ServiceProviderModule. It holds a list of instances of type T. All Implementations of these service providers define their abstract type, in your case it would be class StorageServiceProvider extends ServiceProvider. The service implementations just inject the StorageServiceProvider and in the constructor they just register themself with storageServiceProvider.registerService(this). A consumer service, which needs all storage implementations injects the StorageServiceProvider and calls getServices() method and can iterate over them. I am coming from the java spring world and there it is already handled implicitly. Is there a in house solution in nest for handling list of services with the dependency inversion principle? :) Thx
basically OCP, LSP and DIP are very similar, as per video in OCP payment methods can be added, LSP different payment strategy can be used, and DIP allows using one line to switch between different class implementations, all examples are pretty close to each other
In OCP you can extend if the subclass is the same as the base class but with an added functionality. e.g A cock is a bird that can crow. Cock class can extend the bird class which has the fly functionality and then implement its own crow functionality.
Single responsibility means that your controller is not responsible for sending emails. Controllers are only responsible for handling the http request / response and invoking the business logic layer. In frameworks like nestJs that includes all the validation and transformation of the endpoint input as well. If you remove the presentation layer from your solution can it still orchestrate the use case?
Good video. Since the PaymentGateway and StorageFactory do not share code across their respective implementations, they would be better suited as interfaces as opposed to abstract classes. A good general rule to follow is that, if there is a need to share code across multiple implementations use an abstract class to achieve this, otherwise an interface is all you need.
SPR doesn't mean "class should do only one thing". Classes or modules do a lot of things. SPR means that class or module "should have only one reason to change". It means that when you have to change orders processing you shouldn't make changes in other parts of your application e.g. users, accounting etc. And the orders module still does a lot of different things.
The controller can be replaced with another method because it should not contain business rules. Controllers are responsible for handling requests and delegating the processing to other components. By keeping controllers lightweight and decoupled from business logic, it allows for flexibility and easier replacement with alternative methods or technologies.
That's right, but I'm always wondering how and where should I put these wrapper implementations.. How do you use it usually? For example when you have to save something and also sending notification, you should not put both different service calls into the controller (as you mentioned), but how would you call that service that wraps the business logic? I would usually inject my abstract notification service into the service that saves the data, and call the sendNoti method inside the "saving" method
@@martonvalentin7386 This is a very used solution, mainly by frameworks. Another option is to create an event within your service/use case to make this call more decoupled. Then the event of sending the notification will be responsible for sending it, being able to put it in a queue and not depending on the flow of the notification to confirm the creation. Imagine that your notification throws an exception, it would break your creation which is another case, thus breaking the single responsibility principle.
The Open / Close Principle (Objects or entities should be open for extension but closed for modification.) has nothing to do with DI. The "D" in SOLID is where IOC or DI would come into play. Open / Close is more about coding against a contract rather than a concrete implementation. So in other words, use interfaces and abstract classes.
Hey, I am looking for a setup for test suites in NestJS, but with addition of factories for classes, in order to help me with seeding and "Arrange" Step of the Test. Any idea where to watch that? Especially, for complex schemas where i.e. I want to test a module, which every entity depends on the creation of others.
Dude you are very amazing developer, I was just looking around to improve my coding structure and make it more readable, reusable and maintainable, This is the video anyone who wants to look first before writing nest js code. Thank you!!
If your code inside your service method throws an error, does the email still get sent? In your case no but it's because you don't have any error handling not because it's part of the use case. You would have to add some additional business logic to your controller in order to decide if the email should get sent. Oh, and your product manager just asked you to send a certain type of email based on the response from the database when adding a new order. Now you have to add even more business logic to your presentation layer because your business logic layer does not complete the use case. As the use case grows so do your problems given your implementation.
Can we structure nest js project similar to Spring boot? I mean separate the files in Controllers, Service, Models etc folders. Is it good idea or practice?
I thought a concrete class should only implement interfaces not abstract classes. Why ts allows that ? Inheritance from class to class I normally use extends. If I want to enforce contract I would use implement from class to an interface
I have a doubt about the S principle, if do it like this, the code in the controller is quite long, and corresponds to business, somehow it is not good for reusable functionality right? Just kinda view from mine! Anyone can help me on it? Or do we have to make trade-offs?
Interfaces do not exist at runtime in Typescript, so it is not possible to resolve dependencies by interfaces (unless you use a String token that has the same name as your interface and use the @inject() decorator in your constructor)
"Make no mistake, there is a principle like that. A function should do one, and only one, thing. We use that principle when we are refactoring large functions into smaller functions; we use it at the lowest levels. But it is not one of the SOLID principles-it is not the SRP." page 62 of Clean Architecture by Robert C. Martin
I think controller doesn't have a larger scope compare to service. Service handles all the logic in our application. It is not good to bogus the controller, let service do the logic.
The problem with the SR principle is how you decide what is a single responsibility. Where to draw a line? Uncle Bob clearly said in his book that the SR is not a single fn call The SR means that your method should care only about a single piece of business logic like in your case processing the order. It may involve multiple steps. He's saying parts that have a single common reason for change should live together. Sending an email is a valid part of submitting the order, you want your users to know that the operation is successful and their order is received and processed. In this case, it should live within the service. I guess, you wouldn't want your users to spam you with calls to confirm whether or not you received their order just because you forgot to call an email service after you submitted their order.
The domain logic should not be inside the application layer in your case you show that when you call the email method inside the controller of orders! There can be a lot of logic from other modules so what you will do in that case is all the logic will be both of them in controllers and in services that's the antipattern That's wrong!
If you want to call othe method from another service inside a service and avoid to inject this another service, you just can emmit an event and put this other method listen to it.
Naming a class suffixed with "Service" doesn't make SRP easier, class named as per its responsibility not only enforces the developer to write code in SRP but also for readability, there's no need to name a class called "StorageS3FetcherService" if you can name it as "StorageS3Fetcher"
In your "LSP example", you say, "You don't have to change any code, etc", but you do; you're manually changing the strategy being used via the injection in the controller. How would you go about programmatically determining this? Eg: Activating the sale pricing either based on different times of the year, or being able to arbitrarily use sale pricing. This seems like a bit too contrived of an example that may lead people down an arguably incorrect path.
You totally shot yourself in the foot when you decided to add the sendOrderEmail() in the controller. Sending a notification, in this case an email is part of your business use case. Your service class is responsible for orchestrating the use case. Look at it this way. If you remove the API presentation layer (controllers) can your app still orchestrate the use case? In your case no. You can imagine there may be a number of things you could put in place of the API presentation layer like going from REST to GraphQL or a grpc microservice or even a CLI. Your implementation does not scale given your decision to put business logic inside the controller. Your giving bad advice here ( ua-cam.com/video/vE74gnv4VlY/v-deo.html ). The service class that handles the submitOrder use case should invoke the process of sending an email. If you really want to look like a senior dev then you would suggest dispatching an event once the order is created successfully and allow any number of operations to subscribe to that event one of which may be sending an email.
ua-cam.com/video/vE74gnv4VlY/v-deo.html "you can change the pricing strategy without changing any code", but you changed code to use the other strategy! What you need is a factory that will provide you with the desired pricing strategy based on some context or other condition.
Loved the explanations and everything. Quick thing to add and probably a lot of users do it differently. Using your service as the "Business layer" doesn't mean you write bad code or not clean code. My humble opinion is that, "controllers" should take care only for input and output of the application, nothing else. Service is actually the business part and the clean code should be there. You have to think also about the architecture and controllers should not do business logic. Ofc repository,dao or whatever for the data layer.
Keep with the great stuff :)
I was two days ago trying to pass the repository linked with the interface in usecase. Find out at 10:43 that it needs @Inject
Yes, I agree, too. I also think it is fine if we let service handle its business logic instead of laying it on controller. I don't want to split my controllers just because it is too long to handle like he recommends for the SRP :(
yes! this is very important from an architecture's perspective, let's say for example ports and adapters architecture, where you should encourage such decoupling that even you can switch the controller implementation (from example moving from a rest controller to a kakfa topic subscriber) and the business logic should stay the same.
I think logic is not handled on the controller, the controller only forwards requests from view to service and receives response and forwards to view.
I thought it was a bit abstract but he ended up saying it should be handled in another layer, rather than the service (this layer can communicate between different services and will be called from the controller). But he gave an example from the controller so you feel it's not quite right. But I understand what he thinks.
Bruh all this stuff is just opinions. There is no correct answer
@@justfromnowhere well then what is the correct answer for this? if you have more class to just serve a small function, your project will become very very big.
@@TMANandMAISON991 why not write everything in one big file with goto statements? roflmao
you are violating KISS principle
I really love this NestJS series. Please keep going!!
Thank you for including good and bad examples! By far the best way for me to learn
Fantastic way of teaching these concepts and one I have hoped to see for years now: real world issues people create constantly, why they are mistakenly created, how to avoid them, and how best practices (SOLID, etc.) guide us to avoid making such mistakes along the way to creating more composable code. Thank you for the video!
that was facinating. please provide more nestjs videos
Excelente video. Just a comment, at 4:09 ordersService should only receive a DTO, how the service implements the solution is not concern to the controller.
I am confused with example on GitHub for open closed principle. Should we call registerPaymentGateway in controller? Will this object (paymentGateway) always have only 1 payment method at a time? I thought paymentGateways (the one in paymentService) should have all our relative payments options inside the object, so that when we call processPayment it checks payment method against the one we have. I am just confused the way it’s organised in your GitHub repo) I didn’t even see where you are calling processPayment Method in your code
very helpful. thank you and keep doing great stuff
hey @CoderOne, don't you think that on 4:00 your controller does too much of logic? I've been taught, that controllers should only transfer the data from view to services and back.
What would you do if there will be addtitional logic between creating order and sending it? For example, let's imagine you must create orders without restrictions, but send action should be applied only after some filtering. would you add this logic right in your controller between two service calls?
It has no logic operations, just calling functions, if you need to check another thing, maybe process data or validate it, you'll have another service
@@vinimaciel I don't think so, because sometimes, we also need to check the other service call is fine or have errors to process next steps .
Do you have ideas when we face this case ?
Don't get the implements against extends part. "extends" is not bad thing and it still covers Liskov principle. If you want to force one (or several) of methods to be implemented in children, you just make the base class abstract and make those methods abstract as well (just like you did in open-close example). "implements" is good when you combine multiple interfaces in one class implementation. But "extends" allows you exactly *extend* something (not re-implement from scratch). Good overview though.
Yeah, LSP is not about using implements instead of extends
Thanks for that video.
How do you recommend injecting all implementations of services in one array with the dependency inversion principle? In your Scenario you have 2 Service implementations and let's say you want to inject List services. One method is: Module factory methods or the use Value in the module file. But it becomes more complicated when the service implementations are in different modules - i do not want to import all the modules, which implement them, just for defining an array of services. What i do currently is a service provider module with an abstract ServiceProvider class with a registerService(service: T), and a getServices():T[] method from a ServiceProviderModule. It holds a list of instances of type T. All Implementations of these service providers define their abstract type, in your case it would be class StorageServiceProvider extends ServiceProvider. The service implementations just inject the StorageServiceProvider and in the constructor they just register themself with storageServiceProvider.registerService(this). A consumer service, which needs all storage implementations injects the StorageServiceProvider and calls getServices() method and can iterate over them.
I am coming from the java spring world and there it is already handled implicitly. Is there a in house solution in nest for handling list of services with the dependency inversion principle? :) Thx
basically OCP, LSP and DIP are very similar, as per video in OCP payment methods can be added, LSP different payment strategy can be used, and DIP allows using one line to switch between different class implementations, all examples are pretty close to each other
Great content, really learnt alot from this video
In single responsibility principle you could also use events to decouple
Why are you using Abstract class with abstract method Payment Gateway instead of interface in 7:19?
In OCP you can extend if the subclass is the same as the base class but with an added functionality. e.g A cock is a bird that can crow. Cock class can extend the bird class which has the fly functionality and then implement its own crow functionality.
Single responsibility means that your controller is not responsible for sending emails. Controllers are only responsible for handling the http request / response and invoking the business logic layer. In frameworks like nestJs that includes all the validation and transformation of the endpoint input as well. If you remove the presentation layer from your solution can it still orchestrate the use case?
Good video. Since the PaymentGateway and StorageFactory do not share code across their respective implementations, they would be better suited as interfaces as opposed to abstract classes. A good general rule to follow is that, if there is a need to share code across multiple implementations use an abstract class to achieve this, otherwise an interface is all you need.
Would love to see a good course of Nest.js with these clean-code - SOLID principles.
i'm also want from wscube tech team
good video, like from brazil.
nice bro please keep updating us with your awesome knowledge about backend specially nest js
It's the first time I see an exemple of Record type in real world, thank you!
Hello, what's theme were you using on the video ? Thank you
SPR doesn't mean "class should do only one thing". Classes or modules do a lot of things. SPR means that class or module "should have only one reason to change". It means that when you have to change orders processing you shouldn't make changes in other parts of your application e.g. users, accounting etc. And the orders module still does a lot of different things.
Great explanation. Thanks
The controller can be replaced with another method because it should not contain business rules. Controllers are responsible for handling requests and delegating the processing to other components. By keeping controllers lightweight and decoupled from business logic, it allows for flexibility and easier replacement with alternative methods or technologies.
That's right, but I'm always wondering how and where should I put these wrapper implementations.. How do you use it usually? For example when you have to save something and also sending notification, you should not put both different service calls into the controller (as you mentioned), but how would you call that service that wraps the business logic? I would usually inject my abstract notification service into the service that saves the data, and call the sendNoti method inside the "saving" method
@@martonvalentin7386 This is a very used solution, mainly by frameworks.
Another option is to create an event within your service/use case to make this call more decoupled. Then the event of sending the notification will be responsible for sending it, being able to put it in a queue and not depending on the flow of the notification to confirm the creation. Imagine that your notification throws an exception, it would break your creation which is another case, thus breaking the single responsibility principle.
The Open / Close Principle (Objects or entities should be open for extension but closed for modification.) has nothing to do with DI. The "D" in SOLID is where IOC or DI would come into play. Open / Close is more about coding against a contract rather than a concrete implementation. So in other words, use interfaces and abstract classes.
Amazing as always, thanks for the tips!
Hey, I am looking for a setup for test suites in NestJS, but with addition of factories for classes, in order to help me with seeding and "Arrange" Step of the Test. Any idea where to watch that?
Especially, for complex schemas where i.e. I want to test a module, which every entity depends on the creation of others.
Dude you are very amazing developer, I was just looking around to improve my coding structure and make it more readable, reusable and maintainable, This is the video anyone who wants to look first before writing nest js code. Thank you!!
Nice video man. However, I prefer to use extend, and then make the method in the base class an abstract method as well.
How do you Handle Money in such situation?
In Laravel they use moneyphp
Amazing tutorial, thank you!
If your code inside your service method throws an error, does the email still get sent? In your case no but it's because you don't have any error handling not because it's part of the use case. You would have to add some additional business logic to your controller in order to decide if the email should get sent. Oh, and your product manager just asked you to send a certain type of email based on the response from the database when adding a new order. Now you have to add even more business logic to your presentation layer because your business logic layer does not complete the use case. As the use case grows so do your problems given your implementation.
Doesn't StorageFetcher breaks LSP?
in 8.17, when you're filling the paymentGateways ?
Can we structure nest js project similar to Spring boot? I mean separate the files in Controllers, Service, Models etc folders. Is it good idea or practice?
Really good video!
great video
thank for your sharing
I thought a concrete class should only implement interfaces not abstract classes. Why ts allows that ? Inheritance from class to class I normally use extends. If I want to enforce contract I would use implement from class to an interface
The last time i want to learn more about this framework , but i didn't get a large community as other frameworks
I have a doubt about the S principle, if do it like this, the code in the controller is quite long, and corresponds to business, somehow it is not good for reusable functionality right? Just kinda view from mine! Anyone can help me on it? Or do we have to make trade-offs?
soo good, would you make more videos on nestjs design concepts
very good video =))
Very helpful!
Very awesome. Thx
Hey great video, why the storage fecther is an abstract class and not an interface
What is user can select any of the payment providers ? Your solution doesn't include that.
amazing, thanks for the tips. +1 subs
Is there any reason for using abstract classes instead of interfaces?
Very good question but no answer ! too bad.. I would like to get answer, really !
Interfaces do not exist at runtime in Typescript, so it is not possible to resolve dependencies by interfaces (unless you use a String token that has the same name as your interface and use the @inject() decorator in your constructor)
"Make no mistake, there is a principle like that. A function should do one, and
only one, thing. We use that principle when we are refactoring large functions
into smaller functions; we use it at the lowest levels. But it is not one of the
SOLID principles-it is not the SRP." page 62 of Clean Architecture by Robert C. Martin
great tips keep going
please what the theme name you use in vscode ?
Halcyon! I need to do a video about it soon 😅
Unfortunately not many js developer are even noticed about these principles
I think controller doesn't have a larger scope compare to service.
Service handles all the logic in our application. It is not good to bogus the controller, let service do the logic.
Can you create a nestjs tutorial serie?
How does this architecture work with state management sayusing context api or redux toolkit?
This is a backend framework, does not deal with state management
@@lardosian oh, I read that as Nextjs either way, very informative video
@@josephjoey3904 Easily done, but lots of videos covering what your after - eg React Query for example..
The problem with the SR principle is how you decide what is a single responsibility. Where to draw a line? Uncle Bob clearly said in his book that the SR is not a single fn call The SR means that your method should care only about a single piece of business logic like in your case processing the order. It may involve multiple steps. He's saying parts that have a single common reason for change should live together. Sending an email is a valid part of submitting the order, you want your users to know that the operation is successful and their order is received and processed. In this case, it should live within the service. I guess, you wouldn't want your users to spam you with calls to confirm whether or not you received their order just because you forgot to call an email service after you submitted their order.
Aren't these just basic OOP patterns rebranded for javascript devs?
i definitely agree, they're just trying to do java in typescript :D
pls make course for nest.js
talking about giving urself extra work
The domain logic should not be inside the application layer in your case you show that when you call the email method inside the controller of orders!
There can be a lot of logic from other modules so what you will do in that case is all the logic will be both of them in controllers and in services that's the antipattern
That's wrong!
If you want to call othe method from another service inside a service and avoid to inject this another service, you just can emmit an event and put this other method listen to it.
a genius
Naming a class suffixed with "Service" doesn't make SRP easier, class named as per its responsibility not only enforces the developer to write code in SRP but also for readability, there's no need to name a class called "StorageS3FetcherService" if you can name it as "StorageS3Fetcher"
In your "LSP example", you say, "You don't have to change any code, etc", but you do; you're manually changing the strategy being used via the injection in the controller. How would you go about programmatically determining this? Eg: Activating the sale pricing either based on different times of the year, or being able to arbitrarily use sale pricing. This seems like a bit too contrived of an example that may lead people down an arguably incorrect path.
🚀
This guy is using coding structures that junior developers use till they learn how to properly code. This video is a waste of time in my opinion.
This is monstly not very useful in large codebases.
You totally shot yourself in the foot when you decided to add the sendOrderEmail() in the controller. Sending a notification, in this case an email is part of your business use case. Your service class is responsible for orchestrating the use case. Look at it this way. If you remove the API presentation layer (controllers) can your app still orchestrate the use case? In your case no. You can imagine there may be a number of things you could put in place of the API presentation layer like going from REST to GraphQL or a grpc microservice or even a CLI. Your implementation does not scale given your decision to put business logic inside the controller. Your giving bad advice here ( ua-cam.com/video/vE74gnv4VlY/v-deo.html ). The service class that handles the submitOrder use case should invoke the process of sending an email. If you really want to look like a senior dev then you would suggest dispatching an event once the order is created successfully and allow any number of operations to subscribe to that event one of which may be sending an email.
ua-cam.com/video/vE74gnv4VlY/v-deo.html "you can change the pricing strategy without changing any code", but you changed code to use the other strategy! What you need is a factory that will provide you with the desired pricing strategy based on some context or other condition.