This page attempts to outline the overarching concepts related to the life cycle of service transactions in Verne. A life cycle in this context is the following:

Life Cycle Definition

The journey and various states a “thing” (i.e. any root domain e.g. company, reserved name, user, business calendar, payment basket, etc.) can take throughout it’s life. Therefore this encompasses it’s creation (migration/registration) as well as any maintains or corrections performed on it.

This page relies heavily on concepts outlined in the business service concept related to the life cycle such as business services, registry data, service instances and service transactions – get very comfortable with these concepts.

Service Instance and Service Transactions

The business service concept outlines the relationship between a service instance and its service transactions. In short, there may be one service transaction used for the duration of a service instance but there can equally likely be multiple service transactions per service instance.

Also get familiar with versioning and the service transaction, in particular the following service transaction properties which are important when it comes to related service transactions for the same service instance:

PropertyDescription
serviceIdentifierIdentifies all service transactions relating to the same service instance
committedDateMarks a service transaction as being user-accepted i.e. non-committed service transactions are not seen by most system processes (except merge)
prevSiblingIdLinks back to the service transaction this one is making additional changes to (another user-saved action)
nextSiblingIdLinks to the service transaction that supersedes this one in terms of a maintenance (i.e. not a correction) if applicable
prevMergeIdLinks back to the service transaction this one was before it was successfully merged with another (referenced in the merge.fromVersionId property)
nextMergeIdLinks to the service transaction that replaced this one during a successful merge
mergeContains the details relating to a merge attempt. If successful, a nextMergeId will also exist, but merge details will exist regardless i.e. even for merge conflicts, so the merge details reflect whether a merge was attempted on a service transaction, whether successful or not, whereas the prevMergeId/nextMergeId values only relate to successful merges
ghostWhether the service transaction is considered throw-away data. e.g. views or uncommitted user edits that were discarded, either explicitly by the user or was abandoned and expired or some other system function
ghostReasonWhy the service transaction became a ghost if it is one

Superseded Service Transactions

If a service transaction has a value for either of the next identifiers above (i.e. version, sibling or merge), then the service transaction is considered superseded and no longer "active". If a service transaction has merge details but no nextMergeId, then it is the result of an unsuccessful merge (a merge conflict). In this case it can still be active in some senses (a user can have it loaded) but it can no longer be acted upon and is considered a dead end.

The following is the list of service transaction properties that, if they have a value, define a service transaction as being in superseded state and the typical action that put them there:

  • nextVersionId – a new maintain service was applied/activated
  • nextSiblingId – a more recent edit has been user-saved (if not activated); or a correction has been applied/activated (if it is activated)
  • nextMergeId – changes were made to another service transaction that were successfully merged into this one
  • merge (but no nextMergeId) – changes were made to another service transaction that resulted in a conflict when trying to be merged into this one

Details about when these values are set can be found on the versioning page.

Multiple Service Transactions per Service

Verne automatically saves the service transaction whenever a field is changed, so you can’t not-save your changes as such. So how do we support scenarios like when a user starts a service, saves and exits, loads it up again (from their dashboard) to continue working on it but then decides to cancel and discard their changes?

The answer is when a saved service transaction is loaded (from a user’s dashboard) instead of the existing service transaction being loaded up, a copy is made that can either be discarded or accepted at some point, in which case it would supersede the original one it was based off (with appropriate previous/next* sibling values set).

It is worth noting that previous sibling IDs (and previous version IDs) are set (when relevant) on the service transaction as soon as it’s created, so it knows “where it came from”, but the one it was based off is left alone (more than one service transaction can be based off it after all) until one of those service transactions is user-saved/committed (activated for next version IDs) at which point the next sibling/version ID is set on the originator.

Separate service transactions per user-loaded or user-saved change allow us to support some advanced features like:

  • restoring unsaved changes if a user abandons their session (i.e. closes their browser) without choosing whether to save or discard their changes
  • showing only the changes made since last reviewed on the internal task review screen when a service is returned to the presenter for revision then resubmitted

Service Transaction Tips

"Tips" relate to service transactions that have no yet been applied from a business perspective. Additionally a "tip" only applies to service transactions that have not been superseded or set to a dead-end state. Therefore service transaction tips can be thought of as service transactions that are still in play and can contribute to a service instance.

Below is a simple walk through of the start of a service to illustrate the life cycle of service transaction tips:

  1. You start a new business service. A new service transaction is created (ST1) which is the only service transaction relating to this service instance and will be a tip (unless it was activated right away, in which case it would be a head rather than a tip but let’s stick with the more standard workflow). This service transaction will have a serviceIdentifier assigned that is unique for this instance of the service.
  2. You save and exit. ST1 is still the only service transaction for this service (i.e. with the same serviceIdentifier value) and is now the current tip since it has been user-saved (committed) and will have its committedDate set
  3. You then load the existing service via the dashboard. A new service transaction will be created (ST2) based off the current tip (ST1) and these two service transactions will be related in the following way:
    • They will have the same serviceIdentifier indicating they belong to the same service instance
    • ST2 will have the _id of ST1 as its prevSiblingId, indicating that it is based off ST1
    • ST1 will not have ST2 as its nextSiblingId as it hasn’t been replaced by it or anything. ST1 is still the current tip for the service instance
  4. You make some changes and save and exit ST2. The following will now occur:
    • ST1 will have ST2 as its nextSiblingId making ST1 a superseded service transaction and largely ignored by the system from now on
    • ST2 is now committed (has a committedDate) and is the current tip for the service instance
  5. You load the existing service via the dashboard. A new service transaction will be created (ST3) based off the current tip (ST2) and these two service transactions will be related in the usual manner (see above). There are now two tips for the service instance: S2 (current) & S3
  6. You make some changes but then discard/cancel ST3S3 will get set to a ghost (ghost set to true and a ghostReason of ‘discarded’) and you’ll be back there being only tip for the service instance (S2) which is also the current tip

As you can see from the example above, there can be multiple service transactions created during the process of submitting a service instance (i.e. those sharing the same service service identifier (serviceIdentifier), but there is only ever one one current tip. The only difference between a non-current tip and the current tip is the current tip has a committed date (committedDate).

Current Tip The term “current tip” is used to reference the non-superseded, committed service transaction relating to a non-activated service instance. The current tip is the service transaction that new edits will be based off for the given service service identifier.

A tip could relate to a service transaction that has not been user-saved/committed yet, or be the current tip if it has been user-saved/committed. A tip can be in any state of being applied e.g. pending, review, revise, etc.

Since correction services cannot be saved before being applied, correction services only ever have one tip, and it is never current since it is not committed until applied (at which point it becomes a head).

Formally, a service transaction is a tip if it is:

  • not a ghost
  • has not been superseded by having a next sibling or merge ID or merge details as the result of a merge conflict
  • is either a correction that has not yet been committed or does not activated status

Note: A service transaction that is the result of a conflicted merge has not been superseded by another transaction but it still not considered a tip since it is in a dead-end state, therefore no longer active.

Service Transaction Heads

"Heads" relate to service transactions that have been successfully applied/activated.

There can be multiple heads (i.e. previous heads and the current one), but only one current head for a given identifier. The only difference between a non-current head and the current head is the current head does not have a next version ID, whereas all others do.

Current Head The term current head is used to reference the current, activated service transaction, reflecting the current entity (or thing) data. The current head is the service transaction that new maintain services are based off, as well as views of the current entity data.
The term “head” only ever applies to activated service transactions, whereas tips relate to service transactions that have not yet been applied. A current tip becomes a head version when it is applied (the current head in fact if it is a maintain service and either the current head or a plain old previous head if it’s a correction).

A head transaction can only become a non-head if corrected, in which case the new correction will be the new head. Conversely any head transaction (and only head transactions) can be a target for correcting. Un-applied corrections are never a head version even if the status is activated since they can not be user-saved/committed before being applied, so they go straight from being an active tip to a head (when applied) and the current head if correcting the current head.

Formally, a service transaction is considered a head if it is:

  • is activated
  • is committed (while you can’t usually have activated non-committed transactions, you can for corrections, so this is used to ensure we exclude non-applied corrections)
  • is not a ghost
  • has not been superseded by having a next sibling or merge ID or merge details as the result of a merge conflict

Dead-End States

Service transactions in a dead-end state are essentially ignored by the system. This means they are not included when retrieving service transactions for listing dashboard items, performing merges, checking conflicts etc.

A service transaction is considered in a dead end state if it can no longer be acted upon or updated by a user (whether the presenter or someone else) or any system process. The exception to this is if a user is already viewing the service transaction when it is put into a dead end state e.g. it was the target of a merge and is now either superseded by the successful merge result or is in a state of conflict.

The following are ways in which a service transaction are defined by Verne as being in a dead-end state:

  • it has been superseded
  • it is a ghost
  • it has a status relating to an end-state. This can be configured, but Verne includes the following statuses in this category as standard:
    • cancelled – the service instance has been cancelled by the presenter
    • expired – the service Instance has expired by a day-end job as it was not submitted/activated within the allotted time frame
    • rejected – the service instance has been rejected by an internal reviewer

Example

  1. (ST1) A user wishes to register a new company, so they select a ‘register company’ menu item:
    • A new service transaction is created (ST1)
    • ST1 gets _ididentifier and serviceIdentifier values set (amongst other things)
    • We have one service transaction: ST1 (a tip)
  2. (ST1) The user saves and exits after making some changes:
    • The service transaction is saved on every update but is registered as a special save-commit when the save & exit is performed. This means the committedDate is set on it
    • We still have only one services transaction: ST1 (current tip)
  3. (ST2) The user selects the saved, but unfinished company registration from their dashboard:
    • A second service transaction is created (ST2) for these new changes, with a prevSiblingId linking to the _id of ST1
    • ST2 gets a unique _id value, but the same identifier and serviceIdentifier values as ST1
    • We now have two service transactions: ST1 (current tip) and ST2 (a tip), linked to each other
  4. (ST2) The user submits the service and it is activated right away (i.e. there’s no workflow such as payment or review required):
    • ST2 becomes the (current tip) (by virtue of being submitted) and is then activated, so becomes the (current head) since it now represents the entity data is live on the register for that identifier
    • ST1 became superseded by the second one when ST2 became the current tip. This meant the nextSiblingId on ST1 was set to the _id of ST2
    • We still have two service transactions: ST1 which is superseded (in an end state) and ST2 (current head), linked to each other
  5. (ST3) A few days later the user (or someone else with authority) decides to maintain the address, so selects this option (most likely via a menu item or button):
    • A new service transaction is created (ST3) targeting the current head using the internal identifier from ST2 (which of course is also the same for ST1)
    • ST3 gets new _id and serviceIdentifier values set (amongst other things) but has the same identifier value as ST1 & ST2
    • ST3 gets the domainTree set using the exact domainTree from ST2
    • We have three service transactions: ST1 (superseded), ST2 (current head) and ST3 (a tip)
  6. (ST3) The user saves and exits ST3 after making some changes:
    • ST3 gets the committedDate is set on it
    • We still have three service transactions: ST1 (superseded), ST2 (current head) and ST3 (a current tip)
  7. (ST4) The user selects the saved, but unfinished maintain address service (ST3) from their dashboard:
    • A new service transaction is created (ST4) for these new changes, with a prevSiblingId linking to the _id of ST3
    • ST4 gets a unique _id value, but the same identifier as ST1ST2 & ST3 and the same serviceIdentifier as ST3
    • We have four service transactions: ST1 (superseded), ST2 (current head), ST3 (a current tip) & ST4 (a tip)
  8. (ST5) Another user with authority decides to maintain the company name, so selects this option (most likely via a menu item or button):
    • A new service transaction is created (ST5) targeting the current head using the internal identifier from ST2
    • ST5 gets new _id and serviceIdentifier values set (amongst other things) but has the same identifier value as ST1ST2ST3 & ST4
    • ST5 gets the domainTree set using the exact domainTree from ST2
    • We have five service transactions: ST1 (superseded), ST2 (current head), ST3 (a current tip), ST4 (a tip) & ST5 (a tip)
  9. (ST5) They change the name in ST5 and submit the service and it is activated right away once again:
    • ST5 becomes the (current tip) (by virtue of being submitted) and is then activated, so becomes the (current head) since it now represents the entity data the is live on the register for that identifier
    • ST2 becomes a previous/non-current head. This means it has its nextVersionId set to the _id of ST5
    • The address changes from ST5 (based off ST3) get merged into all service transactions also based off ST3 that are still active, which means: ST3 & ST4
      • A new service transaction is created (ST6) which is ST3 with the changes from ST5 merged in. ST6 gets a unique _id, its prevVersionId set to ST5‘s _id and a prevMergeId set to ST3‘s _idST3 becomes superseded by ST6 as the nextMergeId is set to the _id of ST6
      • A new service transaction is created (ST7) which is ST4 with the changes from ST5 merged in. ST7 gets a unique _id, its prevVersionId set to ST5‘s _id and a prevMergeId set to ST4‘s _idST4 becomes superseded by ST7 as the nextMergeId is set to the _id of ST7. Since ST4 is still being edited (i.e. open in the user’s browser) they’ll receive a message indicating that changes have been merged, and do they want to reload with the latest changes. They select yes, so ST4 is replaced by ST7 as the loaded service transaction
    • We have seven service transactions: ST1 (superseded), ST2 (a head), ST3 (merged), ST4 (merged) & ST5 (current head), ST6 (a current tip) & ST7 (a tip)
  10. (ST8) Another user (let’s say it’s linked to an organisation) selects the saved, but unfinished maintain address service (ST3) from their dashboard:
    • A new service transaction is created (ST8) for these new changes, with a prevSiblingId linking to the _id of ST6
    • ST8 gets a unique _id value, but the same identifier as ST1ST2ST3ST4ST5ST6 & ST7 and the same serviceIdentifier as ST3ST4ST6 & ST7
    • We have eight service transactions: ST1 (superseded), ST2 (a head), ST3 (merged), ST4 (merged) & ST5 (current head), ST6 (a current tip), ST7 (a ghost) & ST8 (a tip)
  11. (ST8) The user makes some changes in the maintain address form and saves & exits:
    • ST8 gets a committedDate set on it
    • We have eight service transactions: ST1 (superseded), ST2 (a head), ST3 (merged), ST4 (merged) & ST5 (current head), ST6 (a current tip), ST7 (a ghost) & ST8 (a tip)
  12. (ST7) The user selects to cancel their edits, discarding their changes to the saved maintain address service:
    • Since ST7 was not save-committed, it is simply discarded. This means it is given a ghost value of true with a ghostReason of discarded
    • We still have seven service transactions: ST1 (superseded), ST2 (a head), ST3 (merged), ST4 (merged) & ST5 (current head), ST6 (a current tip) & ST7 (a ghost)
0
0

Jump to Section