We've already seen version numbers in some examples. When we edited our Blueprint, the version number increased from 1 to 2. When querying for previous data, version numbers can be more convenient than timestamps. We'll see this in the section on querying by version. With versioning, we bump the version number when we edit any data within the composite. For Blueprints, this means that we bump the version number when we edit the Blueprint itself, when we replace the ImgurImage, when we replace the blueprint string, and when we add or remove tags. We'll take a closer look in the section on Composites.

Query as-of version

As-of queries by version over rest are performed by adding a query parameter to the URL. Our template is . Plugging in the key from our running example, and the version number 1, we GET This is similar to the as-of query by timestamp from the previous section, and the response is identical so we won't repeat it here. version GET /api/blueprint/{blueprintKey}?version={version} /api/blueprint/6ed1f638-a63c-3a54-af67-ba494f27bff2?version=1

Version queries in SQL

At the SQL layer, queries by version number are implemented by starting with the version table. This query returns version 1, which existed for the duration . systemFrom systemTo key createdById lastUpdatedById number createdOn 2001-01-03 23:59:59.0 2001-01-04 23:59:59.0 6ed1 User ID User ID 1 2001-01-03 23:59:59.0 2001-01-04 23:59:59.0 9999-12-01 23:59:00.0 6ed1 User ID User ID 2 2001-01-03 23:59:59.0 At this point we take the system_from value of and use it in our subsequent queries. The queries on all other tables are identical to the queries in the previous section. For example, to query the BLUEPRINT table: select * from BLUEPRINT_VERSION t0 where t0.key = '6ed1f638-a63c-3a54-af67-ba494f27bff2' and t0.number = 1 [2001-01-03, 2001-01-04) 2001-01-03 23:59:59.000 select * from BLUEPRINT t0 where t0.key = '6ed1f638-a63c-3a54-af67-ba494f27bff2' and t0.system_from <= '2001-01-03 23:59:59.000' and t0.system_to > '2001-01-03 23:59:59.000'

Composites

In the previous example, we edited the Blueprint's title and markdown description, creating version 2. Now we'll replace the Blueprint string, the ImgurImage, and add two more tags. We want to bump the version number just once more, to 3. We update the blueprint by ing . PATCH /api/blueprint/{id}?version=2

Response

As desired, we performed the several edits while bumping the version number by only one. In addition, all of the new times are identical: . At the SQL level, all the edits were performed in a single transaction. systemFrom 2001-01-05T23:59:59Z { "key": "6ed1f638-a63c-3a54-af67-ba494f27bff2", - "systemFrom": "2001-01-04T23:59:59Z", + "systemFrom": "2001-01-05T23:59:59Z", "systemTo": null, "version": { - "number": 2, - "systemFrom": "2001-01-04T23:59:59Z", + "number": 3, + "systemFrom": "2001-01-05T23:59:59Z", "systemTo": null, "createdOn": "2001-01-03T23:59:59Z", "createdBy": { "userId": "User ID" }, "lastUpdatedBy": { "userId": "User ID" } }, "title": "Edited blueprint title", "voteSummary": { "numberOfUpvotes": 0, "systemFrom": "2001-01-03T23:59:59Z", "systemTo": null }, "blueprintString": { - "sha": "cc341849b4086ce7b1893b366b0dc8e99ce4e595", - "createdOn": "2001-01-02T23:59:59Z", + "sha": "b11911083a0cf471a5156108389f9899675ccb0c", + "createdOn": "2001-01-03T00:00:00Z", "createdBy": { "userId": "User ID" } }, "imgurImage": { - "imgurId": "Imgur ID 1", - "imgurType": "image/png", - "height": 300, - "width": 300, + "imgurId": "2nd Imgur ID", + "imgurType": "2nd Imgur Type", + "height": 200, + "width": 200, "systemFrom": "2001-01-01T23:59:59Z", "systemTo": null }, "descriptionMarkdown": "Edited Blueprint description markdown", "tags": [ { "tagCategory": "belt", "tagName": "balancer", "systemFrom": "2001-01-03T23:59:59Z", "systemTo": null, "tag": { "category": "belt", "name": "balancer", "ordinal": 1, "systemFrom": "2000-01-01T00:00:00Z", "systemTo": null } + }, + { + "tagCategory": "belt", + "tagName": "prioritizer", + "systemFrom": "2001-01-05T23:59:59Z", + "systemTo": null, + "tag": { + "category": "belt", + "name": "prioritizer", + "ordinal": 2, + "systemFrom": "2000-01-01T00:00:00Z", + "systemTo": null + } + }, + { + "tagCategory": "moderation", + "tagName": "scheduled for deletion", + "systemFrom": "2001-01-05T23:59:59Z", + "systemTo": null, + "tag": { + "category": "moderation", + "name": "scheduled for deletion", + "ordinal": 18, + "systemFrom": "2000-01-01T00:00:00Z", + "systemTo": null + } } ] }

Ownership direction

sits in the middle of a many-to-many relationship between and . In this example, we considered to be part of the composite making up the . Should we also consider it to be part of the as well? This is our choice as application designers. In this case, it makes sense for to be part of the , but not part of . Stack Overflow makes a similar choice. and s are both versioned. Applying new tags to a question creates a new version of the , but not the . In the UML diagram above, composite relationships are denoted by black diamonds. Composites are subtle, so let's walk through a few examples. Editing a Blueprint's title or description creates a new version. These are properties directly on the root type. Adding or removing BlueprintTag mappings creates a new version. These objects live within the composite. BlueprintTag mappings don't have any mutable properties. If they did, editing those properties would create a new version. For example, if we persisted their relative ordering with an ordinal property, then reordering the Blueprint's tags would create a new version. When the Blueprint author changes their display name, this does not create a new version. The User object is not part of the composite. We don't allow reassigning Blueprints to another author. If we did, repointing the author would create a new version. This works well with a temporal schema, because Blueprint.createdById would be swapped. BlueprintTag Blueprint Tag BlueprintTag Blueprint Tag BlueprintTag Blueprint Tag Questions Tag Question Tag