How the Factory Method Could Simplify Your Database Migration
Migrating from one database to another is one of the riskiest operations you can perform in a live system. Schemas drift, query engines…
How the Factory Method Could Simplify Your Database Migration

Migrating from one database to another is one of the riskiest operations you can perform in a live system. Schemas drift, query engines behave differently, performance varies — and worst of all, the migration can rarely happen in a single clean cut. In many real-world scenarios, both databases must remain active during a transition period.
This is exactly the challenge we faced recently. Instead of scattering if(useNewDb) flags across the codebase or rewriting half the application, we solved the problem cleanly using one of the most foundational patterns in software engineering:
👉 The Factory Method Pattern. A simple, elegant approach that let us switch databases seamlessly without breaking the application.
In this article, you’ll learn:
- What the Factory Method Pattern is (in plain English)
- Why it’s the perfect fit for multi-database migrations
- A practical, real-world implementation
- How we ran two databases in parallel with zero code duplication
- What we learned from the experience
What Is the Factory Method?
The Factory Method is a *creational design pattern* that defines a common interface for creating objects while allowing the actual implementation to be chosen at runtime.
In simple terms:
Instead of instantiating objects directly, you delegate object creation to a factory method that chooses the correct class.
This gives you flexibility: your application logic never needs to know which database implementation is being used. When the implementation changes — such as switching from MongoDB to PostgreSQL — you only update the env variable (aka feature flag), not the business logic.
Our Real Use Case: Migrating From MongoDB to PostgreSQL
We were migrating our platform from Database A (MongoDB) to Database B (PostgreSQL).
The challenges:
- We needed both databases running in parallel during a phased migration
- The rest of the application must stay completely unaware of which database was serving requests
- Database-specific code had to remain isolated
- Switching between databases needed to be a simple config change — not a refactor
The Factory Method Pattern became the ideal solution.
Designing the Abstraction Layer
We started by defining shared interface as contract that both database clients would implement:
export interface DatabaseClient {
findUser(id: string): Promise<User>;
saveUser(user: User): Promise<void>;
deleteUser(id: string): Promise<void>;
}
Our application now depends only on this interface — not on MongoDB, not on PostgreSQL.
Implementing the Concrete Database Clients
MongoDB Implementation
export class MongoDatabaseClient implements DatabaseClient {
async findUser(id: string) { /* Mongo code */ }
async saveUser(user: User) { /* Mongo code */ }
async deleteUser(id: string) { /* Mongo code */ }
}
PostgreSQL Implementation
export class PostgresDatabaseClient implements DatabaseClient {
async findUser(id: string) { /* SQL code */ }
async saveUser(user: User) { /* SQL code */ }
async deleteUser(id: string) { /* SQL code */ }
}
Now we have two interchangeable implementations behind a single interface.
Notice: your business logic does not care what happens inside. That’s the power of abstraction.

if you know, you know
The Factory Method
Now we need to proxy all the requests to the factory, which delegates the task for the specific client based on our feature flag.
export class DatabaseFactory {
static createClient(): DatabaseClient {
const dbType = process.env.DB_TYPE;
if (dbType === "mongo") return new MongoDatabaseClient();
if (dbType === "postgres") return new PostgresDatabaseClient();
throw new Error(`Unknown DB type: ${dbType}`);
}
}
Supporting seamless multi-database migrations
You can even support dual writes or shadow reads during migration:
export class DualWriteClient implements DatabaseClient {
constructor(
private mongoDatabaseClient: MongoDatabaseClient,
private postgresDatabaseClient: PostgresDatabaseClient
) {}
async saveUser(user: User) {
await this.mongoDatabaseClient.saveUser(user);
await this.postgresDatabaseClient.saveUser(user);
}
async findUser(id: string) {
return this.mongoDatabaseClient.findUser(id);
}
}
And update the factory:
if (dbType === "dual") {
return new DualWriteClient(
new MongoDatabaseClient(),
new PostgresDatabaseClient()
);
}
With this, switching database modes — old, new, dual — became a one-line configuration change.
Using the Factory in the Application
Instead of manually creating DB clients, services simply call:
const db = DatabaseFactory.createClient();
const user = await db.findUser("123");
await db.saveUser(user);
No matter how many times we change databases in the future, none of this code changes.
Why the Factory Method Was a Game Changer
✔ Zero code changes in business logic
We updated the database layer without touching services, controllers, or workflows.
✔ Parallel database execution
Dual-write and shadow-read modes were trivial to add.
✔ Safe switchovers
If something broke, we reverted by changing one environment variable.
✔ Cleaner, maintainable architecture
The factory became the central decision point, avoiding scattered conditionals.
✔ Perfect for long, risky migrations
The pattern let us move gradually with confidence.
Final Thoughts
Database migrations are notoriously complex, but the Factory Method Pattern make the migration easier:
- Safe
- Flexible
- Maintainable
- Reversible
- Transparent to the rest of the codebase
If you’re ever in a situation where the underlying implementation may change — databases, storage systems, APIs, messaging providers — the Factory Method Pattern is one of the cleanest and most reliable approaches you can take.
A small amount of good design upfront can save you from massive refactoring later.
Happy coding y’all ✌️
메타데이터
- post_id
- 19f30f422de0
- slug
- how-the-factory-method-could-simplify-your-database-migration-19f30f422de0
- url
- https://medium.com/softwarecraft-mastery/how-the-factory-method-could-simplify-your-database-migration-19f30f422de0
- canonical_url
- https://medium.com/softwarecraft-mastery/how-the-factory-method-could-simplify-your-database-migration-19f30f422de0
- author_url
- https://medium.com/@saif-hasnaoui
- status
- ok
- fetched_at
- 2026-06-15 20:49:13