As your product and back-office requirements evolve you'll need to update your Schema and migrate existing data. Your Schema is immutable and versioned, and once deployed to a Ledger FRAGMENT will enforce backwards compatibility checks. Use migrations to safely change your Schema and Ledger data while ensuring compatibility with your code, zero down-time, and data that is always immutable and auditable.
By deploying a new Schema version, you can:
- Create new Entry Type versions
- Disable and archive old Entry type versions
- Migrate entries posted with archived Entry Type versions
- Disable and archive old Ledger Accounts
- Migrate entries posted to archived Ledger Accounts
The exact process for performing a migration depends whether you need to migrate all or some of your existing data, and whether affected balances need to stay accurate during the migration.
a. Updating Entry Types
#When you create a new Entry Type it will automatically be marked as V1. Once deployed, Entry Type versions are immutable and cannot be deleted from your Schema. Editing Entry types in the Dashboard will automatically create new versions as needed.
In the Schema JSON Entry Types have a type and typeVersion field that together must be unique. To ensure backwards compatibility all deployed Entry Type versions are always kept on the Schema.
{
"key": "schema-key",
"chartOfAccounts": {...},
"ledgerEntries": {
"types": [
{
"type": "user_funds_account",
"typeVersion": 1,
"lines": [
{
"account": {
"path": "assets/banks/user-cash"
},
"key": "funds_arrive_in_bank",
"amount": "{{funding_amount}}"
},
{...}
]
},
{
"type": "user_funds_account",
"typeVersion": 2,
"lines": [
{
"account": {
"path": "assets/banks/user-cash"
},
"key": "funds_arrive_in_bank",
"amount": "{{funding_amount}} + {{fee}}"
},
{...}
]
}
]
}
}When posting you can specify typeVersion to select which version of an Entry Type to use. If not set, 1 will be used as the default.
b. Disabling Entry Types
#Once you have created a new Entry Type version, you can prevent postings of the previous version by setting the old Entry Type versions's status field to disabled in your Schema.
{
"key": "schema-key",
"chartOfAccounts": {...},
"ledgerEntries": {
"types": [
{
"type": "user_payment",
"typeVersion": 1,
"status": "disabled",
"lines": [...]
},
{
"type": "user_payment",
"typeVersion": 2,
"lines": [...]
}
]
}
}When an Entry Type is disabled:
- Any attempt to post using that Entry Type will result in a
BadRequestError - Existing entries posted with that type will remain unchanged and queryable
- The Entry Type can later be re-enabled or archived
- You can still reverse or migrate entries that were posted with the disabled Entry Type
c. Archiving Entry Types
#You can archive an Entry Type version to trigger the migration process for historical data. Archiving an Entry Type version:
- Requires the Entry Type version to be first be
disabledfor 45 seconds - Hides it from the Dashboard Schema designer by default
- Creates a
LedgerEntryDataMigration(see below) for each Ledger using the Schema to query for its entries to migrate - Does not automatically migrate or change existing entries
To archive an Entry Type, set its status field to archived in your Schema:
{
"key": "schema-key",
"chartOfAccounts": {...},
"ledgerEntries": {
"types": [
{
"type": "user_payment",
"typeVersion": 1,
"status": "archived",
"lines": [...]
},
{
"type": "user_payment",
"typeVersion": 2,
"lines": [...]
}
]
}
}Once archived, you can un-archive an Entry Type version by settings its status back to active. This will set the LedgerEntryDataMigration created for the archival to status inactive. The Entry Type version will then be available again for use in your application.
d. Migrating Entries
#When you deploy a Schema with a newly archived Entry Type version a LedgerEntryDataMigration will asynchronously be created for each Ledger using the Schema. To migrate existing entries that were posted with the archived Entry Type version:
- Enumerate
ledgerEntrieson the relevantLedgerEntryDataMigration. Since the Entry Type version was previously disabled, no new Ledger Entries will be added to this list. - Call
migrateLedgerEntryfor each Ledger Entry with the new version of the Ledger Entry. This will remove the Ledger Entry from theLedgerEntryDataMigration. The migration is complete when theledgerEntriesarray is empty.
query GetEntryMigrations(
$ledger: LedgerMatchInput!
$filter: LedgerEntryDataMigrationsFilterSet
) {
ledger(ledger: $ledger) {
ledgerEntryDataMigrations(filter: $filter) {
nodes {
entryType
typeVersion
ledgerEntries {
nodes {
id
type
posted
parameters
}
pageInfo {
hasNextPage
endCursor
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}{
"ledger": {
"id": "ledger-id"
},
"filter": {
"entryType": {
"equalTo": "user_payment"
}
}
}Use the migrateLedgerEntry mutation to migrate your Ledger Entries. This mutation's input takes an id of the Ledger Entry to be migrated, and the newLedgerEntry to migrate to. Under the hood this performs a reversal of the entry to migrate followed by a repost using the new Entry.
mutation MigrateLedgerEntry($input: MigrateLedgerEntryInput!) {
migrateLedgerEntry(input: $input) {
... on MigrateLedgerEntryResult {
reversedLedgerEntry {
id
ik
}
reversingLedgerEntry {
id
ik
}
newLedgerEntry {
id
ik
type
typeVersion
posted
created
lines {
nodes {
amount
}
}
}
}
}
}{
"input": {
"id": "entry-to-migrate-id",
"newLedgerEntry": {
"ledger": {
"id": "ledger-id"
},
"type": "user_payment",
"typeVersion": 2,
"parameters": {
"amount": "10000",
"fee": "100"
}
}
}
}e. Updating Accounts
#Like Entries, your Ledger Accounts are generally immutable and safeguarded by FRAGMENT's backwards-compatibility rules. However a number of Account migrations are supported to ensure flexible Schema design.
Accounts can be migrated between strongly and eventually consistent by updating the consistencyConfig field for the Account on the Schema. Once deployed a LedgerMigration will be created for each of the Schema's Ledgers that will apply the update.
Single-currency Accounts can be upgraded to multi-currency Accounts by updating the currencyMode field for the Account on the Schema. Once the Schema change is deployed a LedgerMigration will be created for each of the Schema's Ledgers that will apply the update.
Note:
- Multi-currency Accounts cannot be downgraded to single-currency
- You will need to ensure that you're providing the
currencyargument when reading thebalance,ownBalance, orchildBalancefields of the now multi-currency Account. No change is required if you're reading thebalances,ownBalances, orchildBalancesfields of the Account.
f. Disabling Accounts
#When you need to prevent new entries from being posted to a Ledger Account you can disable it by setting the Account's status field to disabled in your Schema. This is useful when:
- You're migrating to a new account structure
- An account is no longer needed for new transactions
- You're preparing to archive and migrate historical data
When an Account is disabled:
- Any attempt to post Ledger Entries to it will result in a
BadRequestError - All Entry Type versions that post to the disabled Account must also be disabled in your Schema
- Existing entries and balances remain unchanged and queryable
- Entries can still be posted to any of its active children Accounts
- The account can later be re-enabled or archived
{
"key": "schema-key",
"chartOfAccounts": {
"accounts": [
{
"key": "legacy-user-funds",
"name": "Legacy User Funds",
"template": "assets/banks/legacy-user-funds",
"status": "disabled"
},
{
"key": "user-funds",
"name": "User Funds",
"template": "assets/banks/user-funds"
}
]
}
}g. Archiving Accounts
#Once an Account is disabled, you can archive it to trigger the migration process for historical data. Archiving an Account:
- Requires the Account to first be
disabledfor 45 seconds - Hides it from the Dashboard Schema designer by default
- Creates a
LedgerAccountDataMigrationfor each Ledger using the Schema to query for its entries to migrate - Does not automatically migrate or change existing entries posted to the Account
To archive an Account, set its status field to archived in your Schema:
{
"key": "schema-key",
"chartOfAccounts": {
"accounts": [
{
"key": "legacy-user-funds",
"name": "Legacy User Funds",
"template": "assets/banks/legacy-user-funds",
"status": "archived"
},
{
"key": "user-funds",
"name": "User Funds",
"template": "assets/banks/user-funds"
}
]
}
}h. Migrating Accounts
#When you archive an Account, FRAGMENT automatically creates a LedgerAccountDataMigration that tracks all entries posting to that account. You can query for these migrations and filter by specific structuralPaths:
query GetAccountMigrations(
$ledger: LedgerMatchInput!
$filter: LedgerAccountDataMigrationsFilterSet
) {
ledger(ledger: $ledger) {
ledgerAccountDataMigrations(filter: $filter) {
nodes {
accountPath
ledgerEntries {
nodes {
id
type
typeVersion
posted
parameters
lines {
nodes {
account {
path
}
amount
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}{
"ledger": {
"id": "ledger-id"
},
"filter": {
"accountPath": {
"equalTo": "assets/cash"
}
}
}Before migrating a Ledger Account it is important to consider whether or not you can take downtime on reading the Account balance. This will inform whether you should run an online or offline migration.
For offline migrations your Account balance will be in flux while the migration is running. To perform one:
- Update your Schema to move to the new Account
- Disable the old Account and create the new Account you want to migrate to.
- Disable all Entry Type versions that post to the old Account.
- Create new Entry Type versions that post to the new Account.
- Deploy your Schema.
- Update your code to move to the new Account
- Update your code to use the new Entry Type versions.
- Since new data is going to the new account and old data is still in the old account, both of these Account balances will be inaccurate. Ensure that any process that reads these balances is aware of this.
- Update your Schema to remove the old Account
- Archive your old Account and old Entry Type versions that post to it.
- Deploy your Schema.
- Migrate existing data
- Query all Ledger Entries posting to the old Account using the
ledgerEntrieslist on theLedgerAccountDataMigration. - Migrate your list of Ledger Entries using the new Entry Type versions. Once completed, read from the new Account balance.
- Query all Ledger Entries posting to the old Account using the
For online migrations your Account balance will stay accurate throughout the process. To perform one:
- Update your Schema to add new Accounts and Entry Type versions
- Create the new Account you want to migrate to. Add an additional control Account to support the migration.
- Create new dual-write Entry Type versions that post to both the old, new, and the control Account (used to balance the Entry).
- Create a new final Entry Type versions that post to only the new Account.
- Deploy your Schema.
- Start dual-writing
- Update your code to use the new dual-write Entry Type versions for new data. Now both old and new Accounts will be updated with new data, but the new Account does not yet contain existing data, so its balance will be inaccurate.
- Query for all Ledger Entries posting to the old Account that used the old Entry Type versions.
- Use
migrateLedgerEntryto migrate each of the old Ledger Entries to the new dual-write Entry Type version. Now both old and new Accounts will have matching balances.
- Switch over to the new Account
- Update your code to use the new Account balance.
- Update your code to use the final Entry Type version that posts to only the new Account for new data.
- Archive the old Account and its data
- Disable the old Account and the dual-write Entry Type versions.
- Deploy your Schema.
- Archive the old account and the dual-write Entry Type versions.
- Deploy your Schema.
- Query all Ledger Entries posting to the old Account using the
ledgerEntrieslist on theLedgerAccountDataMigration. - Use
migrateLedgerEntryto migrate old Entries to final Entry Type versions. Once completed, the old Account will have no lines.