Avoiding interfaces in DTOs

I am trying to get my head around the best DTO design for a particular use case.

Our application supports custom properties which allow our customer to add their own properties to our business objects. The custom property may be one of a variety of types, from primitives (string, int etc) to a reference to another business object.

Given that I do not want to re-generate seperate DTOs for each customer it feels like the simplest solution is something like this:

 public Dictionary<string, ICustomProperty> CustomProperties { get; set; }

This if course relies on SS injecting the __type property for deserialization.

Other than this going against the SS philosophy the main problem I have is that types implementing ICustomProperty do not reflect this when we call /types/java or /types/csharp.

I am looking for either an alternate design pattern I can implement which avoids the interface or the inclusion of the interface in the SS metadata.



Maybe I can shed some light on what we did with this situation. I won’t say if it’s best practice or not as I have never seen any conclusive solution for custom properties but perhaps I can provide our own experiences in how we dealt with this…

On the server and in our C# client API we provided a ICustomProperties interface (w/ a Dictionary<string, object> CustomProperties property) on all of our request and response DTOs. We then customized the json serializer SS comes with to handle custom properties automatically.

In the concrete C# DTOs on the server we used a configuration api we built up to provide information on how to map the custom properties to our database (or our internal business entities in our case). Also if the property was passed through on the json as another property and not as a child in the CustomProperties property we would add all properties not on the concrete DTO into the CustomProperties property automatically during server side deserialization.

From the client side by default all CustomProperties were sent down as normal properties on the DTO. Making them indistinguishable from the concrete DTO properties. C# clients would use our serializer wrapper and javascript clients worked out of the box.

So in the end this is a very custom solution but works well for us even for clients who are not using .net.

Basically you’ll want to design it so DTO’s don’t use polymorphism. You can look take a similar approach to how Amazon DynamoDB handles multiple different types in its AttributeValue where it contains a DTO with explicit properties for different value types.

You can make this more dynamic by just storing the value in a string in which case you’ll want to capture the type it is, e.g:

class CustomProperty
    string Type
    string Value

For storing objects you could serialize it as JSON in the Value or have a more structured type for storing Object properties, likewise with arrays, e.g:

class CustomProperty
    Dictionary<string,CustomProperty> Object
    List<CustomProperty> Array

Thanks for the reply.

We have done something fairly similar to what you have done and it works well, except when we attempt to generate Java or C# client proxies from the /types/java or /types/csharp endpoints.

From my reading of your solution it would have the same problem as ours, the DTO would not contain the custom property and the custom property type in the metadata will not implement the interface.

The solution from mythz is fine but so far as I can see declaring a ‘string Type’ property is no better design than relying on __type.

My investigation of the code that generates the type metadata indicates that hacking it to implement interfaces would be difficult as the interfaces are not included in the Metadata type.

Maybe I can get this working using base classes rather than interfaces, which does not seem any better design wise but looks like it will at least work.

It is better because it’s flat, completely descriptive and doesn’t rely on polymorphism which is what’s going to have less issues.

Is it possible to organise a phone support call sometime? I just don’t see how a type, such as your CustomProperty above, which relies on a Type property to describe the data stored in Value is completely descriptive.

How is it flatter or more descriptive than having ICustomProperty which relies on __type being injected?

Sorry if I am being obtuse but I really want to understand rather than just nod and smile.

Is it more descriptive because it has the Value property? So the end user knows about the Value string property within which they can store any data type and rely on the server to deserialize it based on the Type property?

thanks for your patience.

By descriptive I mean that all the properties on the DTO are statically defined, which clients/metadata can know of ahead of time. Polymorphism is the opposite, it’s saying here’s a black hole in your Service Contract which I want to try send you anything in - which has weak support across serializers and often leads to issues.

When you want untyped data structures, use built-in collections like a dictionary - which has 1:1 mapping to built-in collections in different languages.

OK, I get that. The difference is the Value string property. I agree that is better than an interface. To an extent though it feels like a less bad rather than a good solution though given that the Value property is also a black hole. Could be a number,string,object,date…

Right, that’s making it more dynamic so that it can be more generic, just like a Dictionary can be used to hold any key/value pairs. A string can be used to store anything which is why you need the type to define what it is. The difference is that it adheres to the service contract and makes it an app-level concern. If you look at DynamoDb DTO contract they still use strings to capture any number, e.g:

new AttributeValue { N = "201" }

But the N says to interpret that as a number. The more dynamic approach I’ve suggested is instead of having different properties, use the same Value property and instead have a separate Type property to hold what property it is. It’s my preference (out of the 2 options, but I’d choose collections for unstructured data wherever possible) since it takes less code to genericize and lets me later support more types without modifying the Service Contract. But either way works.