Stream: methodology
Topic: Repeating choice elements
Grahame Grieve (Oct 29 2019 at 22:06):
We currently have a number of places where we have a particularly unwieldy definition link this:
Grahame Grieve (Oct 29 2019 at 22:06):
Grahame Grieve (Oct 29 2019 at 22:07):
or, as an alternative:
Grahame Grieve (Oct 29 2019 at 22:10):
Grahame Grieve (Oct 29 2019 at 22:11):
Neither of these patterns are particularly rewarding, and I'd like it if we could come up with a better approach such that the structure definition could just say:
Instantiates[x] : Canonical(Definition) | uri 0..*
Grahame Grieve (Oct 29 2019 at 22:11):
This thread is discussing options to get to that point.
Grahame Grieve (Oct 29 2019 at 22:11):
note that the fundamental underlying problem is how these things are represented in json, as an array with no type
Lloyd McKenzie (Oct 30 2019 at 02:13):
Note that making any changes in this space will likely involve breaking reference implementations, parsers and serializers and involves changing 'normative' content, so it's something that would need to meet with near universal acceptance by implementers if we were going to fix it.
Grahame Grieve (Oct 30 2019 at 03:16):
right. It's not clear that this would count as a 'breaking change' since past content would (must) continue to be valid. but it's still a significant change
Grahame Grieve (Nov 27 2019 at 12:34):
The most obvious thing to do is the simplest, rather than the nicest. To say that if the array is polymorphic, then all entries are Tuples with value and type properties:
"instantiates" : [{ "value" : "http://someuri", "type" : "uri" },{ "value" : { "reference" : "http://someuri" }, "type" : "Reference" }]
Grahame Grieve (Nov 27 2019 at 12:36):
the XML would be:
<instantiatesUri value="http://someuri"/> <instantiatesReference> <reference value="http://someuri"/> </instantiatesReference>
Lloyd McKenzie (Nov 28 2019 at 00:53):
Not sure how that can be a non-breaking change?
Grahame Grieve (Nov 28 2019 at 02:04):
it doesn't change anything that is already exchanged
Thomas Beale (Nov 28 2019 at 08:39):
The key question is whether the choice of which to use is mostly/always at run-time, or whether the intention is that one of these options will always be profiled out to leave the one that is thought to be required in some particular operational situation.
Grahame Grieve (Nov 28 2019 at 10:18):
I think it's a 50:50 mix of those things
Thomas Beale (Nov 28 2019 at 11:49):
If in any Resource, it is not knowable, I.e has to be assumed to be runtime, you can use a wrapper class/type that contains the variant ways of representing the reference, and at the point of reference you don't have any 'multiple' problem at all.
Lloyd McKenzie (Nov 28 2019 at 16:03):
That seems like an implementation strategy, but it's not clear how it adds value to the specification (and equally viable implementation strategy is just to point to Object or equivalent and have logic that checks whether the Object is one of the allowed types.
Lloyd McKenzie (Nov 28 2019 at 16:04):
A key thing is that our choices aren't generally driven by shared attributes, but rather by shared capabilities. It's totally fine to have two classes as part of a choice that have no overlapping attributes at all.
Lloyd McKenzie (Nov 28 2019 at 16:06):
@Grahame Grieve I guess we lucked out that OO decided 'instantiates' wasn't core for Observation. However, it would still break the serialization of instantiates[x] everywhere it does appear and the serialization was supposed to be normative. Howe can we say that's allowed?
Grahame Grieve (Nov 28 2019 at 16:06):
it would still break the serialization of instantiates[x] everywhere it does appear
How do you figure that?
Lloyd McKenzie (Nov 28 2019 at 16:15):
Instead of showing up as "instantiatesUri" as the JSON propery it'll show up as "instantiates"
Grahame Grieve (Nov 28 2019 at 16:16):
only it it's cardinality is 0..*, and nothing like that exists right now
Lloyd McKenzie (Nov 28 2019 at 16:17):
So this would only change the names if the element repeated, but not if maxOccurs=1? The creates a different kind of pain...
Grahame Grieve (Nov 28 2019 at 16:18):
yes that's what I proposed.
Grahame Grieve (Nov 28 2019 at 16:18):
because I don't see a way not to have that pain in the json format.
Thomas Beale (Nov 28 2019 at 18:49):
That seems like an implementation strategy, but it's not clear how it adds value to the specification (and equally viable implementation strategy is just to point to Object or equivalent and have logic that checks whether the Object is one of the allowed types.
Well it's a modelling strategy - it simplifies the Resource definitions. The variant reference / inline attributes are now in some class/definition that can contain various kinds of references and/or values. Secondly, it seems unwieldy having two data items for instantiatesUri and instantiatesCanonical - presumably only one of them will ever be set at any time, and they are both in fact URIs. As far as I can see, the construct canonical(T) indicates that the target of the URI must be a Resource of type T, but does that turn into anything concrete? Anyway, is there any reason a reference to a protocol (where this kind of thing appears in http://hl7.org/fhir/servicerequest.html) can't easily change from something that might happen to be FHIR Resource-represented or representable to something else? That would be another reason to get rid of the pattern instantiatesUri / instantiatesCanonical.
Grahame Grieve (Nov 28 2019 at 18:52):
is there any reason a reference to a protocol (where this kind of thing appears in http://hl7.org/fhir/servicerequest.html) can't easily change from something that might happen to be FHIR Resource-represented or representable to something else
I'm not quite sure what you mean there. The principle is that it might refer to a formally defined protocol - using a PlanDefinition - but it might just refer to some published paper or something published elsewhere. but when profiling, people may want to make a rule that it has to be one or the other. Though they might not either.
It is very unwieldy - I don't like where we are at all here. Hence the discussion.
Lloyd McKenzie (Nov 28 2019 at 19:21):
It is possible to have more than one. A single action could follow more than one protocol or be part of more than one order set. And they might be any combination of resource or web page/PDF.
I don't understand how introducing wrapper classes would simplify. Providing a list of candidate types seems simpler than having a class containing multiple properties restricting to only one.
Thomas Beale (Nov 28 2019 at 20:32):
is there any reason a reference to a protocol (where this kind of thing appears in http://hl7.org/fhir/servicerequest.html) can't easily change from something that might happen to be FHIR Resource-represented or representable to something else
I'm not quite sure what you mean there. The principle is that it might refer to a formally defined protocol - using a PlanDefinition - but it might just refer to some published paper or something published elsewhere. but when profiling, people may want to make a rule that it has to be one or the other. Though they might not either.
It is very unwieldy - I don't like where we are at all here. Hence the discussion.
Right - today it points to an NCI PDF, next month (perhaps due to a new research paper in the BMJ) some places decide to move over to some better NICE.org.uk protocol, for which, let's just say, the Interopen people here happen to have built a PlanDefinition profile, and suddenly those data refs inside various FHIR resources being generated days after that now have a null in the instantiatesUri field, and a new, different URI in the instantiatesCanonical field. Now let's imagine that a year later, everyone moves on yet again to another protocol, say developed by U Waterloo and the refs are now all pointing to HTML pages on their site. You get the idea.
So I think that patterns such as these need to reflect better the very fluid states of affairs in the real world, and thus to assume less, and perhaps unexpectedly, the models actually can be made cleaner, even though the reality they represent is messier than one might like.
Grahame Grieve (Nov 28 2019 at 20:46):
I think you are saying that because it's a URI in either case, the differentiation of what the URI refers to shouldn't be explicit in the instance.
Grahame Grieve (Nov 28 2019 at 20:54):
I think the whole grounds for the pattern in the first place is because some group of implementers very much wanted it to be explicit in the instance
Thomas Beale (Nov 28 2019 at 21:55):
Firstly, how are you going to make the type (e.g. ActivityDefinition) explicit in the URI? Secondly, why are there 2 URI fields when they are (slightly) alternative representations for one actual reference. This is from ServiceRequest, but I think the same argument applies to the other instances of this. You don't use 2 fields to carry one thing at runtime, especially when in all cases it is a URI.
Anyway, do you know what the implementers /designers were really thinking when they created this? Maybe they don't know much about modelling techniques and thought that was the way to capture a single data item that can have different format URIs? Maybe they know all about modelling and had something more esoteric in mind. It is not clear from the definition. Maybe worth finding out about.
Grahame Grieve (Nov 28 2019 at 22:06):
Anyway, do you know what the implementers /designers were really thinking when they created this?
Yes because I asked as soon as I saw the pattern
Thomas Beale (Nov 28 2019 at 22:07):
And... are you saying it's the right pattern for the design intention?
Grahame Grieve (Nov 28 2019 at 22:08):
no. I question both the design intention and the instantiation
Thomas Beale (Nov 28 2019 at 22:10):
Right. So, wouldn't you agree that we need to get more rigorous about this kind of thing? Because at the moment, things like this are getting cemented into the foundations and are starting to become unchangeable legacy way too soon...
Grahame Grieve (Nov 28 2019 at 22:11):
I agree we should question the design yes
Grahame Grieve (Nov 28 2019 at 22:12):
but it always helps to understand the requirements. For instance, why did the designers want to make a statement about the target explicit in the source ?
Thomas Beale (Nov 28 2019 at 22:24):
Not the target, the type of target. We have the same thing in openEHR, when you want to constrain a URI to point to a type of thing, it's actually not that easy.
Grahame Grieve (Nov 28 2019 at 22:24):
how is it done in openEHR?
Thomas Beale (Nov 28 2019 at 22:28):
Well we don't have any pretty method right now, but the right way to do it would be to store the constraint in the relevant archetype node. It means having to have a specific archetype node type for references. The type constraint, unlike nearly every other archetype constraint, isn't a constraint on the runtime value of anything; instead, it just retains the fact that the designers want the URI pointing to a type XYZ thing at runtime. Now, in openEHR at least, when data are being created, you have the archetypes (or bits thereof, within templates) live in memory, in a data-constructor context. So the data builder can find out what that type info was for a URI field, and assuming it has some way to determine the dynamic type of the attached data object, it can do the usual check: does this data satisfy the archetype constraint.
Thomas Beale (Nov 28 2019 at 22:29):
So most if not all of this logic could be replicated in FHIR-land, but it probably needs some special node type in StructureDefinition or somewhere.
Thomas Beale (Nov 28 2019 at 22:29):
I have FHIR DSTU4 in the ADL workbench, I could show how this works in archetypes at some future date.
Grahame Grieve (Nov 28 2019 at 22:32):
well, in fact, we have the field- targetProfile. We just have a set of methodology rules built in that mean that you can't have a type that might have a targetProfile.
Grahame Grieve (Nov 28 2019 at 22:35):
either it does or doesn't. And so you end up where we are
Grahame Grieve (Nov 28 2019 at 22:37):
so a different approach to this problem is to either define a new type that may have a target profile. (or adapt either uri or canonical so that it may have a targetProfile or not)
or Define a special value for targetProfile that means, not a resource
Grahame Grieve (Nov 28 2019 at 22:38):
that reminds me, though, of one of the differences here - the resolution path for a canonical is different to the resolution path for a uri, which is one reason to make them separate
Thomas Beale (Nov 28 2019 at 22:38):
Right, so you have to have a wrapper type, that carries a URI reference, and some other optional constraint info, e.g. the target canonical type if there is one. Then the Resource definition can just have one field 'protocol' or whatever, with the type constraints, but now understood as: if this URI points to a Resource at runtime, it should be of one of these types...
Thomas Beale (Nov 28 2019 at 22:39):
Well, all the more reason to hide that detail in a small type...
Grahame Grieve (Nov 28 2019 at 22:39):
hah- you've ended up exactly where we'd be, if not that the JSON foramt doesn't support that construct. Which is why we disallow it. I'm proposing a solution...
Thomas Beale (Nov 28 2019 at 22:40):
Well that's the tail wagging the dog, but anyway, why doesn't it work? I can think of a JSON structure to do the openEHR equivalent...
Thomas Beale (Nov 28 2019 at 22:41):
isn't the problem in the StructureDefinition meta-type on which the JSON is based?
Thomas Beale (Nov 28 2019 at 22:41):
(i.e. you need a new meta-type)
Grahame Grieve (Nov 28 2019 at 22:41):
because json isn't inherently polymorphic. And we didn't define the structure to do that - much argument about pros and cons back then.
Thomas Beale (Nov 28 2019 at 22:41):
you need to use _type in JSON. If you don't, you're dead.
Thomas Beale (Nov 28 2019 at 22:41):
Seriously...
Thomas Beale (Nov 28 2019 at 22:43):
{ "message": "Error message", "code": 90000, "errors": [ { "_type": "DV_CODED_TEXT", "value": "Error message", "defining_code": { "terminology_id": { "value": "local" }, "code_string": "9000" } }, { "_type": "DV_CODED_TEXT", "value": "Secondary error message", "defining_code": { "terminology_id": { "value": "local" }, "code_string": "8000" } } ] }
Grahame Grieve (Nov 28 2019 at 22:43):
I'll take that as an endorsement of my proposal then, since that is what my proposal was
Thomas Beale (Nov 28 2019 at 22:44):
that's just some openEHR EHR data, but you get the idea - those fields starting with '_type' are polymorphic fields.
Thomas Beale (Nov 28 2019 at 22:44):
I would strongly recommend using that generally, it's crippling not having it.
Thomas Beale (Nov 28 2019 at 22:45):
(we do the same in JSON archetype serialisation, I just don't have one to hand right now)
Brian Postlethwaite (Jan 05 2020 at 03:08):
Makes json de-serialization totally custom doesn't it?
And would need to switch on that property before trying to understand the other fields.
Grahame Grieve (Jan 05 2020 at 06:39):
yes, like we do already with resourceType
Thomas Beale (Jan 09 2020 at 14:39):
Well you can use normal JSON deserialsation, but then you have to post-process the resulting data tree. That implies using an intermediate class model that represents a generic data tree that can be further traversed to generate the 'real' class instances. There are other strategies. JSON just ins't semantically very strong.
Last updated: Apr 12 2022 at 19:14 UTC