If we still rely on exceptions in the domain layer (at the point of constructing domain entities fx), then we have to manage the code duplication for every set of guards for the valid construction of each domain entity. If not, then for every use case/handler that directly or indirectly involves the creation of domain entity A, you would have to remember to guard against the things that might throw in the constructor of that entity. So, following this logic, it might be beneficial to rely on Result<> return types in the Domain layer, too. Domain entities with private constructors and public static Create methods that return Result<>, instead of public constructors that throw exceptions. But if we go Result pattern all the way, the codebase will become full of if statements and checks for the possible outcomes of every operation (is it Error or not). This in turn calls for implementing a small API for Result objects, which would make the whole use case logic look like a pipeline for Result returning operations. We would need a set of methods, that would allow us to seamlessly chain Result returning operations in a railway fashion. Methods like Map, Bind, Tap in the context of Result objects (inspired by functional programming). This is why Result pattern is often met with hostility by developers. I think it's great, but you need the whole team to buy into that pattern.
My rule of thumb: If you can expect that an error might happen but the code can recover and still run properly? Return Error. You have no idea what just happened and there is no way your code can run correctly in this state? Throw.
Should the cache be registered as a singleton rather than scoped? My concern is that using a scoped lifetime might result in the cache being recreated for each new logical operation within the application, which seems inefficient
I loved your explanation, there were so many RU-vid videos saying don't throw exception do this instead, but now I fully get it how to have the best of both worlds. Thanks a lot Gui!
I am using a slightly different approach. I use exceptions in the domain layer as guard clauses, similar to what you showed. But I can also use Result Pattern in Domain Entities methods, for example: Shipment.Deliver(), Shipment.Cancel(), in those cases when my application code analyses the results, instead of handling exceptions to analyse the results. In other words, I use result pattern when I need to analyse the result of execution in the code. If I need to fail fast (guard clauses) I can throw an exception. For validation I use Result Pattern the same as you.
Would you also consider putting the validation in an endpoint filter (in the minimal api case) so it would be validated even before entering the API method handler?
Sure, even a built-in filter/pattern matcher returning a 404 is still "returning an error". Otherwise, write some middleware to handle it. Middleware is a hell of a drug; if you do it right, you can *significantly* reduce/simplify your code, just by removing the edge cases & validation from the core logic.
Hi gui, I recently started my career as a dotnet engineer. My questions is regarding courses, Are they relevant to the real softwares we built? Are they helpful in useful within the company as a developer or a tester? Please don't mind answering.
I like the 'archive' folder idea. I'm mostly fairly happy with the layout of my notes (I use Notion for everything except client work, and Obsidian for client work (so those aren't on Notion's cloud)) - but as you say, quite often I have notes that I no longer need, but don't want to delete. Wil start introducing an archive folder. Thanks :)
Thank you Gui. I watched your excellent presentation on Tech Excellence and was watching your video out of interest. I’m a frontend developer and after watching this it made me realise I could use this pattern to build dynamic DOM trees in my test suite without having to create any HTML files. After finding that working, I realised I could also use it to build a custom TypeScript module configuration per test. This has helped me so much, and is having a massive impact on my test code right now so thank you so much for this. 👍 I had been looking for a way to do this for a while and your video provided the answer with a really simple but powerful pattern.
@@N1mdae yeah, I see parallel between hoarding and taking notes in a sense. And I clarely ain't hoarder. When someone re-visits their notes good for them but I never did
@@vmachacek Exactly, the most I do this days is to take some meeting notes or outlines to clarify some process in logseq or Tana, but ony for the ones I know I need to share or revisit beforehand.
You don't need to remove and re-add dependencies in ConfigureTestServices method. set environment variables instead that will override the appsettings.json configuration. this way you will have a new connection string to RabbitMQ applied automatically.
Would like to see Microsoft have a better solution by calculating UTC time based on local time and Country/State. Many times you only know local time of the event, and then maby country / country+state, example tracking events from carriers. And in DB you might need to store localtime and UTC ( which is unknown). Think NodaTime has some support, but it is still not great, it need to be combined with more packages to have a good solution for the problem. example: event happend "2024-07-14 15:30:45" in Poland. Would like to translate this to correct UTC time
Hey, im using a similar pattern in my current project but I'm having problems using that endpoint in unit tests because the HandleAsync method needs to have all the dependencies which would normally be in the constructor. Do you have any advice for this?
Why is a mock of an interface and using Verify worse than creating a fake and checking that? They kinda of seem the same to me, and that the fake class is just more code to possible maintain. Btw, I like these short bitesized videos. I've learned a lot from them.
After many years of accumulating notes and data, I came to the conclusion that I needed to reorganize everything depending on the current context, whether because I had a new responsibility or even changed department or company. Therefore, I thought that the root of the organization should always start with the chronology, for example the year and month (202407). So for each chronological workspace I would then place my P.A.R.P. - Projects, Activities, Resources, People. Then, within each of these folders I always have a Previous and Next link, which allows me to navigate the timeline when there is a match for the same organization in the previous/next period. Thanks for your session ideas.
I started gradually organizing my work notes using para and enjoy it a lot, I'm planning to start organizing my personal digital assets similarly. One minor thing I do differently, I add numerical prefix to ensure archive is at the bottom (1 - Projects, 2 - Areas, 3 - Resources, 4 - Archive). I like seing projects as the top item that I should focus on short term :)
You use a factory pattern to create/get an instance of an object encapsulating details on how it is created. Usually the caller only has to know about the abstraction, e.g., an interface and it calls the factory to create the instance. Multiple implementations might coexist and the factory is responsible for getting the right one. Builder is different. It's more of a sintaxe sugar, a way to build complex objects by composing steps. In this case, the caller has to know how the object is created
Very nice approach receiving an action to build nested objects. I'll start using it :) For the required properties scenario, if you're using the builders for test cases, an alternative would be to have all required properties set to default values on your builder's constructor, so it's not possible to build an empty object for example. You won't have all the control you have with your steps approach but it's easier to maintain. I would also suggest checking the AutoBogus library to initialize your builders with generated data.
It's also worth pointing out that the builder pattern isn't always the best solution, especially when the object has multiple required fields, because then you're moving compile-time errors to run-time errors.
thanks for the tutorial, very useful. One note on pronunciation, genre is more often pronounced zjohn-rah, rather than "jen-reh" which sounds a lot like you are saying "gender".
Good demo, but why oh why did you use the builder pattern for a dto type object? I feel if you had a proper scenario the demonstration may have carried a bit more heft - what you have is an overengineered construction of an object that can/should be initialised with properties.
We currently have a DTO that is created in your suggested way, all IDE's and Linting tools go on a fit because the creation of that DTO goes over the cyclomatic complexity threshold we use (which is the default threshold IF you enable it). Why? Because the DTO is for a detail page that also contains nested collection that may or may not be empty, and the source we need to map it from can be null, so there is a lot of (list?. Where(x => x != null || x.blabla).Select(...) ?? Enumerable.Empty<Thing>()). The builder pattern is great for splitting up that logic in a way that's more readable and maintainable, especially when your DTO's are immutable value objects.