A different way to return data in C# with OneOf

Поделиться
HTML-код
  • Опубликовано: 28 сен 2024
  • Become a Patreon and get source code access: / nickchapsas
    Check out my courses: dometrain.com
    Hello everybody I'm Nick and in this video I wanna talk about a package called OneOf. This library provides F# style discriminated unions for C#, using a custom type OneOf. An instance of this type holds a single value, which is one of the types in its generic argument list. It might not be my personal favourite way of doing things but I think it is a good project and I want to give it some exposure because I can definitely see some people benefiting from it.
    Give OneOf a star: github.com/mci...
    Don't forget to comment, like and subscribe :)
    Social Media:
    Follow me on GitHub: bit.ly/ChapsasG...
    Follow me on Twitter: bit.ly/ChapsasT...
    Connect on LinkedIn: bit.ly/ChapsasL...
    #dotnet #csharp #oneof

Комментарии • 109

  • @jackkendall6420
    @jackkendall6420 3 года назад +6

    I love the trend of C# slowly becoming more and more of a functional language :)

    • @vasilybulochkin4622
      @vasilybulochkin4622 3 года назад

      It would be really cool if C# will become Object first just like F# is function first)

  • @MartinvanZ
    @MartinvanZ 3 года назад +1

    Interesting. I Just implemented this pattern in my mobile app. For my synchronization I return a result object. I Used an enumeration as the result status and extra fields to return optional objects. I Like it better than throwing an exception. In my case I use a case statement to handle the result.
    This approach is nice because you can clearly see the results possible per function. In my case I would have to create different enumerations per function.
    The only downside I can think of is that when you would have many possible results it may clutter the function definition. More of a readability issue.

  • @CarmenSantiNova
    @CarmenSantiNova 3 года назад +16

    It's an interesting idea but I have to say that exceptions has one BIG benefit and that is that it can bubble through code, you don't have to specify what bubbles.
    This gives you the opportunity to have any layer of your application return an error. Normally in a service-oriented architecture you would have multiple layers where a problem can occur.
    If you were to be explicit with every error that can occur down below you would end up with a very large combined list of results at the top. I am of course not saying that your shouldn't catch and handle exceptions but rather let them bubble but in our case we have introduced what we call a ValidationException which is used to transport validation errors from different services in a normalized manner.
    Although I really like the explicitness of states it takes me back to the Java world where you would define which exceptions a method could throw.

  • @mrshk_vv
    @mrshk_vv 3 года назад +2

    Nice guide. I wasn’t know it and I hope will be implement that feature in my next projects. Hello from russian viewers)

  • @8ytan
    @8ytan 3 года назад

    OneOf seems like it would become very impractical for APIs which are capable of returning a larger variety of responses to a single request. It looks neat and tidy when there's three possibilities, but what about when there's 10? In my opinion, Result also suffers from this to some extent.

  • @justinharris6197
    @justinharris6197 2 года назад +1

    I would have went with OneOf as a type of Result that allows you to return something other than an exception, though I assume this was just to demonstrate that you can return more than 2 values.

  • @lahcencodery
    @lahcencodery 4 года назад +2

    I really this way of handling results! I am going to give it a try

  • @alfredoquintanalopez2265
    @alfredoquintanalopez2265 4 года назад +1

    Great Video Nick!!! What about the performance of OneOf, no the exception path, but the normal path? In the Exception path, performance should be better, because the exception really never throws. Thanks, your videos are amazings!!!

    • @nickchapsas
      @nickchapsas  4 года назад +3

      OneOf performance wise is better than all three of the approaches I showcased. The first one is obviously the slowest due to the exception throwing and the middleware invocation. The OneOf is only the fastest, over the Result one because OneOf is using structs which will allocate less and faster memory compared to Result's exception objects.

    • @mycollegeshirt
      @mycollegeshirt 3 года назад

      @@nickchapsas that's why I thought I wanted to use these with my structs, so I can still have polymorphism without losing speed by using interfaces or .

  • @mojizze
    @mojizze 3 года назад +1

    This approach doesn’t look bad at all.

  • @sunnypatel1045
    @sunnypatel1045 4 года назад +2

    Hi nick love your vids ! Could you do a vid on automapper custom value resolvers please

  • @donka86
    @donka86 2 года назад

    I have two minds about c# implementing more and more functional patterns not just as packages but at the language level too. For example I love scala as a multi paradigm language, but if you work in a team with mixed dev background (f / oo), people always tend to fallback to what they know best and enforcing practices becomes hard. One could argue, you have C# and F# and they are both interoperable on the CLR. Why not modularize your app and use the right language for the right part of the app.

  • @whattheduck7125
    @whattheduck7125 4 года назад

    So many years, still learning

  • @hanspetervollhorst1
    @hanspetervollhorst1 Год назад

    You really got me curious on this and I started using it. Now, I wonder, when combining it with MediatR ( IPipelineBehaviour ), is there a way to know in the Handler-method, that our TResult is of type OneOf, so we instead of throwing Exceptions from there, we can ie return a Microsoft.AspNetCore.Mvc.ProblemDetails ?

  • @JustinBaur136
    @JustinBaur136 4 года назад

    This is super cool, I think I sort of like the exception way more though. I use it with custom exceptions so that I can escape at any level and have a handled structured response. I also don't need to wrap everything in a try/catch because I can have non custom exceptions pass back a 500 and let me know what the exception was or just hide it depending on the environment.

    • @nickchapsas
      @nickchapsas  4 года назад +2

      The main problem with the exception one is that it is adding a lot of "magic" and it lets you assume quite a lot about the behaviour. It also introduces a 10-30 ms which would be an absolute no-no if you're interested in writing high performing applications.

    • @JustinBaur136
      @JustinBaur136 4 года назад

      @@nickchapsas Yeah absolutely. I've normally had to write API's that are highly traceable at the cost of a few ms. So I normally have some middleware to track request information so I might as well have that middleware catch exceptions for me. I can't agree more on middleware feeling like magic though. I wish there was a more structured way to build it.

  • @DanteDeRuwe
    @DanteDeRuwe 4 года назад +2

    Something in me says that this could have been done differently. Some kind of combination with Option should exist where you can get the value, check if it's null, or get any other "Error" struct by implementing a base class, so that you would not have to put OneOf.
    Something like OneOf< Option, UserErrorBase> could work, but that's not elegant.
    You have any idea if this has been done before, or if either LanguageExt or OneOf supports this? Also, is this a good idea or does it have some obvious flaws?

    • @sombraSoft
      @sombraSoft 4 года назад

      You could use a base class. But then you'd lose the point of actually seeing the name of the different types it returns.
      For example if a given API returns OneOf and 25 different classes derive from UserErrorBase, how would you know which cases do handle? You'd certainly not try to pattern match all possibilities.

  • @sheveksmath702
    @sheveksmath702 3 года назад

    I love discriminated unions in F#, but definitely agree that this is pretty verbose in C#. I wonder if you could use C# 8 switch expressions & enums to get something sort of close to this? Obviously it wouldn't be the same, but might be fine for a lot of cases.

  • @Stanniemania
    @Stanniemania 3 года назад +1

    What if you were to swap the user exists and email invalid type parameters in the future? It looks like the caller will not break if you do that so it will start returning the wrong ActionResults and nobody will know (unit tests should catch it but still).

  • @eddyhanderson6916
    @eddyhanderson6916 2 года назад

    I would like to know how throwing exceptions can bring perfomance issue.

  • @nikbrons
    @nikbrons 3 года назад

    For Web Application, the best practice for (email) validation is to use fluentvalidation (you can take out simple validation from domain services).
    It seems to me that the OneOf approach is not justified enough if you only need to return or . IMHO, no need to return more different exceptions in one place to handle them (using OneOf or not)
    How often have you encountered handling various exceptions (greater than 2) on your controller?

    • @nickchapsas
      @nickchapsas  3 года назад +1

      Firstly there is no single best practice for such topic in my opinion. There are layers and you get to choose where to do what. OneOf doesn’t focus on exceptions but rather alternative paths. They can be both parts and bad Paths. I actually have at least 3 different types of responses in most of my controllers and oneof is a lifesaver there

  • @cristerramirez8522
    @cristerramirez8522 4 года назад +1

    Nice tutorial!

  • @IMarvinTPA
    @IMarvinTPA 2 года назад

    I don't understand where the names "user", "email", and "exists" came from at ruclips.net/video/r7QUivYMS3Q/видео.html These seem to have come from nowhere and have no types. My only working theory is that the names are the last camel-cased word in the type, lower-cased from the OneOf declaration. How does it know how to match these generic words?

    • @IMarvinTPA
      @IMarvinTPA 2 года назад

      Match(...) is order dependent. The first function always matches with the first Type, second function matches the second type, etc. I am not fond of order dependent things once it passes about 3. Type of the variable names being created is implied by the position in the Match method. (I want a Match method where you explicitly type the main variable and can put the functions in any order because of it.)

  • @MrSuneF
    @MrSuneF 3 года назад +3

    I would advice against this approach, for a few reasons. One is it appears to create more complex code. What you want it is focus on the main success scenario and not worry about about all the alternative flows with failures. Here the problem is it forces the caller to handle not just 1 result but multiple results. Imagine if you have a function that calls a function that calls a function etc.... With exceptions you can throw an error and handle it in the exception filter. Here you would have to handle it at every level as alternative scenarios. Usually you want logging when there is an error. With an exception filter you can put the logging in a single place, here you would have to put it into all actions in all controllers. Another reason I don't like this approach is that it sort of mixes errors with successes in an unclear way. Everybody knows an exception is unexpected, it's something we developers see as a red flag, something that needs investigation. Here it sort of legitimizes errors by elevating them to objects/structs on equal footing with successful responses, I think that is just a really bad idea. Just my 0.02.

  • @alexgjo3132
    @alexgjo3132 3 года назад

    Throwing exceptions when validating a request is bad, but I also don't like this approach with OneOf either. A combination of custom result object and Fluent Validation is what works best for me.

  • @AndersBaumann
    @AndersBaumann Год назад

    This is a misuse of exceptions. Exceptions should not be used for validation. No invariants have been broken. Use the Result class instead. Exceptions should only be thrown in the domain model when invariants are about to be broken.

  • @VennYT
    @VennYT 3 года назад

    So cool!

  • @IvanAndreev91
    @IvanAndreev91 3 года назад +1

    Why don't we use some IoC provider or other DI tools in this case? Is "OneOf" really better?

    • @nickchapsas
      @nickchapsas  3 года назад

      What does IOC and DI have to do with what’s discussed in the video?

  • @davidopdebeeck8953
    @davidopdebeeck8953 3 года назад +1

    it’s neat, might use it but it’s still very painful compared to doing the same in F# code. thanks for the video, great stuff as always.

  • @TheEvilVir
    @TheEvilVir 3 года назад

    I don't like it. First of all throwing an exception properly breaks the ASP.NET's execution pipeline. Middleware, that is set to run after controller, has obvious and generic way of choosing if to skip or use different execution path, in case of an exception. When you return a object just with different value, plenty of middleware out there will execute happy path even if controller failed.
    Likewise your API will now have two concepts of errors: yours OneOf and standard exceptions thrown by middleware.
    Also it makes code less readable.

  • @jfevia
    @jfevia 3 года назад

    I fail to see how this is a fair comparison. Creating new structs and using a custom implementation of pattern matching isn't really that much different from having a base type and returning a concrete implementation, then using built-in pattern matching features with minimal performance hit. Also, while it's true that this enables the use of more strongly typed code, someone could end up with too many types returned (although that would be a code smell).
    Overall this feels unnecessary...

  • @RM-14js
    @RM-14js 4 года назад

    do you recommend using Rider in Windows 10? or should i stay with visual studio 2019?

    • @nickchapsas
      @nickchapsas  4 года назад +1

      I absolutely love Rider but it might be for personal reasons. I also write Java and Kotlin so I also use Intellij which means I can share my settings between the two and I also use a Mac which means I get the exact same experience across all of them. I also prefer Rider's performance and intellisense over VS.

  •  4 года назад +1

    From a clean code perspectie is not a good practice to have more than 3 generic types in any class, so ... this won't cut it for me in a real life scenario. It 's nice but not useful when you have 10 different responses in a service (400,422,403,409, etc...) you would end with OneOf.... .this is not practical.

    • @nickchapsas
      @nickchapsas  4 года назад +1

      You can use the OneOfBase class to implement and create classes that contain and hide the number of generics.

  • @filipecotrimmelo7714
    @filipecotrimmelo7714 4 года назад

    Nice! =)

  • @endermanfromthemoon
    @endermanfromthemoon 2 года назад

    I don't like it because on the other side (the side of consumer) it's going to be a pain to implement all these different response types from a single endpoint

  • @whoiam6395
    @whoiam6395 4 года назад

    I always putting like by default, even not going to watch video for any reason

  • @xMrMiagix
    @xMrMiagix 3 года назад +13

    Alright. Let's do some refactoring of APIs today :D

  • @sombraSoft
    @sombraSoft 4 года назад +27

    This is absolutely genius. In this example it's way better than XML doc for "throws exception"
    It enforces you to aknowledge what kind of returns you can have and handle them accordingly.
    This pattern matching thing reminds me so much of Elixir (but this is even better, it's STRONGLY TYPED). Also, the names you're giving to these "exceptions" like InvalidEmail are totally like atoms in elixir or symbols in ruby.
    GOOD JOB!

  • @zc2012
    @zc2012 4 года назад +15

    the OneOf approach looks a good as it clearly shows what the client can expect from a service, but also increase the complexity of the client code. I personally like the Result approach.

    • @nickchapsas
      @nickchapsas  4 года назад +8

      The problem with the Result approach in my opinion is that when you start having more Exceptions that you need to handle in there then you need to edit that Failed clause which means that your code is violating the open-close principle. It's also using a beefy Exception object which allocates more memory.

    • @tdp4276
      @tdp4276 4 года назад +6

      @@nickchapsas Agreed on violation of the open-close principle, that's annoying. But to get around the use of "heavy" exception objects, just don't use them, make your own objects to encapsulate in Result and interpret those instead.

    • @lextr3110
      @lextr3110 3 года назад +1

      one problem with Wrappers is the constant need to Wrap and Unwrap.. it obscurate the type interfaces

    • @SebGruch
      @SebGruch 3 года назад +1

      @@nickchapsas Therefore, for my project I'd created dedicated ProcessingResult, not hauling Exception, but only ErrorCode enum and ErrorMessage. But I have a very strict situation, where I have only 3 error codes (actually these are like BadData, Ignore, TryLater, with the message providing more details on the error - either validation error or caught exception message) and do not expect it to expand.

  • @mariuszskuza2425
    @mariuszskuza2425 3 года назад +1

    I prefer to create a basic class and then another extended class that inherit the basic one, for example: Response (base) and Response.Success (data) or Response.Error (status code, message) and then the service returns Response type of any kind.

  • @felipesuarez6564
    @felipesuarez6564 4 года назад +2

    You said that the OneOf approach to handle errors was nice but not your cup of tea. What is then your preferred alternative to handle business validations/errors in C# ?

    • @nickchapsas
      @nickchapsas  4 года назад +7

      I am actually planning to make a video for this one so I won't spoil it just yet :D

    • @felipesuarez6564
      @felipesuarez6564 4 года назад

      @@nickchapsas looking forward to it!

    • @joelhutters4025
      @joelhutters4025 4 года назад

      Nick Chapsas Your videos are great, and I am really looking forward to the one with your prefered choice.

    • @joelhutters4025
      @joelhutters4025 3 года назад

      Waiting for this cliff hanger. Would you mind to share your preferred approach beforehand? Link reference etc?

  • @dastrn
    @dastrn 3 года назад +2

    This is really good stuff.

  • @jyrikgauldurson8169
    @jyrikgauldurson8169 3 года назад +1

    I have used a similar implementation of the Either monad for these cases coming from a functional background. I usually combine errors into one so I have two types, the correct one, and the error route. In the theory, this is known as a disjoint union or coproduct.

  • @carlosjunior5371
    @carlosjunior5371 3 года назад +2

    This approach makes the code much more expressive. Although I prefer to use domain notifications. Thanks for sharing!

    • @adriangodoy4610
      @adriangodoy4610 3 года назад

      why it makes it much more expensive?

    • @Alguem387
      @Alguem387 2 года назад

      does it tho? its just wrapping it on a struct

  • @TheJessejunior
    @TheJessejunior 3 года назад +1

    maaann!! thema subjects your´re abording are always so helpfoul! thanks a lot

  • @ernstgreiner5927
    @ernstgreiner5927 4 года назад +2

    Thank you for the tutorial!
    AFAIK there are plans to support 'Discriminated Unions' in future versions of C#, so there should be more IntelliSense support too.
    I'm learning F# since a couple of weeks and what i can say is that one way of F# is handling this is like:
    Result
    where TResult is the happy path where you expect one result and TError the error path where DU are used.
    It's a very descriptive way of handling processes, i like this very much!
    But anyway, thumbs up to OneOf

    • @nickchapsas
      @nickchapsas  4 года назад

      I cover the Result approach in this video. It's the second example I give

    • @ernstgreiner5927
      @ernstgreiner5927 4 года назад

      @Nick Chapsas
      what i'm talking about is, the signature of the method in the third example
      public OneOf CreateUser(User user)
      will change to
      public Result CreateUser(UnvalidatedUser user)

    • @nickchapsas
      @nickchapsas  4 года назад

      @@ernstgreiner5927 Oh I see. Well I don't really see the reason for this one. I will introduce unnecessary nesting and you can't really extract the "Fault" part without repeating yourself since two responses are not the same.

    • @ernstgreiner5927
      @ernstgreiner5927 4 года назад

      This is a functional thing, from an OOP pov you wouldn't get such ideas.
      In F# it's used for process chaining like
      let completedOrder = unvalidatedOrder |> validateOrder |> assembleOrder |> shipOrder |> chargeOrder |> closeOrder
      every step returns on success an distinct type, ValidatedOrder, AssembledOrder, ShippedOrder...
      In C# there will be one OrderObject with an status enum, in F# 10 types...

  • @LuigiTrabacchin
    @LuigiTrabacchin 2 года назад

    Maybe with code generators it will be possible to change those isTn in something more fluent...

  • @benjamincharlton3774
    @benjamincharlton3774 3 года назад +1

    Really interesting video, thank you! So, if throwing exceptions from the service layer to the controllers is not a good way to communicate common validation problems, and using OneOf is also not your cup of tea, what is your favourite way to do it?

    • @nickchapsas
      @nickchapsas  3 года назад +2

      As of late I’ve been actually using OneOf quite a bit more but I am bouncing back and forth between OneOf and Result from Language.Ext

    • @benjamincharlton3774
      @benjamincharlton3774 3 года назад

      @@nickchapsas Nick, sorry to bother you again. I am trying to put together what you have taught me about OneOf, CQRS and EventSourcing. Do you return OneOf from a write-side CommandHandler such as: public class CancelBookingCommandHandler : IRequestHandler

  • @Spartan322
    @Spartan322 3 года назад

    Yeah, I've made my own Variant classes before, its convenient when you can't or don't want to manage the individual types and know you're always going to return just one thing.

  • @rolandtennapel5058
    @rolandtennapel5058 3 года назад

    Stop making sense!!! 🤣 No man totally agree; Exceptions should be thrown as an exception, not as if they were some elaborate 'switch'-board. It's lazy programming and this coming from a hobbyist C# dabbler 😝

  • @atrumamantus
    @atrumamantus 3 года назад

    Interesting, but the added complexity from essentially multiple return types with type based branching doesn't quite seem like a good trade off to me. Also, I know it's just an example, but this code is doing a lot of things: validations, duplicate entry checks, entity creation. Honestly I'd look more at breaking this up into different layers that handle their different logical concerns separately.

  • @IvanBerezhnyk
    @IvanBerezhnyk 2 года назад

    Hi Nick, thanks for the video. I wonder what in this case would be your cup of tea ? :) How to separate API and Application layers properly?

  • @mishamovdivar
    @mishamovdivar 4 года назад

    The only thing that concerns me is nesting. What if you have to call another method (which also returns AnyOf) within the one of the Match lambda, and call another method within that method, etc. It'll make a code really hard to read.

  • @lextr3110
    @lextr3110 3 года назад

    you can also return a tuple of (T, exception) and pattern match on it.. or object inherithance is actually a discriminated union.. pattern matching on T is the same... its only the simple way to create these discriminated union in some languages that give them these advantage
    they can very easily become class hiearchy at any time from the discriminated union..

  • @evidgamer1699
    @evidgamer1699 2 года назад

    This pattern is natively built into Rust. Some of the concepts of Rust are very advanced and surely should be adopted in C# also.

  • @konrad8015
    @konrad8015 3 года назад

    You have mentioned it's not your personal favourite way of error handling. What is your favourite then and why?

  • @teseract7442
    @teseract7442 4 года назад

    how to create badRequestt for a list of badRequest, more fields?

  • @seancpp
    @seancpp 3 года назад

    I really like this. Thanks for showing it

  • @viktorasmickunas2527
    @viktorasmickunas2527 3 года назад

    Very interesing, thanks

  • @alirezanet
    @alirezanet 4 года назад

    Cool library

  • @WarrenLeggatt
    @WarrenLeggatt 4 года назад

    The logical conclusion of this is map/bind or Select/SelectMany in Linq. Linq is similar to F# and Haskel "do" notation, you just need to provide the bindings. I have a library of monadic computation I use in C#. My Either object is like Result in your middle example and Result when reduced to a discriminated union is actually OneOf when you think about it.
    With Select/SelectMany bound to the valid state you could then write code like
    public OneOf CreateUser(User user)
    => from _ in IsValidEmail(user.Email)
    from _ in IsExistingEmail(user.Email)
    from addedUser in _users.AddUser(user.Id, user)
    select addedUser;
    And this hides all the "if" noise and leave just what is happening, if the validation steps fail it short circuits and returns the fail state otherwise you get the user in the valid state :)
    Linq is not just for IEnumerable when you push more functional in your C#
    :)

    • @nickchapsas
      @nickchapsas  4 года назад

      I personally don't that approach for two reasons. Firstly you introduce unnecessary nesting which complicates the syntax and I also personally hate the "sql like" flavor of Linq. The second one comes down to purely personal taste but the first one is also a complete dealbreaker for me.

    • @WarrenLeggatt
      @WarrenLeggatt 4 года назад

      @@nickchapsas fully understand. I got used to it via f# do notation and got to like it. Does mean new people in need to understand more concepts

    • @WarrenLeggatt
      @WarrenLeggatt 4 года назад

      @@nickchapsas with the sql lite link i only fall back to it for monadic bind or if the linq stack needs to capture variables from higher up

  • @andriizahr448
    @andriizahr448 3 года назад

    But what about the Single Responsibility Principle? One method is able to return one of 3 different types instead of 1. It is not a good idea

    • @nickchapsas
      @nickchapsas  3 года назад +1

      Single responsibility said that the method should DO one thing. Not return one thing. For example if you returned a single object that based on what is null in the object you take different decision then do you honor single responsibility? It's the same thing and SR isn't being violated by the usage of oneof.

    • @andriizahr448
      @andriizahr448 3 года назад

      @@nickchapsas thanks for detailed response

  • @Bupyc2007
    @Bupyc2007 4 года назад

    What theme do you use in Rider?

    • @nickchapsas
      @nickchapsas  4 года назад +1

      I did this a long time ago but I think i recreated the visual studio dark one

  • @janoslaszlo3272
    @janoslaszlo3272 3 года назад

    Out of these 3 ways you presented, which one do you use?

  • @aderitocruz6054
    @aderitocruz6054 3 года назад

    Could I compare it with Tuple? For what I see its almost the same except in the return that u have to code some condition make it equal

    • @nickchapsas
      @nickchapsas  3 года назад +1

      You cannot because in a tuple you can discard parameters. You don't HAVE to deal with all the tuple values.

    • @aderitocruz6054
      @aderitocruz6054 3 года назад

      @@nickchapsas yes indeed

  • @olayemiafolabi2151
    @olayemiafolabi2151 4 года назад +2

    Thanks for the tutorial. Would like to see your approach to achieve returning error message without throwing exceptions. Also, looking forward to a short explanation on when to use struct vs class

  • @torbjrnholtmon7195
    @torbjrnholtmon7195 4 года назад

    Thank you so much for the tutorial!
    Me and and my colleges discuss this topic very often. We never land on a definitive answer on how we should handle it.
    We often go the Result route, but it has its problems, and sometimes it can get tricky to onboard and convince other developers to use it.
    I dream of one day to have an "definitive" answer on how to tackle this topic.

    • @nickchapsas
      @nickchapsas  4 года назад +3

      The beauty or problem with programming (depending on how you see it) is that for some things you can have your own flavour because there is no definitive answer. There are requirements (functional or non functional) that might force your hand to make some decisions but you will always have your own opinion and your own way of doing things. I like to describe "Definitive" in programming as the thing that the least amount engineers complain about.

  • @anju313
    @anju313 4 года назад

    This would increase complexity on the client side as they might also need to use OneOf to match multiple responses from a single endpoint.

    • @nickchapsas
      @nickchapsas  4 года назад +1

      The client side doesn’t need to know anything about how the server side is dealing with the requests internally. You just have your regular expected contracts. There are multiple public APIs that do the same thing behind the scenes but the consumers know nothing about it