Design Your Liquibase Project
Your changelog is the core of your Liquibase project. While you can use one changelog for all your deployments, this may not scale well. Instead, you can use multiple changelogs for different purposes. To organize multiple changelogs, you can use the include
and includeAll
tags to create a root (main/parent) changelog and one or more nested (child) changelogs. This document explains some best practices on structuring your changelogs.
Prerequisites
If you're new to Liquibase, make sure you understand the fundamentals before creating your own project:
- Install Liquibase – Download Liquibase on your machine
- Get Started with Liquibase – Learn how to use Liquibase with an example database
Create a project
The Get Started guide linked previously shows you how Liquibase deployments work using a sample database and changelog. However, your organization's needs go beyond a demonstration. To create a new Liquibase project, run the init project
command in your command line:
liquibase init project
This generates a new changelog and liquibase.properties
file that you can customize and expand. You can use the default settings or specify your own. For more information on command arguments, see the init project
page.
Choose a schema design pattern
You can use Liquibase in two ways:
- Shared schema: All developers work in a single, shared database schema
- Multi-schema: Each team works in a different database schema
To choose what's best for your organization, consider the processes your developers currently use to deploy changes to the database.
Shared schema
If you use a shared schema with Liquibase, all your developers work on the same database schema.
Using a single schema can make it simple to design, manage, standardize, and query your database. However, using a single schema in a large database may become confusing as you create more objects. If you need to write long queries to navigate complex object structures in a single-schema database, the queries may negatively affect performance.
It's important for everyone using the same schema to agree on a deployment process. For example, one team may modify a shared script without realizing that it's a dependency for another team's automation. One solution is to keep all schema content in a single repository owned by one team. When other teams want to make changes, they submit pull requests from their source control tool. Alternatively, each team can work in their own directory in the schema and try to minimize cross-team dependencies.
Multiple schemas
If you use multiple schemas with Liquibase, you create one or more schemas in your database for different teams.
Using multiple schemas may make it easier to organize your data, which may be helpful in large databases containing many unique data structures. Also, using multiple schemas may prevent conflicts with source control merges or broken dependencies, help you store data backups, and let you customize user and application permissions for each schema.
However, using multiple schemas means you may have to duplicate some of your data, which can make consistency more difficult. Depending on your automation tooling and workflow, you may also need to call Liquibase multiple times to send your code to production, which requires extra maintenance from your DevOps team.
Choose a changelog structure
When designing your Liquibase project, it is a best practice to create a simple root changelog that you don't have to modify directly. Instead, you can make changes to each of the changelogs beneath it, which you can reference with the include
and includeAll
tags. The nested changelogs can be modified and deployed independently, but you can also deploy all changes in a single update
from the root changelog. There are two common changelog design patterns, object-oriented and release-oriented. The design you choose should correspond to the structure of your existing code repository and workflow.
Object-oriented
Tip: For most organizations, it is a best practice to use an object-oriented changelog design pattern.
In an object-oriented changelog structure, you create one changelog per object or object type. For example:
com
example
db
changelog
changelog-root.xml
changelog-index.xml
changelog-procedure.xml
changelog-table.xml
changelog-view.xml
In this example, we have a root changelog called changelog-root.xml
which contains include
tags referencing changelog-index.xml
, changelog-procedure.xml
, changelog-table.xml
, and changelog-view.xml
.
Alternatively, you can create directories for each type of object, each containing one or more changelogs per particular database object:
com
example
db
changelog
changelog-root.xml
changelog-indexes
my-favorite-index.xml
that-other-index.xml
changelog-tables
employees.xml
customers.xml
In this example, we have a root changelog called changelog-root.xml
which contains includeAll
tags referencing the directories /changelog-indexes
and /changelog-tables
. The changelogs you modify regularly are contained in the nested directories rather than in the overarching /changelog
directory.
These designs are useful because they allow you to easily see changes made over time to specific objects (like a particular table) or groups of objects (like all tables in your database). An object-oriented changelog format makes it easier to roll back changes to particular objects without worrying about unrelated changes in the changelog.
Having a more detailed changelog structure can also potentially simplify the Source Control Management (SCM) process. If all your developers work in the same changelog, they may experience source control merge conflicts more frequently. By separating your changelogs into use-cases for different objects, developers are likely to be working in different files and therefore will not overwrite each other's code.
Release-oriented
If a release-oriented changelog structure, you create one changelog per release or release group. For example:
com
example
db
changelog
changelog-root.xml
changelog-1.0.xml
changelog-1.1.xml
changelog-2.0.xml
In this example, we have a root changelog called changelog-root.xml
which contains include
tags referencing changelog-1.0.xml
, changelog-1.1.xml
, and changelog-2.0.xml
.
Alternatively, you can create directories for each major release (or other release group), each containing one or more changelogs per minor release:
com
example
db
changelog
changelog-root.xml
changelog-1.x
changelog-1.0.xml
changelog-1.1.xml
changelog-2.x
changelog-2.0.xml
In this example, we have a root changelog called changelog-root.xml
which contains includeAll
tags referencing the directories /changelog-1.x
and /changelog-2.x
. The changelogs you modify for a particular release are contained in the nested directories rather than in the overarching /changelog
directory.
A release-oriented changelog structure lets you bundle all content associated with a release in one place. However, this design can be difficult to maintain. If you want to see changes made to a particular object over time, the release-oriented structure requires you to look in multiple locations for that information.
Additionally, having a single changelog per release (even minor release) may lead to more frequent source control merge conflicts. You can solve this by creating multiple changelogs within each release directory, such as /changelog-1.0/indexes.xml
and /changelog-1.0/tables.xml
. However, this structure may be more complicated to navigate and maintain than a pure object-oriented changelog design.
Prepare for deployment
- When you use the
includeAll
tag in your root changelog, you must ensure your file naming convention is consistent. Liquibase sorts pending scripts in alphanumeric order and then deploys them in that order. - You can specify filtering logic to control which changesets in your changelog are executed at runtime by using preconditions, contexts, labels, and other changelog attributes like
runAlways
andrunOnChange
. For more information, see Changelog Attributes. - When you publish updates to your software, it is a best practice to release separate artifacts for each step in your development pipeline, such as artifacts for
dev
,qa
, andprod
. You can easily version artifacts and run checks on them with automation tools. Alternatively or in addition, you can use contexts to group changesets into environments and labels to mark changesets as belong to specific features. Simulating environments with branches in source control is not recommended.
Next steps
- Liquibase Database Tutorials – Configure Liquibase to work with your own database
- Best Practices – Read about best practices to follow with Liquibase