6.1 Relationship Handling in Synchronization
Synchronization becomes challenging when resources are related to each other. SData provides a conceptual framework that should handle most practical situations. It relies on a small set of rules that applications should follow to synchronize their resources.
SData distinguishes four types of relationships between resources:
SData provides a first set of synchronization rules for reference relationships and a second one for parent/child relationships. Associations are not handled directly as it is expected that associations will be reconstructed from references that have been synchronized (see below).
The first rule is that referenced resources must be synchronized before referencing resources. For example, a sales order resource usually references a contact resource. So contacts must be synchronized before sales orders.
The reference information should be encoded via an sdata:uuid attribute in the synchronization payload, as the following example demonstrates:
<salesOrder sdata:uuid="44D446D4-5700-41cc-92FB-3BA0FF6017CC" xmlns="http://schemas.sage.com/myContract"> <salesOrderID>43660</salesOrderID> <orderDate>2001-07-01</orderDate> <shipDate xsi:nil="true" /> ** <contact sdata:uuid="4AB7DA77-C841-4bef-955A-08D661D86430"/> ** <subTotal>7326.5034</subTotal> <!-- more elements - omitted for clarity --> </salesOrder>
The receiving endpoint will use its UUID mapping table to translate the contact UUID into an internal contact ID. It will then store the internal contact ID in the sales order record.
The payload element that represents a reference relationship should only carry an sdata:uuid attribute, it should not have any XML sub-elements. In the example above, the <contact> element does not have any subelements.
It is usually possible to find an ordering of the resource kinds that complies with the first rule above. There are nevertheless cases where references form cycles and where this rule cannot be applied completely. In such cases, several passes may be necessary to synchronize all references.
A typical example is the relationships between employees and departments in an HR system: employees have a reference to the department they belong to and departments have a reference to a manager employee. This problem can be solved by synchronizing the departments without their manager reference in a first pass, then synchronizing the employees, and then running a second pass on the departments with their manager reference properly set.
Parent / Child relationships are more difficult to handle because it is usually difficult, if not impossible, to synchronize the parent and child resources separately. For example, in the case of a sales order and its lines, the lines must usually be sent together with their containing sales order because they must be handled together in a single transaction. Synchronizing the lines separately would jeopardize the integrity of the system.
So, SData recommends that parent and children be synchronized together in a single pass. The children resources will not be synchronized individually, they will always be sent with their parent resource, even if the child has been modified and the parent has not.
On the other hand, SData does not impose that all children be sent at all times. Only the children that have been modified since the last synchronization pass must be included in the parent payload and SData provides a special attribute (sync:isDeleted) to mark deleted children. SData recommends that only modified children be sent but this is just an optimization: sending all the children at every pass is also allowed.
The following example shows the payload to synchronize a sales order resource and its lines:
<salesOrder sdata:uuid="44D446D4-5700-41cc-92FB-3BA0FF6017CC" xmlns="http://schemas.sage.com/myContract"> <salesOrderID>43660</salesOrderID> <orderDate>2001-07-01</orderDate> <shipDate xsi:nil="true" /> <contact sdata:uuid="4AB7DA77-C841-4bef-955A-08D661D86430"/> <subTotal>7326.5034</subTotal> <orderLines> <salesOrderLine sdata:uuid="DD70AC21-7647-4718-BC4D-1C10B957AC54"> <lineNumber>3</lineNumber> <product sdata:uuid="455BCC8A-A261-4cf0-A105-599995160C5A"/> <orderQty>1</orderQty> <unitPrice>874.7940</unitPrice> </salesOrderLine> <salesOrderLine sdata:uuid="2CAE812E-7A33-4dae-89D8-9E206AA28F95"> <lineNumber>8</lineNumber> <product sdata:uuid="455BCC8A-A261-4cf0-A105-599995160C5A"/> <orderQty>1</orderQty> <unitPrice>874.7940</unitPrice> </salesOrderLine> <salesOrderLine sdata:uuid="B5084C19-ED7F-4751-9B9F-212006598ADE" sync:isDeleted="true"/> </salesOrder>
As the child resources are always synchronized in the context of their parent, the endpoint should use the same clock tick for the parent and its children. So, the payload does not contain any synchronization state (endpoint + tick + modification stamp) for child resources.
The recipient should always use the UUIDs to match the child resources with existing resources. If the recipient finds a match on the UUID, it should update its existing child resource, unless sync:isDeleted is set to true, in which case it should delete its existing child.
If, on the other hand, the recipient does not find any match, it should consider that the resource is a new child and it should create it.
In the example above, the recipient will update line #3 if it finds a match on its UUID, it will create line #8 if it does not find a match on its UUID, and it will delete the last line (UUID B5094C19…).
When a parent resource is deleted, its children should be automatically deleted by the recipient. So the payload should not include the child resources. The example sales order and all its lines can be deleted by sending the following payload:
<salesOrder sdata:uuid="44D446D4-5700-41cc-92FB-3BA0FF6017CC" sync:isDeleted="true" xmlns="http://schemas.sage.com/myContract" />
Associations are often derived from reference relationships in the opposite direction. A typical example is the relationship between a trading account and its sales orders. This association is derived from the reverse relationship, which is the reference from a sales order to its trading account. In this case, there is no need to synchronize associations explicitly because the associations will be correctly computed once their corresponding references have been synchronized.
The situation becomes more complex when the association is many to many rather than one to many. A typical example would be the association between students and classes: one student attends several classes, one class is being followed by several students. But these associations are usually modeled with an intermediate “association resource”. In the student/class example, the association resource would be a “class assignment” resource that references one student on one side and one class on the other side. So, this case can be handled by synchronizing the student and class resources first, and then synchronizing the class assignment resources with their references to the students and classes that have been synchronized before.
The case where many to many associations are not modeled via an intermediate “association resource” is left for further study. One solution would be embed the association list in one of the payloads.