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:
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.
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.
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:
BadRequestErrorYou can archive an Entry Type version to trigger the migration process for historical data. Archiving an Entry Type version:
disabled for 45 secondsLedgerEntryDataMigration (see below) for each Ledger using the Schema to query for its entries to migrateTo 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.
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:
ledgerEntries on the relevant LedgerEntryDataMigration. Since the Entry Type version was previously disabled, no new Ledger Entries will be added to this list.migrateLedgerEntry for each Ledger Entry with the new version of the Ledger Entry. This will remove the Ledger Entry from the LedgerEntryDataMigration. The migration is complete when the ledgerEntries array 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"
}
}
}
}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:
currency argument when reading the balance, ownBalance, or childBalance fields of the now multi-currency Account. No change is required if you're reading the balances, ownBalances, or childBalances fields of the Account.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:
When an Account is disabled:
BadRequestError{
"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"
}
]
}
}Once an Account is disabled, you can archive it to trigger the migration process for historical data. Archiving an Account:
disabled for 45 secondsLedgerAccountDataMigration for each Ledger using the Schema to query for its entries to migrateTo 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"
}
]
}
}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:
ledgerEntries list on the LedgerAccountDataMigration.For online migrations your Account balance will stay accurate throughout the process. To perform one:
migrateLedgerEntry to 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.ledgerEntries list on the LedgerAccountDataMigration.migrateLedgerEntry to migrate old Entries to final Entry Type versions. Once completed, the old Account will have no lines.