Get the source code for this video for FREE → the-dotnet-weekly.ck.page/result-pattern Want to master Clean Architecture? Go here: bit.ly/3PupkOJ Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
I try to be realistic. I'm always aware of the pros/cons of my choices, but I'm not always so good at articulating that in my videos. This is a good example of me improving at that. 😅
You need to show the caller code too, to be fair. This approach will need if checks (or pattern matching) at the caller side. The exception approach will need try catches at the caller side. An if can be ignored, an exception cannot be ignored. Exceptions are not expensive if your case is exceptional :) But an if must always be checked in the results pattern. Both approches have their pros and cons. I think it depends when to use which pattern.
Result pattern works better with the Railway pattern, IMO. Or even a Maybe monad (see Mark Seeman talk). With some boilerplate it makes the code neat and readable. I had to retractor some "if" laden code that has to handle Result-returnung helpers last week and I'm pretty happy with how it came together (for now).
Ignoring a check on the Result object is you being lazy as a programmer. I can also ignore an exception, right? I just don't write the try-catch statement. If I don't have a global exception handler, it can definitely be ignored - my app crashes. Exceptions are simply not worth it if you already know that something is "wrong" in your flow.
@@MilanJovanovicTech nobody is talking about being lazy. Developers are humans too, we can forget things. Even PR review will slip errors into production now and then. A problem with results pattern is that if you ignore the check other code can run that should not be run. With exceptions that is not possible. What I’m saying is it depends when to use results pattern. In my experience it is very well suited with validation, but I wouldn’t want to control validation with exceptions to begin with … Calling external resources, I would just let the exceptions flow. Results pattern tends to hide information when problems occur here.
I am curious how can someone ignore the result type of the function they are about to call. You'd get compile-time errors if you confuse the actual value with the result one.
@@andreibicu5592 There is not always a usable value returned, as seen in the examples in this video, only Errors. The caller can forget to check for the return value because the function is not returning any usable value, apart from an error of noError.
Both you and @nickchapsas have great teaching methods, ideas, and demeanors - especially to be so young. You're teaching an old dog new tricks. Years ago we lost my previously-favorite instructor (K Scott Allen). If you don't know who he is/was, then please look him up. I wish I had known him in the real world. Anyway, I came here to say that you're doing great work in this world by helping us all ... and at the same time, filling some really big shoes. Kudos ... and please keep it up.
Wow, thank you! I watched dozens of his courses when I was starting out as an engineer, so just to be placed in the same sentence is a huge achievement for me. Thanks again, and I promise to continue sharing practical and helpful content. :)
Implemented Result Pattern in my latest .NET core project. Simple and amazing. Very versatile. Examples -- Capture a transaction. I expect that it can be expired, or that it is already captures. It is not exceptional situation, it happens on regular basis. Previously I used to use exceptions. Rewrote to Result. This change in mindset made me better programmer instantly. And code is cleaner
Hi Milan. Another great video. Thank you! I have some questions: 1. For the Result class, why would you create a constructor with a validation, if it is private? You are the only client and there's no way you could provide wrong values. I would create two separate constructors, a parameterless one (for success) and one with Error (for failure). 2. It's a pitty you didn't mention about the extensions (OnSuccess/OnFailure/Bind/Match/etc), so people don't make too much use of the IsSuccess/IsFailure checks. 3. Wouldn't be better in terms of performance to use the record keyword instead of the class one ? The same way you did with the Error ? 4. What do you think about a Result with a StatusCode? Would it be better without it, and set the status code at the endpoint level? The problem is that there could be many types of errors. What if we miss some? 5. Will you also create a video for the monoids, especially the nullable/optional one ? Looking forward for your next video with a Result with value/values. Thank you!
1. Safety 2. They didn't make sense in the current context - they do make sense in the consuming layer 3. How would it help performance? 4. I like that idea, and i might start implementing it. 5. Undecided on that one. Option could be covered with a Result - right?
The only thing I don't like about the result pattern is the fact that it's complicated to implement generic mediatr behaviors like ValidationPipelineBehavior. People use many ugly hacks with reflection to make it work, or return some envelope from handlers, while when using exceptions you just throw it and catch inside global middleware. But all in all, I prefer the result object pattern with some nice extension methods like Match(onSuccess, onFailure), Bind, and so on. One thing I would add to your implementation would be some kind of ErrorType enum (I find using status codes in Error objects a leaky abstraction because it assumes your presentation layer) which contains some generic errors types like NotFound, Validation, Conflict, Failure, so it's easier to write a handling function inside let's say BaseCarterModule that will return appropiate status codes.
This is very similar to what go does for error handling and it's built in. A lot of people don't like it, but it works better than anything else I've ever used.
Yup, I'm going to update my code to Result approach. Then I will write some extensions like then, tap for Result object, then I can have a list of Result functions that execute sequentially. 😀
Thanks so much for the explanation. I do use Results on my solutions every day. I would argue that the final proposal of combining messages and results for the returning types can be a bit misleading or confusing. I would keep the Result.Success or Result.Failure for the sake of clarity. So you have only one dimension in the abstraction. However, it is great to know more possibilities. Great!
I think some of the magic with Mediator pipeline behaviors and generics make things difficult to follow. Since command handlers are very specific, I prefer to inject the validators into my command handlers. I have to write 3 extra lines in every handler (validate, if not valid, return FailureResult), but it is very clear what is going on. I do prefer the result pattern over exceptions. I return an abstract Result or Result class, but they can be of type SuccessResult, FailureResult, or NotFoundResult. Then in api or razor, can do something like var result = await mediator.Send(command); if (result is FailureResult failureResult) add errors to response, if (failureResult is NotFoundResult) return 404 response, otherwise return 200/201 with any data.
I would always stick to throwing custom exceptions personally. This way you are handling errors in 1 form, as exceptions as you may need to handle other types of exceptions thrown by third party packages. Result object is possible obviously but in yuk in the real world. In modern apps it's easy to find some performance especially when we are talking about using micro services cloud solutions so I would always opt over a maintainable and reliable design over what will cost a tiny bit cheaper to run or people perform ever so slightly better under a certain plan.
- Exceptions might be slower but how many milliseconds more are we talking about and is it going to affect the user experience because of a little more milliseconds? Exceptions are a rare thing to happen and therefore extra processing is not an issue. - Returning Task from a method doesn't look clean. Methods' return types should be something useful and optimistic. Not an error. A drawback of a custome error object is that the callers need to be aware of that object and be able to handle it. - Instead of building many custom exceptions, one can create a custom reusable exception type for different use cases and add a property for the type of exception. - one benefit of exceptions is the ability to have a global exception handler at a top level which an exception can bubble up to, in case a developer forgets to handle it at a lower level.
- Exceptions can reduce throughput by as much as 30% - I return Task or Task - I already have a reusable Result type - and can expand it with a custom "error type" to achieve the same thing - I can still have a global exception handler So what would you do in case of a validation error? Throw an exception?
Nice, seems a kind of railway-oriented programming... What I miss from the failure part is some details/data that helps you identify the troublesome record. Well done.
I already use a similar logic but I liked yours more. however, I'm wondering would it be more useful if it had the capability of returning a list of errors instead one?
I was planning similar result logic as this with list of errors. Obviously this depends what kind of logic you are writing. For example when doing data validation returning multiple errors makes definitely a lot of sense.
Although I like the explanation of the result pattern, my concern is that it is presented as a better approach than exceptions. In my opinion that is not the case. It serves different purposes and handling concerns. Result pattern, I see valid, for validating user input. In that case there is a higher change of invalid data. But please don't litter your entire code base with it. The amount of if-result checks will kill you. Exception have their use case for... exceptions. Only catch them when you handle them. All other cases handle at the top most level (usually some middleware)
"The amount of if-result checks will kill you." Milan didn't mention about the extensions, but you aren't supposed to check everywhere the IsSuccess/IsFailure flags. You would regularly have the OnSuccess/OnFailure/Bind/Match extension methods that will make your code cleaner.
@@andreibicu5592 Exactly. Not only Milan did not mention it, but pretty much _nobody_ who advocates for this technique in these "popular software engineering materials" mentions it. Without extensions like Bind/Match (to mimic that clean monad composition mechanism from languages like Haskell), this "Lose exceptions, use Result pattern instead... because exceptions should be exceptional, y' know" is the worst advice possible to give to an inexperienced architect, let alone a novice programmer, leading to abysmal, impossible to read (and manage) code (and I haven't even mentioned the fact that .NET libraries use exceptions _extensively_, so you cannot escape exception handling anyway). Due to its popularity and almost universal lack of awareness about its caveats, I found this particular advice to be one of the most detrimental ones for the quality of the code produced worldwide (I was sold on it too at one point in my career and I was making sure to use Result pattern _everywhere_). I can only say that I'm at least glad that Jason Taylor did not succumb to it in his CleanArchitecture template and opted for CustomExceptionHandler instead (which is a much smarter choice when it's acceptable to keep things simple and not fixate on the performance that much). Result pattern needs some love from the language. C# does not give it OOTB and, if you want to use the Result pattern without awful consequences, you need to provide this love yourself.
@@andreibicu5592 Exactly. Not only Milan did not mention it, but pretty much _nobody_ who advocates for this technique in these "popular software engineering materials" mentions it. Without extensions like Bind/Match (to mimic that clean monad composition mechanism from languages like Haskell), this "Lose exceptions, use Result pattern instead... because exceptions should be exceptional, right?" is the worst advice possible to give to an inexperienced architect, let alone a novice programmer, leading to abysmal, painful to read (and manage) code (and I haven't even mentioned the fact that NET libraries use exceptions _extensively_, so you cannot escape exception handling anyway). Due to its popularity and almost universal lack of awareness about its caveats, I found this particular advice to be one of the most detrimental ones for the quality of the code produced worldwide (I was sold on it too at one point in my career and I was making sure to use Result pattern _everywhere_). I can only say that I'm at least glad that Jason Taylor did not succumb to it in his CleanArchitecture template and opted for CustomExceptionHandler instead (which is a much smarter choice when it's acceptable to keep things simple and not fixate on the performance that much). Result pattern needs some love from the language. C# does not give it OOTB and, if you want to use the Result pattern without awful consequences, you need to provide this love yourself.
This pattern works really well in languages with native compiler support for the Result type (like in Rust), but for the C# it is rather a compromise solution.
You have nuget packages that provide this kind of object. Showing how it's done is cool, cause you see why you'd use it, but wouldn't recommend implementing it for your app, just using some ready implementation.
I really like the Nomad response approch, since the state is alreway clear what you would get. But IMO this should not always replace Exceptions, especially as a "black-box" (service, framework) writer. Throwing an exception is like placing a stick in the bike wheels just before the cliff. It should stop you from falling over. By using a return type you give the client of your code to overlook the situation, meaning it can simply not check the return type. YES I know it can use a try catch also but this done with though.
This Result Pattern has consistency advantage as well where isSuccess/success always indicates success or fail of the call. Some has 'data' property to store return value, it can be anything like string, JSON and etc. Coders easier to guess what is the output of the APIs although not well documented. Some APIs has unexpected output like same URL can return string, array or file based on different conditions and poorly documented. Coders need to spend more time to test and verify the unexpected outputs. More codes needed as well to handle unexpected outputs.
dude thanks so much, really nice and rich video, I notice in the "evolution" of my programming career that what make u a good programmer is how do u handle errors 😄
Hi Milan, can I perform an implicit cast for a generic type so that instead of returning something like ' EntityResult.Failure(error) ', I can just return ' error '? Error is also generic :/
I was wondering. why do you pass in isSuccess with an error to the constructor. And then doing some validation. When IMO if you just pass in the error object you could use: public bool IsSuccess => Error == Error.None; Maybe im missing something
how would you suggest adding a logging interceptor to this pattern, with a custom exception i use a middleware to catch and log exceptions using serilog all packed in single place.
@@MilanJovanovicTech what if i am not using mediator any alternate? another issue with approach is with exceptions if my service is a return type service it either returns formatted errors or the result, here i guess i will use dynamic return type but that also have its cons a slight runtime overhead and bye bye compile-time type safety.
Is it fair to say that the result pattern is basically used to handle known exceptions and validation errors. But you'd still want exceptional exceptions. Try to figure out if I should start using FluentResults in my Azure Function Apps. At the moment I just throw custom exceptions, but catch them at the top level, do a switch on them to decided which HTTP code to return to the client. But assuming that the benefit is performance with the result pattern as every time I throw the exception, I am building out the stack and all the other data that I don't need or care about in these scenarios?
Exactly, error you know how to handle are a perfect fit for this. We still have "actual" exceptions that we can let bubble up to some global handler and return a 4xx/5xx response. I think you should notice a performance improvement from switching to the Result monad, if you often throw exceptions in the Azure Function.
I'm diving deeper into result pattern. The one drawback I am seeing is if we have many layers such as the Controller, service, repository layers, etc.... Maybe there are tricks to handling that as well? Because if we have a failure in the repository layer, we then have to check in the service layer and then return that and then do a final check in the controller to see if it failed and what response status we want to return, ie: 400, 404, 500, etc...
Another great video and explaination. The last trick with the implicit operator is somethinf I would not do. In my opinion it degrades the readablility as it is, well, implicit. I would need to think about as im reading the code. But that could just be me 😂
This is great! I find it very useful, but with this approach, in the controller, Do we have to manage what type of http status would be returned with a set of ifs statements 🤔 depending on if an error is returned? Could it be a clean way to do it?
Thank you so much for your hard work and the time you've dedicated to creating this video! I'm wondering if you plan to create a video on how to use Localization and Result Pattern together?
This looks cool, and makes sense for basic validation that can be returned to a client, but what if you're using something like Datadog and you need to see failed http requests or integration event handling. If something is failing, I need to see and filter non-200s without having to drilling down the details to expose what is truly happening.
You can extend exception, use http status codes, status messages and throw them, then return standardized error json without this pattern. This pattern is useful only if you can handle the result and provide different approach based on it. But using this pattern to replace exceptions is the worst idea. I'm totally unhappy Milan promotes this approach, without valid or even worse in a wrong context!
@@balazs.hideghety Using Exceptions for things which are not unexpected (like checking and reporting one or more validation errors) is just pain silly. Surely the people that use that pattern understand its basically like having one or more goto statements in the middle of your code which forces it to jump and continue executing somewhere else like some middleware/pipeline method. Using Exceptions like this is a sloppy/lazy bodge - if your going to the job then do it properly!
Yeah, but if I'm passing back Result objects in my methods, how would I pass back whatever I was originally intending to pass back? For example, suppose I have a method that passes back an Order object. If I change it to pass back a Result, then it can't pass back the Order. By throwing an exception, I can pass back the Order object as intended and bubble up an exception when required so it will be handled in a catch(...) clause. Maybe I'm missing something. Please enlighten me.
Hi Milan, Great video again! Thank you!💡 I've a question. I'm seeing you're categorizing things around features in Domain layer. Previsouly you were using Entities, Enums, Value Objects... structure inside the Domain. But in this way, we don't have that since you're grouping around features. Now we put everything related to a feature together such as Repository Interfaces, Enums, Value objects, Custom Errors, Custom Exceptions... What if we get a shared value Object that can be used across multiple features? Then where should we put that since now we don't have ValueObjects grouping? For example lets say we have features such as Customer, Company etc. And we have Value object called PhoneNo which can be used in both Customer Model and Company Model. Now where should we place this? what do you suggest?
For me you have two options. One is to have it duplicated in both features. Cause (usually) this kind of value will have some different behavior in different contexts/features. And those features can possibly be different domains. So at some point different applications Another approach, that saw more often, but it usually ends up being a mess, is to have some kind of "common" feature, where you put this kind of stuff. At first glance it seems cool, but you end up coupling two domains together and the "common" becomes a bucket for stuff that you didn't think through well enough :-D
It's really a useful tutorial, but it's interesting me that which design pattern should we use to handle also unknown exceptions ? There may be some sort of exceptions in runtime that we have not defined, and in compile time to prevent this situation what should we do ?
Could this maybe be expanded in a a way so result.failure also contains a StatusCodeResult or at least a HttpStatusCode? That way we can just return the "value" of a failure in a controller instead of having to manually match based on a string code every time? Not sure if there are any drawbacks to adding sth like this
Hi MIlan, here you gave example in which result object is use in domain service. is it also okay to use result object in Entity as well. or in entity exception is always preferable.
Thanks for sharing this milan. You mentioned that this approach might consume more memory allocation. Could you please elaborate on it ? And how to overcome such scenarios? One more request sorry, can you do vlog on using task, whenall, get data properly even if some tasks fails, etc. i feel this is something very common thing which devs should be using, but there isnt any good posts that properly shows how to do it efficiently. Thanks again !
Tbh I’m in two minds about the Result. It’s a great idea, and works however it does bloat the code out if it is complex enough. On a positive side - you are forced to handle the failure explicitly. On a negative side - you have to handle the failure and bubble it all the way up, even if the failure scenario is rare. Also, very quickly you are going to come across the need to aggregate the failures all the way up. Basically your code complexity will increase along the way. If your app is small enough then maybe it’s worth it, otherwise I don’t think so. It almost feels like you have to fight the language to shove the Result in. I don’t think there’s anything wrong with custom exceptions. Ppl say they are expensive, yes they are but how many is your app throwing? If you constantly generating them - you got much bigger problems than the cost of the exception
Sorry, I expressed myself wrong can you do a tutorial using Result Pattern with FluentValidation and MediatR Behaviours? I already see that on Reddit a long time ago.
There is nothing wrong with the result pattern, it’s just a pattern and needs to be applied pragmatically. In one of the projects I exclusively applied it to the entity and value objects validation, as well as interacting with 3rd party apis. This is a trade of software engineering, not a dogmatic rule following, you pick up the best tools for the job and apply them.
@@MilanJovanovicTech existing large projects. I’ve tried to add a result to one of the apis, which wasn’t even that big. It prompted lots of changes bottom up, making the code a lot more verbose, which was not the end of the world. But then, it started posing real questions in terms of what to do in a failure or exception scenarios ( my result was a container for success, failure and exception), which basically turned into a business problem. In real commercial projects nobody is going to spend time on these sort of things if they don’t see an obvious business value
Great video. I was walking through all your DDD videos but can't remember where you explained what type to return from either Domain Service method, or Entitiy method, etc. Can you please attach that URL for me
I like this approach but im wondering. How do you return dynamic errors? for example: "You already following {channelName}". How do you achieve this since errors are just static strings.
Milan. Your tutorial are awesome.. Your DDD series is too good its help me a lot I mean I can't explain... Since 8 months I'm following you. I have a query can we set config values at runtime. I'm mean make a setup page provide the details like database provider (sqlite, sql server, mysql etc.). If it possible then make a video on this topic. It's a request from my side.. please 🙏
@MilanJovanovicTech isn't there something to convert the exception to an error and return it then handle it normally in the upper layer ? And if there is a way could you recommend some article for it
I think result pattern preferred over exceptions is terrible. The problem arises when you have 3 different layers and you want to pass down the error through all of them and then return different status code based on that. I'd prefer result pattern where I want to do something differently if my call failed. If I just need to map it to a different status code I'd prefer exceptions + mapping them in exception handler.
What stops you from doing stuff differently when your call fails? You map these as well as exceptions. Also you can implement your own status codes inside results if you want.
@@KingOfBlades27 yes. In my current project, we implement status codes based on HTTP as we are using REST APIs. Unforessen exceptions are caught by the middleware and logged accordingly.
@@MilanJovanovicTechit might only make it less painful. Don't get me wrong, result pattern or monads are fine as long as you need to perform different action based on success or failure (typically just the caller handles it differently) If used as a replacement to exception / exception handler mapping it is just painful
Why would the stacktrace be needed in a domain error. You are telling the calling code it can't do something, not investigating why something irregular happened..
Thanks for the video. I am curious that you told us that it is expensive to throw exceptions. But, they took less mental time to process what you have shown us. Please, tell us how much is your code more efficient regarding a) code performance (have you done benchmarking?) b) developer handing the error until shown to user (how many ifs are added to handle 4 errors (catches would be 4 and they are below the main code so they don't make the code that ugly) but 4 ifs inside core business logic (I am not sure if it looks good).
You'd still have the IFs in your code business logic - the only difference is throwing exceptions vs returning an error. Even worse if the exceptions are hidden - then you have no idea what could happen without examining the underlying calls. As far as performance, you can see as much as a ~30% drop in request throughput when throwing exceptions. At the end of the day, it's about intent for me. Results make more sense.
I think Exceptions is better. You don't need to pollute of your API (internal and public) method signatures with your result classes. Consumer automatically stops on exception (you don't need to handle it). Just use one or two exception classes with error codes. Don't use exception handling in consumer's loops. Use aspnet filter (or even AOP like Fody) to handle exceptions in controller and wrap in result classes if you need.
Exceptions should only be used when there is an actual Exception. Using Exceptions for things like validation errors that are not unexpected which are then caught and handled by a middleware/pipeline method is almost no different to putting one or more goto statements in the method doing the checks. I can't believe anyone would still use such a lazy and sloppy way to handle validation errors in a modern solution.
@@dwhxyz from what you've said it seems like there are no handling of exceptions because knowing about its possibility means that it's an expected error, hence we should handle it as errors and not exceptions. That logic makes exception a 👻, we must not know what they are for them to exist .
@@nikalisten Checking state when appropriate and throwing an Exception if the state is unexpected is very different to checking and responding to expected state. Expected state can lead to both happy and unhappy paths as demonstrated in the case when validating an incoming entity from an external source. Most checks/guards which results in throwing an Exception are normally put there as a nicer alternative to letting the runtime and/or .NET/third party library code throwing an Exception with a not so helpful message. There will be however other times where an Exception is thrown after checking for bad state, even though it should in theory not be possible for that state to have ended up bad in first place. The reason its done is because if left unguarded, the consequences of that bad state if the program continues to execute could be catastrophic, especially if the program happy carries on executing without triggering other Exceptions somewhere else. You could almost consider Exceptions thrown in those cases as runtime unit tests.
I've done this in the past and eventually ended up with just bouncing off the count of errors in the result to determine success or failure so I didn't have to inform the class itself.
@MilanJovanovicTech I didn't have to inform the result object of success state via constructor. It self determined the state. So, I just collected all errors, put then in a list even if just one and used the existence of one to figure out success or fail. Smaller signature, self determined, snd success is based off of count internal instead of external determination.
Great video thanks. However, i have mixed feelings on implicit conversions of return types. In this case it's pretty straight forward and should be easy for new devs to figure out... but i've seen implicit conversions that invoke database calls and proxy objects to the point you never know what is going on.
I downloaded the solution and I have a question, not related to the video itself. I can see that you use to make constructors private and static Create methods for Entity classes. Like Follower for example. I understand the problem of constructors for Entities: 1). What if some state field becomes optional? 2). What if you have to 1,2 or more new fields? 3). What if some state field will need validation? 4). What if you have 2 parameters of the same type coming one by another. Like firstName, lastName. You can accidentally mess them, compiler won't argue. All this problems can be solved by using fluent builder. I would understand if you had some interesting method name, describing what exactly is going on when you create an object via this method. Lets call this approach - Named Constructors. But what exactly are you trying to achieve when you move object init logic from constructor to static method with simple name Create?
I can place side effects in the factory method, like raising domain events. Doing that in the constructor could lead to strange behavior because of ORMs and Serializers using the constructors. Also, it allows me to "fail" creation gracefully with a Result object. Whereas with a constructor I'd have to throw an exception.
Great video, on the Result class you should have used the IsFailure on line 13 but other than that, it's perfect. Also what would you do if you have an array of objects you lets say need to import through an API and every import result can fail. I'm doing the import asynchronously on a list and I think I don't want to stop on a failure of a single object import, but do want to keep importing everything, collecting the errors/successes/results.
Get the source code for this video for FREE → the-dotnet-weekly.ck.page/result-pattern
Want to master Clean Architecture? Go here: bit.ly/3PupkOJ
Want to unlock Modular Monoliths? Go here: bit.ly/3SXlzSt
I like that you comment on the pros and cons of your implementaton ~ 11:30. You're not dogmatic about it. Thank you for your information.
I try to be realistic. I'm always aware of the pros/cons of my choices, but I'm not always so good at articulating that in my videos. This is a good example of me improving at that. 😅
You need to show the caller code too, to be fair.
This approach will need if checks (or pattern matching) at the caller side.
The exception approach will need try catches at the caller side.
An if can be ignored, an exception cannot be ignored.
Exceptions are not expensive if your case is exceptional :)
But an if must always be checked in the results pattern.
Both approches have their pros and cons. I think it depends when to use which pattern.
Result pattern works better with the Railway pattern, IMO. Or even a Maybe monad (see Mark Seeman talk). With some boilerplate it makes the code neat and readable. I had to retractor some "if" laden code that has to handle Result-returnung helpers last week and I'm pretty happy with how it came together (for now).
Ignoring a check on the Result object is you being lazy as a programmer. I can also ignore an exception, right? I just don't write the try-catch statement. If I don't have a global exception handler, it can definitely be ignored - my app crashes.
Exceptions are simply not worth it if you already know that something is "wrong" in your flow.
@@MilanJovanovicTech nobody is talking about being lazy. Developers are humans too, we can forget things. Even PR review will slip errors into production now and then.
A problem with results pattern is that if you ignore the check other code can run that should not be run.
With exceptions that is not possible.
What I’m saying is it depends when to use results pattern.
In my experience it is very well suited with validation, but I wouldn’t want to control validation with exceptions to begin with …
Calling external resources, I would just let the exceptions flow. Results pattern tends to hide information when problems occur here.
I am curious how can someone ignore the result type of the function they are about to call. You'd get compile-time errors if you confuse the actual value with the result one.
@@andreibicu5592 There is not always a usable value returned, as seen in the examples in this video, only Errors.
The caller can forget to check for the return value because the function is not returning any usable value, apart from an error of noError.
Almost every single time I watch one of your videos I got shocked.... that I really know nothing..... thanks for sharing!
Huh 😂 There's always something to learn, so I don't think much about it
Both you and @nickchapsas have great teaching methods, ideas, and demeanors - especially to be so young. You're teaching an old dog new tricks. Years ago we lost my previously-favorite instructor (K Scott Allen). If you don't know who he is/was, then please look him up. I wish I had known him in the real world. Anyway, I came here to say that you're doing great work in this world by helping us all ... and at the same time, filling some really big shoes. Kudos ... and please keep it up.
Wow, thank you! I watched dozens of his courses when I was starting out as an engineer, so just to be placed in the same sentence is a huge achievement for me. Thanks again, and I promise to continue sharing practical and helpful content. :)
Implemented Result Pattern in my latest .NET core project. Simple and amazing. Very versatile. Examples -- Capture a transaction. I expect that it can be expired, or that it is already captures. It is not exceptional situation, it happens on regular basis. Previously I used to use exceptions. Rewrote to Result. This change in mindset made me better programmer instantly. And code is cleaner
It's all about the intent of your code.
How do you manage the http status codes to return in the controller based on different errors that the service may return using the result pattern?
ErrorOr
Mhm
I was hoping you'd also talk about returning multiple validation errors. nice video btw!
Touched on that here: ua-cam.com/video/85dxwd8HzEk/v-deo.html
But not to difficult to alter the Result class
Hi Milan. Another great video. Thank you!
I have some questions:
1. For the Result class, why would you create a constructor with a validation, if it is private?
You are the only client and there's no way you could provide wrong values.
I would create two separate constructors, a parameterless one (for success) and one with Error (for failure).
2. It's a pitty you didn't mention about the extensions (OnSuccess/OnFailure/Bind/Match/etc), so people don't make too much use of the IsSuccess/IsFailure checks.
3. Wouldn't be better in terms of performance to use the record keyword instead of the class one ? The same way you did with the Error ?
4. What do you think about a Result with a StatusCode?
Would it be better without it, and set the status code at the endpoint level?
The problem is that there could be many types of errors. What if we miss some?
5. Will you also create a video for the monoids, especially the nullable/optional one ?
Looking forward for your next video with a Result with value/values.
Thank you!
1. Safety
2. They didn't make sense in the current context - they do make sense in the consuming layer
3. How would it help performance?
4. I like that idea, and i might start implementing it.
5. Undecided on that one. Option could be covered with a Result - right?
Was actually wondering good way to implement similar logic. Implicit conversion in the end was the icing on the cake. Great video 🎉
Glad you enjoyed it!
Great, thank you Milan.
That was helpful; it allowed me to better understand the ErrorOr pattern created by Amichai Mantiband.
Glad it was helpful!
Beautiful Implementation ! You are extremely good explaining! Congratulations! Keep coding and making videos you are a natural!
Thanks a lot!
The only thing I don't like about the result pattern is the fact that it's complicated to implement generic mediatr behaviors like ValidationPipelineBehavior. People use many ugly hacks with reflection to make it work, or return some envelope from handlers, while when using exceptions you just throw it and catch inside global middleware. But all in all, I prefer the result object pattern with some nice extension methods like Match(onSuccess, onFailure), Bind, and so on. One thing I would add to your implementation would be some kind of ErrorType enum (I find using status codes in Error objects a leaky abstraction because it assumes your presentation layer) which contains some generic errors types like NotFound, Validation, Conflict, Failure, so it's easier to write a handling function inside let's say BaseCarterModule that will return appropiate status codes.
Okay, I'll show a simple solution next video - no hacks, no nothing. Just interfaces and generic constraints.👍
The error type enum is a nice touch 👌
This is very similar to what go does for error handling and it's built in. A lot of people don't like it, but it works better than anything else I've ever used.
Agreed, I've seen a lot of pushback to this approach. More languages should do what Go does.
Yup, I'm going to update my code to Result approach. Then I will write some extensions like then, tap for Result object, then I can have a list of Result functions that execute sequentially. 😀
Check out some of my Railway-Oriented Programming videos
The last tip was a nice touch. Have been doing the same for around ten years. I will upgrade my code. Thanks :)
Glad it was helpful!
Thanks so much for the explanation. I do use Results on my solutions every day.
I would argue that the final proposal of combining messages and results for the returning types can be a bit misleading or confusing. I would keep the Result.Success or Result.Failure for the sake of clarity. So you have only one dimension in the abstraction. However, it is great to know more possibilities.
Great!
It's still Success/Failure, though. What changes is the error type.
I think some of the magic with Mediator pipeline behaviors and generics make things difficult to follow. Since command handlers are very specific, I prefer to inject the validators into my command handlers. I have to write 3 extra lines in every handler (validate, if not valid, return FailureResult), but it is very clear what is going on. I do prefer the result pattern over exceptions. I return an abstract Result or Result class, but they can be of type SuccessResult, FailureResult, or NotFoundResult. Then in api or razor, can do something like var result = await mediator.Send(command); if (result is FailureResult failureResult) add errors to response, if (failureResult is NotFoundResult) return 404 response, otherwise return 200/201 with any data.
But that's 3 extra lines, across a few hundred use cases? Adds up
I would always stick to throwing custom exceptions personally. This way you are handling errors in 1 form, as exceptions as you may need to handle other types of exceptions thrown by third party packages. Result object is possible obviously but in yuk in the real world. In modern apps it's easy to find some performance especially when we are talking about using micro services cloud solutions so I would always opt over a maintainable and reliable design over what will cost a tiny bit cheaper to run or people perform ever so slightly better under a certain plan.
Wait till you hear about Railway-Oriented Programming
- Exceptions might be slower but how many milliseconds more are we talking about and is it going to affect the user experience because of a little more milliseconds? Exceptions are a rare thing to happen and therefore extra processing is not an issue.
- Returning Task from a method doesn't look clean. Methods' return types should be something useful and optimistic. Not an error. A drawback of a custome error object is that the callers need to be aware of that object and be able to handle it.
- Instead of building many custom exceptions, one can create a custom reusable exception type for different use cases and add a property for the type of exception.
- one benefit of exceptions is the ability to have a global exception handler at a top level which an exception can bubble up to, in case a developer forgets to handle it at a lower level.
- Exceptions can reduce throughput by as much as 30%
- I return Task or Task
- I already have a reusable Result type - and can expand it with a custom "error type" to achieve the same thing
- I can still have a global exception handler
So what would you do in case of a validation error? Throw an exception?
Nice, seems a kind of railway-oriented programming... What I miss from the failure part is some details/data that helps you identify the troublesome record. Well done.
The Error can be expanded further to contain more details
I think you could even create an exception and return that (rather than throw it).
I already use a similar logic but I liked yours more. however, I'm wondering would it be more useful if it had the capability of returning a list of errors instead one?
I was planning similar result logic as this with list of errors. Obviously this depends what kind of logic you are writing. For example when doing data validation returning multiple errors makes definitely a lot of sense.
Yeah, for sure. You can easily modify the Result class to support that.
You can also use custom exception and use an enum as the error code.
You can do that with Error type also
Throwing exception for input validation is baaad. You don't want to have exception driven logic.
Although I like the explanation of the result pattern, my concern is that it is presented as a better approach than exceptions. In my opinion that is not the case. It serves different purposes and handling concerns.
Result pattern, I see valid, for validating user input. In that case there is a higher change of invalid data. But please don't litter your entire code base with it. The amount of if-result checks will kill you.
Exception have their use case for... exceptions. Only catch them when you handle them. All other cases handle at the top most level (usually some middleware)
So what will you do when you fail a precondition in a use case?
"The amount of if-result checks will kill you."
Milan didn't mention about the extensions, but you aren't supposed to check everywhere the IsSuccess/IsFailure flags. You would regularly have the OnSuccess/OnFailure/Bind/Match extension methods that will make your code cleaner.
@@andreibicu5592 Exactly. Not only Milan did not mention it, but pretty much _nobody_ who advocates for this technique in these "popular software engineering materials" mentions it. Without extensions like Bind/Match (to mimic that clean monad composition mechanism from languages like Haskell), this "Lose exceptions, use Result pattern instead... because exceptions should be exceptional, y' know" is the worst advice possible to give to an inexperienced architect, let alone a novice programmer, leading to abysmal, impossible to read (and manage) code (and I haven't even mentioned the fact that .NET libraries use exceptions _extensively_, so you cannot escape exception handling anyway).
Due to its popularity and almost universal lack of awareness about its caveats, I found this particular advice to be one of the most detrimental ones for the quality of the code produced worldwide (I was sold on it too at one point in my career and I was making sure to use Result pattern _everywhere_). I can only say that I'm at least glad that Jason Taylor did not succumb to it in his CleanArchitecture template and opted for CustomExceptionHandler instead (which is a much smarter choice when it's acceptable to keep things simple and not fixate on the performance that much).
Result pattern needs some love from the language. C# does not give it OOTB and, if you want to use the Result pattern without awful consequences, you need to provide this love yourself.
@@andreibicu5592 Exactly. Not only Milan did not mention it, but pretty much _nobody_ who advocates for this technique in these "popular software engineering materials" mentions it. Without extensions like Bind/Match (to mimic that clean monad composition mechanism from languages like Haskell), this "Lose exceptions, use Result pattern instead... because exceptions should be exceptional, right?" is the worst advice possible to give to an inexperienced architect, let alone a novice programmer, leading to abysmal, painful to read (and manage) code (and I haven't even mentioned the fact that NET libraries use exceptions _extensively_, so you cannot escape exception handling anyway).
Due to its popularity and almost universal lack of awareness about its caveats, I found this particular advice to be one of the most detrimental ones for the quality of the code produced worldwide (I was sold on it too at one point in my career and I was making sure to use Result pattern _everywhere_). I can only say that I'm at least glad that Jason Taylor did not succumb to it in his CleanArchitecture template and opted for CustomExceptionHandler instead (which is a much smarter choice when it's acceptable to keep things simple and not fixate on the performance that much).
Result pattern needs some love from the language. C# does not give it OOTB and, if you want to use the Result pattern without awful consequences, you need to provide this love yourself.
Thank you for taking the time to make this video. Much appreciated.
Glad it was helpful!
im currently on dart project but still watch milan's video to grasp oop concepts 😂 thank you
You're most welcome :)
This pattern works really well in languages with native compiler support for the Result type (like in Rust), but for the C# it is rather a compromise solution.
I make it work 🤷♂️
You have nuget packages that provide this kind of object. Showing how it's done is cool, cause you see why you'd use it, but wouldn't recommend implementing it for your app, just using some ready implementation.
I really like the Nomad response approch, since the state is alreway clear what you would get. But IMO this should not always replace Exceptions, especially as a "black-box" (service, framework) writer.
Throwing an exception is like placing a stick in the bike wheels just before the cliff. It should stop you from falling over. By using a return type you give the client of your code to overlook the situation, meaning it can simply not check the return type. YES I know it can use a try catch also but this done with though.
I'm not saying don't use exceptions at all. Just don't use them for return values in _expected applications flows_
Watched your video first time. Really impressed. Thanks for sharing
Glad you enjoyed it!
This Result Pattern has consistency advantage as well where isSuccess/success always indicates success or fail of the call. Some has 'data' property to store return value, it can be anything like string, JSON and etc. Coders easier to guess what is the output of the APIs although not well documented.
Some APIs has unexpected output like same URL can return string, array or file based on different conditions and poorly documented. Coders need to spend more time to test and verify the unexpected outputs. More codes needed as well to handle unexpected outputs.
I'd say consistency is important :)
dude thanks so much, really nice and rich video, I notice in the "evolution" of my programming career that what make u a good programmer is how do u handle errors 😄
Glad I could help!
Hi Milan, can I perform an implicit cast for a generic type so that instead of returning something like ' EntityResult.Failure(error) ', I can just return ' error '? Error is also generic :/
You can't
@@MilanJovanovicTech Thanks for the quick response and the video!
Great stuff, thx Milan, the only issue I have with this is the fact that it increases the code complexity.
That's the tradeoff
Very helpful tutorial. This approach helps a lot with unit testing.
The next video shows that ua-cam.com/video/o_KVvUjwxIw/v-deo.html
I was wondering. why do you pass in isSuccess with an error to the constructor. And then doing some validation. When IMO if you just pass in the error object you could use: public bool IsSuccess => Error == Error.None; Maybe im missing something
Practicality, but sure you can make it more concise like that
how would you suggest adding a logging interceptor to this pattern, with a custom exception i use a middleware to catch and log exceptions using serilog all packed in single place.
MediatR pipeline behavior?
@@MilanJovanovicTech what if i am not using mediator any alternate? another issue with approach is with exceptions if my service is a return type service it either returns formatted errors or the result, here i guess i will use dynamic return type but that also have its cons a slight runtime overhead and bye bye compile-time type safety.
Is it fair to say that the result pattern is basically used to handle known exceptions and validation errors. But you'd still want exceptional exceptions. Try to figure out if I should start using FluentResults in my Azure Function Apps. At the moment I just throw custom exceptions, but catch them at the top level, do a switch on them to decided which HTTP code to return to the client. But assuming that the benefit is performance with the result pattern as every time I throw the exception, I am building out the stack and all the other data that I don't need or care about in these scenarios?
Exactly, error you know how to handle are a perfect fit for this. We still have "actual" exceptions that we can let bubble up to some global handler and return a 4xx/5xx response.
I think you should notice a performance improvement from switching to the Result monad, if you often throw exceptions in the Azure Function.
I'm diving deeper into result pattern. The one drawback I am seeing is if we have many layers such as the Controller, service, repository layers, etc....
Maybe there are tricks to handling that as well? Because if we have a failure in the repository layer, we then have to check in the service layer and then return that and then do a final check in the controller to see if it failed and what response status we want to return, ie: 400, 404, 500, etc...
Yes, that's a side effect
This aproach works well on simple operations.
If you will do some more complicated, you will cry.
Probably
Not related to technical, I love ur theme. 😂🎉
The IDE? ReSharper :)
Another great video and explaination. The last trick with the implicit operator is somethinf I would not do. In my opinion it degrades the readablility as it is, well, implicit. I would need to think about as im reading the code. But that could just be me 😂
A fair argument. Also because it wouldn't work as smooth with Result when trying to return an error
I am with you in this one. Actually, I just comment something along those lines your point out here.
This is great! I find it very useful, but with this approach, in the controller, Do we have to manage what type of http status would be returned with a set of ifs statements 🤔 depending on if an error is returned? Could it be a clean way to do it?
Yes, you can create a Match function and return something generic for an error: ua-cam.com/video/YBK93gkGRj8/v-deo.html
@@MilanJovanovicTech interesting. I will keep a check on that. Thanks!
Still would like to see an example with data return on success. You said "Next Video", but I sure could use an example here.
Well, it's a tight line between keep the videos concise and focused and adding too much to go off topic
How I can implement this in an API, If I Have to return differents ViewModels or Errors
What do you mean?
I loved this tip! How do you suggest reporting error notifications from the Handler to FollowerErrors?
ProblemDetails in API?
Thank you so much for your hard work and the time you've dedicated to creating this video! I'm wondering if you plan to create a video on how to use Localization and Result Pattern together?
Doubt I'll touch on Localization
This looks cool, and makes sense for basic validation that can be returned to a client, but what if you're using something like Datadog and you need to see failed http requests or integration event handling. If something is failing, I need to see and filter non-200s without having to drilling down the details to expose what is truly happening.
HTTP request can fail without having to throw exceptions right?
@@MilanJovanovicTech good point.
You can extend exception, use http status codes, status messages and throw them, then return standardized error json without this pattern. This pattern is useful only if you can handle the result and provide different approach based on it. But using this pattern to replace exceptions is the worst idea. I'm totally unhappy Milan promotes this approach, without valid or even worse in a wrong context!
@@balazs.hideghety Using Exceptions for things which are not unexpected (like checking and reporting one or more validation errors) is just pain silly. Surely the people that use that pattern understand its basically like having one or more goto statements in the middle of your code which forces it to jump and continue executing somewhere else like some middleware/pipeline method. Using Exceptions like this is a sloppy/lazy bodge - if your going to the job then do it properly!
Yeah, but if I'm passing back Result objects in my methods, how would I pass back whatever I was originally intending to pass back? For example, suppose I have a method that passes back an Order object. If I change it to pass back a Result, then it can't pass back the Order. By throwing an exception, I can pass back the Order object as intended and bubble up an exception when required so it will be handled in a catch(...) clause. Maybe I'm missing something. Please enlighten me.
Result - check some of the next videos
What is different with this exception handling versus something like the ErrorOr nuget package?
Same idea
How would you use the Result object with value types for a domain model?
Result generic type
Hi Milan, Great video again! Thank you!💡
I've a question. I'm seeing you're categorizing things around features in Domain layer. Previsouly you were using Entities, Enums, Value Objects... structure inside the Domain. But in this way, we don't have that since you're grouping around features. Now we put everything related to a feature together such as Repository Interfaces, Enums, Value objects, Custom Errors, Custom Exceptions...
What if we get a shared value Object that can be used across multiple features? Then where should we put that since now we don't have ValueObjects grouping?
For example lets say we have features such as Customer, Company etc. And we have Value object called PhoneNo which can be used in both Customer Model and Company Model. Now where should we place this? what do you suggest?
That's coming in tomorrows video :)
For me you have two options.
One is to have it duplicated in both features. Cause (usually) this kind of value will have some different behavior in different contexts/features. And those features can possibly be different domains. So at some point different applications
Another approach, that saw more often, but it usually ends up being a mess, is to have some kind of "common" feature, where you put this kind of stuff. At first glance it seems cool, but you end up coupling two domains together and the "common" becomes a bucket for stuff that you didn't think through well enough :-D
@@woocaschnowak Yeah, Thanks for the info.
I like to handle error code in middleware, that way its in one place using a standard pattern. Throwing custom exceptions does divide the community...
Definitely a divisive topic 😁
I found from other articles, some programmers also implement a Match method. Why did you decide not to use it here?
It wasn't needed. I use a Match in endpoints in most cases where I use a Result, actually
@@MilanJovanovicTech Thanks.
It's really a useful tutorial, but it's interesting me that which design pattern should we use to handle also unknown exceptions ? There may be some sort of exceptions in runtime that we have not defined, and in compile time to prevent this situation what should we do ?
For unknown exceptions you use... exceptions 😁
Could this maybe be expanded in a a way so result.failure also contains a StatusCodeResult or at least a HttpStatusCode? That way we can just return the "value" of a failure in a controller instead of having to manually match based on a string code every time? Not sure if there are any drawbacks to adding sth like this
Yes, you can do that
Awesome ❤❤
But I missed the strong type return without IResult return in minimal API. Get stick here for in my project😞
Also take a look at this: ua-cam.com/video/YBK93gkGRj8/v-deo.html
Awesome video! Thanks so much to you!.
Glad it was helpful!
How can one effectively handle various error status codes in this context?
You can introduce an enum to group the error codes by type
Hi Milan! What moq library do you prefer to use?
NSubstitute or Moq
Hi MIlan, here you gave example in which result object is use in domain service. is it also okay to use result object in Entity as well. or in entity exception is always preferable.
Yes, perfectly fine
Thanks for sharing this milan. You mentioned that this approach might consume more memory allocation. Could you please elaborate on it ? And how to overcome such scenarios? One more request sorry, can you do vlog on using task, whenall, get data properly even if some tasks fails, etc. i feel this is something very common thing which devs should be using, but there isnt any good posts that properly shows how to do it efficiently. Thanks again !
Tbh I’m in two minds about the Result. It’s a great idea, and works however it does bloat the code out if it is complex enough. On a positive side - you are forced to handle the failure explicitly. On a negative side - you have to handle the failure and bubble it all the way up, even if the failure scenario is rare. Also, very quickly you are going to come across the need to aggregate the failures all the way up. Basically your code complexity will increase along the way. If your app is small enough then maybe it’s worth it, otherwise I don’t think so. It almost feels like you have to fight the language to shove the Result in. I don’t think there’s anything wrong with custom exceptions. Ppl say they are expensive, yes they are but how many is your app throwing? If you constantly generating them - you got much bigger problems than the cost of the exception
Task.WhenAll is a good suggestion 👌
Each Result object is another allocation. If you have many of them it adds up.
Sorry, I expressed myself wrong can you do a tutorial using Result Pattern with FluentValidation and MediatR Behaviours? I already see that on Reddit a long time ago.
Let me see, maybe I already covered it: ua-cam.com/video/85dxwd8HzEk/v-deo.html
That was really interesting approach, thank you.
How would you implement stacktrace in Result class?
Environment.StackTrace
super 😊
what about Result in Minimal API? Is it possible return strong type?
any video or resource plz.
Return TypedResults
Thanks for sharing your time and knowledge,
Most welcome!
I just implemented this lesson today in my email sender code, and it is much more readable now. Thanks again
It was very nice approach, but I wish you could show where and how to use that result types
Didn't I show it enough in the video?
By the way, you forgot to specify type parameter T after Match in your blog.
public static T Match(...){}
should be public static T Match(...){}
Good catch!
There is nothing wrong with the result pattern, it’s just a pattern and needs to be applied pragmatically. In one of the projects I exclusively applied it to the entity and value objects validation, as well as interacting with 3rd party apis. This is a trade of software engineering, not a dogmatic rule following, you pick up the best tools for the job and apply them.
When would you not use it?
@@MilanJovanovicTech existing large projects. I’ve tried to add a result to one of the apis, which wasn’t even that big. It prompted lots of changes bottom up, making the code a lot more verbose, which was not the end of the world. But then, it started posing real questions in terms of what to do in a failure or exception scenarios ( my result was a container for success, failure and exception), which basically turned into a business problem. In real commercial projects nobody is going to spend time on these sort of things if they don’t see an obvious business value
Great video. I was walking through all your DDD videos but can't remember where you explained what type to return from either Domain Service method, or Entitiy method, etc. Can you please attach that URL for me
It could be this one? ua-cam.com/video/KgfzM0QWHrQ/v-deo.html
Well explained !!!
Thanks :)
What do u mean lack of stack trace and how can we fix it?
Results don't have a stack trace like exceptions do. Environment.StackTrace is an option.
Great Explanations . How can we return status code say 401 using result pattern
Yes, of course
I like this approach but im wondering. How do you return dynamic errors? for example: "You already following {channelName}". How do you achieve this since errors are just static strings.
Turn it into a function -> FollowerErrors.AlreadyFollowing(channelName)
And just construct the error using that parameter
Milan. Your tutorial are awesome..
Your DDD series is too good its help me a lot I mean I can't explain...
Since 8 months I'm following you.
I have a query can we set config values at runtime. I'm mean make a setup page provide the details like database provider (sqlite, sql server, mysql etc.). If it possible then make a video on this topic.
It's a request from my side.. please 🙏
It doesn't make much sense to update these at runtime 🤔
@@MilanJovanovicTech ok. Thank you...
Thanks for the video, but i am wondering how to handle un expected exceptions
Try-catch 😁
@MilanJovanovicTech isn't there something to convert the exception to an error and return it then handle it normally in the upper layer ? And if there is a way could you recommend some article for it
What if i need to return entity in Result class? It can be archieved using TEntity data property?
Btw, nice video. U r awesome
Create a Result wrapper, covered in one of the next few videos
what about returning data types instead of only success/failure?
Create a generic Result (next video)
I think result pattern preferred over exceptions is terrible. The problem arises when you have 3 different layers and you want to pass down the error through all of them and then return different status code based on that. I'd prefer result pattern where I want to do something differently if my call failed. If I just need to map it to a different status code I'd prefer exceptions + mapping them in exception handler.
What stops you from doing stuff differently when your call fails? You map these as well as exceptions. Also you can implement your own status codes inside results if you want.
@@KingOfBlades27 yes. In my current project, we implement status codes based on HTTP as we are using REST APIs. Unforessen exceptions are caught by the middleware and logged accordingly.
@@PauloWirth Sounds about what I have been planning to do 👍
Use vertical slices (or as few layers as possible) - it'll reduce the overhead.
@@MilanJovanovicTechit might only make it less painful. Don't get me wrong, result pattern or monads are fine as long as you need to perform different action based on success or failure (typically just the caller handles it differently) If used as a replacement to exception / exception handler mapping it is just painful
Why would the stacktrace be needed in a domain error. You are telling the calling code it can't do something, not investigating why something irregular happened..
Logging errors, mainly
Hi, is it good if i add stack traces to the Result object too?
I did that on one project, and it worked fine
Thanks for the video. I am curious that you told us that it is expensive to throw exceptions. But, they took less mental time to process what you have shown us.
Please, tell us how much is your code more efficient regarding a) code performance (have you done benchmarking?) b) developer handing the error until shown to user (how many ifs are added to handle 4 errors (catches would be 4 and they are below the main code so they don't make the code that ugly) but 4 ifs inside core business logic (I am not sure if it looks good).
You'd still have the IFs in your code business logic - the only difference is throwing exceptions vs returning an error.
Even worse if the exceptions are hidden - then you have no idea what could happen without examining the underlying calls.
As far as performance, you can see as much as a ~30% drop in request throughput when throwing exceptions.
At the end of the day, it's about intent for me. Results make more sense.
But isnt the errors and exceptions are different all together. Why r we converting exception to errors ?
I think Exceptions is better. You don't need to pollute of your API (internal and public) method signatures with your result classes. Consumer automatically stops on exception (you don't need to handle it). Just use one or two exception classes with error codes. Don't use exception handling in consumer's loops. Use aspnet filter (or even AOP like Fody) to handle exceptions in controller and wrap in result classes if you need.
What about the performance impact?
@@MilanJovanovicTech Is that really an issue when we are writing simple apis?
Exceptions should only be used when there is an actual Exception. Using Exceptions for things like validation errors that are not unexpected which are then caught and handled by a middleware/pipeline method is almost no different to putting one or more goto statements in the method doing the checks. I can't believe anyone would still use such a lazy and sloppy way to handle validation errors in a modern solution.
@@dwhxyz from what you've said it seems like there are no handling of exceptions because knowing about its possibility means that it's an expected error, hence we should handle it as errors and not exceptions. That logic makes exception a 👻, we must not know what they are for them to exist .
@@nikalisten Checking state when appropriate and throwing an Exception if the state is unexpected is very different to checking and responding to expected state. Expected state can lead to both happy and unhappy paths as demonstrated in the case when validating an incoming entity from an external source. Most checks/guards which results in throwing an Exception are normally put there as a nicer alternative to letting the runtime and/or .NET/third party library code throwing an Exception with a not so helpful message. There will be however other times where an Exception is thrown after checking for bad state, even though it should in theory not be possible for that state to have ended up bad in first place. The reason its done is because if left unguarded, the consequences of that bad state if the program continues to execute could be catastrophic, especially if the program happy carries on executing without triggering other Exceptions somewhere else. You could almost consider Exceptions thrown in those cases as runtime unit tests.
I've done this in the past and eventually ended up with just bouncing off the count of errors in the result to determine success or failure so I didn't have to inform the class itself.
What's the benefit?
@MilanJovanovicTech I didn't have to inform the result object of success state via constructor. It self determined the state. So, I just collected all errors, put then in a list even if just one and used the existence of one to figure out success or fail.
Smaller signature, self determined, snd success is based off of count internal instead of external determination.
Great video thanks. However, i have mixed feelings on implicit conversions of return types. In this case it's pretty straight forward and should be easy for new devs to figure out... but i've seen implicit conversions that invoke database calls and proxy objects to the point you never know what is going on.
Who does database calls in implicit conversions? Hot damn
I downloaded the solution and I have a question, not related to the video itself. I can see that you use to make constructors private and static Create methods for Entity classes. Like Follower for example. I understand the problem of constructors for Entities:
1). What if some state field becomes optional?
2). What if you have to 1,2 or more new fields?
3). What if some state field will need validation?
4). What if you have 2 parameters of the same type coming one by another. Like firstName, lastName. You can accidentally mess them, compiler won't argue.
All this problems can be solved by using fluent builder.
I would understand if you had some interesting method name, describing what exactly is going on when you create an object via this method. Lets call this approach - Named Constructors.
But what exactly are you trying to achieve when you move object init logic from constructor to static method with simple name Create?
I can place side effects in the factory method, like raising domain events. Doing that in the constructor could lead to strange behavior because of ORMs and Serializers using the constructors.
Also, it allows me to "fail" creation gracefully with a Result object. Whereas with a constructor I'd have to throw an exception.
Great video, on the Result class you should have used the IsFailure on line 13 but other than that, it's perfect.
Also what would you do if you have an array of objects you lets say need to import through an API and every import result can fail.
I'm doing the import asynchronously on a list and I think I don't want to stop on a failure of a single object import, but do want to keep importing everything, collecting the errors/successes/results.
That's a nice example. I'd rework the Result to support multiple errors and collect all of them together.
love it. great.
Thanks!
Why didn't you show example with data result return on success?
(next video)
it's perfect!!! thx
No problem!
Great 👍🏻
Appreciate it!
Thanks Milan
Please create a complete course for DDD and also vertical slice architecture ❤❤
I did: www.milanjovanovic.tech/pragmatic-clean-architecture
Doesn't include pure VSA, but you'll see many of the same concepts inside.
Can docker and kubernetes produce videos? ubuntu, hee-hee
What? 🤔
@@MilanJovanovicTech I deploy docker to a linux server, do you know how to package the local system and upload the image file?
@@MilanJovanovicTech Recommend the learning video of docker, thank you very much
Insteresting 🤔
Also related to a recent video
👋👋