For some of our domain objects we allow saving additional custom properties, with names determined by the client and not known to us at compile time. In our database this is basically a set of key value pairs for each entity. We are exploring different mechanisms for representing these additional fields in request JSON. For example:
{ “bultinField”: “A”, “customField1”: “B” }
From the perspective of a client, this design may seem natural: the fact that a particular field is “custom” is a detail of our data model, not the client’s. One thought for implementing this is to use a RequestFilter to compare the serialized json string to the deserialized DTO and determine (using reflection) the properties that are custom (which ones don’t match up to an available property) and store them in a dictionary on the DTO.
The other thought was to explicitly create a Dictionary<string, string> or an array of key-value pairs on the DTO and have the consumer explicitly declare the custom properties there:
{ “builtinField”: “A”, “customFields”: {“customField1”: “B”} }
{ “builtinField”: “A”, “customFields”: [{“name”: “customField1”, “value”: “B”}] }
This seems less ideal as the consumer would have to distinguish the custom properties on their end. However, this should be more straightforward to implement on the server side.
Are there any drawbacks or considerations that need to be considered? Any recommendations for a better implementation, or any potential major problems on the client or server side for a given design?
Fredrick Lackey:
Who is the consumer / user of these classes? To not know the custom properties at compile time, I assume these are public-facing artifacts for a client or someone other than your team. Is this correct? For me, knowing who the consumer of your artifacts are, or how they are being used, would be a crucial factor in making any design decision.
The way we allow attaching untyped metadata to SS’s built-in typed Auth/Register response DTO’s is with the IMeta interface:
https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Interfaces/IMeta.cs
Basically just declaring a Dictionary<string,string> to store loose typed info into. The other way is to just blob a complex type in a JSON string, the difference being that a blobbed complex type is more opaque and less introspectable when traversing in JS.
Brian Pruitt-Goddard:
+Fredrick Lackey The consumer/user of the classes is the client. They are properties only relevant to that client and therefore make no sense to add as “built in” properties available to all clients. In response DTOs, they will be displayed as if they were built in properties (similar to the first approach). Thus, the hope for them appearing in requests in the same seamless way.
+Demis Bellot I tried using the IMeta interface, but it appears to be the explicit dictionary approach that is less-desirable for our situation. Is there a reason this was chosen over the RequestFilter approach or was it simply due to it being easier to implement and less error-prone?
Personally I would make them distinct myself, if you don’t know what it is at compile-time then it’s not apart of your Service Contract so the consumer would know that’s it’s something that’s specially handled. It would be a lot more work trying to hide it, i.e. having to buffer the request stream, deserializing the JSON twice, knowing what makes an unmatched property (e.g. SS JSON is case-insensitive). It would also not be available in typed API’s.
Fredrick Lackey:
From purely a design perspective, I would opt for exchanging a property bag or dictionary for any items that are not known at compile time. Not only does this offer a PROVEN means of growing your various components without some atypical generation solution, it also offers a certain level of clarity to everyone touching the various components by implying these the data contained within the property bag really is unknown at compile time. This tends to imply that the data is either highly generic or that some type of highly dynamic validation must be executed at some point in the path. And, unless you are compiling them into isolated namespaces, this would help create a barrier and offer additional clarity between the static and dynamic code.
That being said, I’ve been down this road before. A company wanted to provide as much “hand holding” as possible to their client and this type of approach provided the APPEARANCE of a more intelligent / thorough design over using a property bag or meta data. By generating the classes and properties the client was forced to adhere to the types and felt as if our solution was specially tailored to them. The only down side I can see is if you are NOT also automating the generation of validation logic. For example, if you are generating a fields called “CreditCardNumber” and “ExpirationDate” are you truly validating this data, both for what it is and together, or simply storing it OR treating it as generic strings and dates. That may come to hurt you down the road if your client thinks that there IS some type of intelligent validation occurring when you are simply treating it as raw data.
Brian Pruitt-Goddard:
Thanks for the input. We are deciding to go with a distinct property to store these dynamic properties.
One question remains about the implementation of this property: Are there any differences between making this property a Dictionary<string, string> vs an array of key value pairs? The array could potentially be extended in the future with additional metadata properties and fits better with existing APIs, however it is a little more complicated.
Nothing other than making catering for the common use-case of custom properties harder.