Name Collissions on DTOs

So I know you’re supposed to create unique request and response DTO names. But two questions:

  1. Is there ANY WAY to get around this requirement? I’m incorporating service contracts from various teams and I can’t guarantee they’ll all be unique. Modular Monolith deployment, so we bring them all together to have a unified API.

  2. It’s getting complicated in a DDD scenario with bounded contexts. The concept of a “User” is different from one context to the other, and telling each team to prefix the name with User is cumbersome

  3. The same is happening with base classes. We have common functionality across DTOs in a given domain and often use a RequestBase base class. But one set of service models RequestBase is different from another’s. The issue is then when the types files are generated in C#, for example, it uses public partial classes, and the classes get combined, so even when the resulting Request DTO name is different, because of the base class, we have a conflict. The same is going to happen with enums and other types. The cascading effect is too great to have to prefix everything.

Looking for guidance on how to manage all this. Ultimately I could, for the most part, deploy them as separate microservices, each with their own OpenAPI definition and separate servicestack instance, etc, etc. I really don’t want to have to do that. I want a single unified experience for our APIs.

Anything I can do? Anything like including namespaces, or auto-prefixing on import, or disabling the auto-generated endpoint (/api/Get…) in favor of only explicit endpoints (which would have to follow through in /ui/… as well)?

You can’t use .NET namespaces in Add ServiceStack Reference generated DTOs, it’s only possible in .NET languages where you can share the ServiceModel DTOs .dll’s and make use of their .NET namespaces.

Ultimately I could, for the most part, deploy them as separate microservices, each with their own OpenAPI definition and separate servicestack instance

This is one solution, where you have different Apps so that you have different Apps for each bounded context so there’s no collision between their different types. This sounds like the cleanest solution for your use-case since your issue is from trying to merge multiple bounded contexts together.

Otherwise to maintain a single AppHost you would need to use a prefix to differentiate the different types, i.e. if you have multiple “User” types, specify what type of User each DTO is that makes it different, typically by prefixing it with the name of the different context it belongs to.

So let’s say I have the actual DLLs and I’m bringing them all together in a single modular monolith app host. All assemblies have their own namespaces and DTOs; but some of the DTOs and supporting characters (enums, base classes, etc) do have overlapping names. Is there any way I can deploy this as a single app host or am I completely hosed?

All Request DTOs still need to be unique, but you should be able to host multiple .dll’s hosted in a single AppHost where the API endpoints will be available to call, but a lot of features like Add ServiceStack Reference and built-in UIs that rely on it wont work if they have type name conflicts.

Any thoughts for the future by allowing some of the auto-features have a discriminator to let them work? Like using full namespace in the auto-APIs (/api/This.Is.My.DTO vs /api/This.Is.My.Other.DTO) ? Or allowing the admin /ui use the [route] attributes instead (e.g. /ui/my/custom/route)? Similarly, the code generator that uses partial classes, recognizing it’s a different class from a different namespace, auto-append a uniqueness identifier (MyBaseClass1 vs MyBaseClass2, or prepending namespaces w/out dots like ThisIsMyDtoBaseCass)? Just looking at non-breaking ideas to allow for same-named DTOs. I totally get the reason behind it – because there’s so much magic going on – but having some configuration to allow for a less wrist-friendly version of the auto-generated code in order to allow “conflict” (that is, DTOs with the same name) would go a long way in supporting larger projects with multiple teams. This is currently a significant blocker for us and I feel it’s a very hacky workaround to have to prepend the domain to all my DTOs, enums, base classes, etc, etc.

As always, I appreciate your fast responses and your consideration. Thanks

1 Like

It’s fundamental concept in ServiceStack that Request DTOs names are unique so that any API on any endpoint with any support serialization format can be invoked with just the type name and payload.

External service model contracts will also never be reliant on .NET namespaces, irregardless of many languages not having .NET’s exact namespace language feature, which would require hacks to shoehorn them into whatever type grouping construct they do support if they can be generated in a single file. But it would make them extremely fragile for changes to .NET namespaces to break serialization at runtime, internal namespace concerns should never leak beyond the service boundary.

Add a prefix to make the type names unique, make it clear to API consumers what makes the types different.

I get that. But I’d go farther and say it’s fundamental to how ServiceStack exposes it’s APIs, and not necessarily how they’re built. If I built a standard route-based API and exposed it through OpenAPI, there are tons of libraries that would auto-generate a client for me. It likely does some auto-naming or conventions-based namings of the classes it generates. If it has a conflict, during generation it appends a 1 or 2 or 3… That’s fragile, I get it. Next time you generate, what was suffix 1 might now be suffix 2 given the addition of some class.

What about this… instead of requiring unique names during my DTO/service vreation, can I do that through configuration?

ServiceModels.InAssembly(…).AddPrefix(“…”); That way my developers are free to develop within their domain, and it’s on me, the one bringing all these separate domains together, to add configuration that guarantees uniqueness?

InAssembly(typeof(Assembly1.MyThing).Assembly).AddPrefix(“Assembly1”)
InAssembly(typeof(Assembly2.MyThing).Assembly).AddPrefix(“Assembly2”)

/ui/Assembly1MyThing
/ui/Assembly2MyThing

In my service model assemblies,

public class Assembly1.MyThing : Assembly1.DtoBase { }
public class Assembly2.MyThing : Assembly2.DtoBas { }

In the code generation,

public partial class Assembly1MyThing : Assembly1DtoBase { }
public partial class Assembly1DtoBase { }

public partial class Assembly2MyThing : Assembly2DtoBase { }
public partial class Assembly2DtoBase { }

That seems like a good concession to not not breaking SS fundamental concepts while still allowing for use cases like mine.

.NET languages are the one place which does support types in different namespaces if you don’t override the default namespaces.

It’s still going to break in every other language.

Can you look again at my suggestion? I didn’t add multiple namespaces. I used a prefix so there wouldn’t be collisions when generating the code, and also when generating the auto-routes such as /api/{dto} and /ui/{dto}.

Doesn’t that solve both our concerns?

I’m saying C# doesn’t need this workaround, it already supports types in different namespaces. Are you suggesting I change every other language to support this feature?

What’s with these weird hacks, why don’t you just change the conflicting type names?

This is a feature that no-one else is going to use.

Definitely not suggesting that, no!

Let’s take a step back. The issue I’m facing is that I have two domains: Users and Widgets.

In Users, I have a GetAccount { int UserId; } service model at route /users/{UIserId}/account that returns an AccountResponse { public int Id; public string Name; }

In Widgets, I have GetAccount { int WidgetId; } service model at /widgets/{WidgetId}/account that returns AccountResponse { public int Id; public int WidgetCount; }

From a DDD standpoint, each have their own idea of what an Account is. So they have individual AccountResponses. And from a DDD perspective, they both are calling GetAccount.

This presents a problem in the SS admin if I want to host both of these in the same service. Because /ui/GetAccount and /api/GetAccount won’t know which version to present.

Similarly, whatever does load in the UI ends up being a composite of the two. So when I load /ui/GetAccount it generates an autoform with UserId and WidgetId as the two request properties on the screen. Instead of picking one or the other, it’s picking both and merging them. I can only assume because it’s creating an internal dictionary and adding in the properties from the type. You can see this most notably when when you use the C# code generator. It uses partial classes everywhere, so you can clearly see why it would end up with both properties in the GetAccount DTO.

So how do we fix it? I can go to all my teams and say “sorry, but someone already used GetAccount. Please rename your class. Oh and also the AccountResponse name is also taken. So rename that too. Oh and the UserType enum. Yea… rename that, too.” That’s a lot to ask the team to rename.

What I’m suggesting is saying “In typeof(UserService).Assembly, when generating code, prefix all objects with ‘User’; and in typeof(WidgetService).Assembly, when generating code, prefix all objects with ‘Widget’”

The end result is /ui/UserGetAccount and /ui/WidgetGetAccount as the auto-generated code. It’s a collission-prevention mechanism. I don’t see how I’d be the only one to use this and how this wouldn’t be a useful feature.

My only other solution right now would be to deploy this as a series of microservices so that I have literally different sets of APIs on different deployments. I’m trying to avoid that. I’m trying to use a modular monolith approach here. Having some kind of mechanism to work around the unique-name across all services requirement would solve for that while keeping the fundamental requirement of uniqueness intact.

We’re misaligned on the meaning here. Either I’m missing the point you’re making or you’re missing the point of my suggestion. If it’s me, let me know where I’m off on my assumptions. Thanks.

Either get them to rename conflicting types or map the types in the Service implementation to a non-conflicting DTO and return that so your internal conflicts are not projected beyond your service layer.

Or fix the root of the problem of having independent teams and have isolated AppHost/APIs per team. The main value of microservices is to enable teams to work freely so they can avoid conflicts like this.

I don’t think it would be useful because I don’t know of anyone else having this issue where they return duplicate types like Account and they leave it up to the client to workout why their different. Do you know of any public API that does this?

I just don’t understand why you think I’m leaving it up to the client to work out. I’m asking AppHost, not the client, to allow me to stitch together my service services. The same way SS allows me to configure what metadata it generates. The same way I can add to my service model VisibleTo settings so SS knows whether or not to generate that particular class when it does generate client libraries. If SS on the AppHost side simply changed what it auto-generates for the user in all clients then it would be as if the developers had named those objects that way in the first place. I honestly see no difference.

The thing about public RESTful APIs is that the underlying implementation should be client agnostic.

If I used MVC-based APIs, it wouldn’t care if I used the same class name (in different namespaces). This requirement of complete uniqueness across all servicemodel assemblies feels like it’s bleeding implementation requirements into my service layer. This isn’t the first time I’ve had this issue, and I’ve worked around it in the past by simply renaming DTOs. I’m looking for ways to avoid having to do that while still not changing a fundamental requirement of SS.

What does their generated APIs look like? How many languages do they support this in?

Agreed, you’re already free to call these APIs with duplicate type names from a REST API, use a generic HTTP client and parse the untyped JSON. The issue is generating conflicting types in different languages.

That doesn’t change any of the questions I’m asking, nor does it change that you think I’m asking the client to do something. I get it. It’s why I’ve used SS for every project I’ve worked on since it first launched. This one issue has been a thorn in my side a few times. I’m proposing a solution that continues to allow that fundamental requirement without imposing the requirement at development time. By allowing me, the AppHost creator, the guy gluing everything together and standing up my service, to apply a prefix/suffix to the service models being referenced in my service assemblies, I can universally stitch together whatever services I want regardless of service source. It becomes infinitely composable by allowing me, the AppHost creator, to define how the composition will work.

I know it’s not recommended, but the same solution could be applied to a V1 vs V2 service assemblies. All sevices in (MyApp.V1.UserService) I would prepend a V1 to the name; and all services in MyApp.V2.UserService I would prepend a V2. So V1GetUser and V2GetUser. They already have different [Route] attributes. But they can’t co-exist under the same deployment simply because they have the same DTO name. Right? So if we added metadata to the “What is my DTO name?” question and the answer is $“{Prefix?}{DTO}” then does that not retain the integrity with allowing stronger composition without requiring design-time requierments of uniqueness?

No one’s going to use this feature because their just going to change the type name instead of resorting to ugly workarounds like this. This feature basically generates incompatible types so now client source code that uses this is incompatible with code from other clients or the internal service gateway implementations that use real types.

I’m not considering this feature because it’s highly disruptive, goes against the constraints existing features were designed with, adds complexity to the code-base trying to support a feature I dislike that we have to maintain and support forever and factor in our future features that this affects, that no-one else will use.

As such I don’t want to have it in ServiceStack’s code-base. If you want to support this feature yourself you’re free to take a copy of the Add ServiceStack Reference implementation for the language you want, e.g. for for C# it’s in CSharpGenerator.cs which is called from TypesCSharp API and create a different API with a modified copy that suits your usecase.

I’m fine with you not wanting to add it to the codebase. I’m still really bothered that you think this is one-off client generation thing. If I felt that we were seeing eye-to-eye on the feature and still you didn’t want to add it, then I’d have let it drop ages ago. Give your last message, I still think what you think my ask is doesn’t align with what I’m actually suggesting to add, nor would it generate incompatible types or any of that.

Regardless, I’ll drop it now. I just wanted to add this last comment for future search sake in case someone else has the question.

I appreciate your time, mythz. Thank you

1 Like

For the record, this is not true. I was asking for this awhile back, and you gave me the same uninspiring response. Typescript-ref and similar class names, different namescape - Clients / TypeScript - ServiceStack Customer Forums

My use case is simple and similar. We have a bunch of Features we want to plug into a single API project. Each feature has its own service models / interfaces, and while running 1 API per 1 Feature separately they are fine, when running multiple features in a single API they can run into the same conflicts described above.

It would be awesome if you could produce a logical work-around that fixes this issue we are experiencing-- to have a way to plug in multiple features and service stack take care of ensuring that each service model/interface is auto-mapped to ensure no duplication of type names.

Thus allowing the metadata / openapi / auto-gen service clients dtos to all get wired up correctly so that when using the serviceclient’s / dtos it hits the API and routes to the correct feature’s service implantation.

It could be as simple as adding the feature name to the pattern and ensuring the mappings get wired up, and as @grexican pointed out, can be optional configuration to wire it up.

 appHost.LoadPlugin(new SomeFeature()).WithPrefix(nameof(SomeFeature));
 appHost.LoadPlugin(new SomeFeature()).WithDefaultPrefix();

Then when generating the dtos / metadata code Account would become SomeFeatureAccount. It’s url would be prefixed with /SomeFeature/ and it allows for the dev teams to work without needing to be aware of this current limitation.

I hope you reconsider your opiniated position, as there are valid use cases out here and your current position leaves a gap in your product. You don’t see it, but it’s there and now I know I’m not the only one to come across it. A lot of friction that could be reduced if only you saw the value in doing so-- even if it was just for this use case.

Thank-you for your time and attention to this matter.

1 Like