Database Architecture Patterns: From Domain Models to Production-Ready Repositories
A systematic guide to building robust, scalable database architectures
Introduction
When I review production systems, I consistently observe that the data persistence layer serves as both the foundation and potential bottleneck of application architecture. The difference between a well-architected data layer and a hastily constructed one becomes evident under load, during schema evolution, or when debugging transaction anomalies at 2 AM.
This guide documents proven patterns for transforming domain models into production-ready repository implementations. We'll examine the Repository pattern, CQRS application to database architecture, ORM mapping strategies, migration workflows, transaction handling, and connection pool configuration—all grounded in official documentation and battle-tested practices.
Architecture Overview
Each layer depends only on layers below, enabling isolated testing and independent evolution.
The Repository Pattern: Mediating Between Domains and Data
Pattern Definition and Purpose
According to Martin Fowler's canonical definition in Patterns of Enterprise Application Architecture, a Repository "mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects."[^1] This abstraction serves three critical purposes:
- Isolation: Domain logic remains unaware of persistence mechanisms
- Testability: Repository interfaces enable straightforward mocking
- Flexibility: Implementation details can evolve without affecting consumers
The Repository pattern differs fundamentally from direct ORM usage. While an ORM provides entity-level CRUD operations, a Repository offers domain-centric query methods that express business intent.
TypeORM Repository Implementation
TypeORM supports both Active Record and Data Mapper patterns, with repositories naturally aligning with the Data Mapper approach.[^2] Each entity receives its own repository, handling operations specific to that entity type.
Basic Repository Structure
// src/domain/entities/User.ts
import { Entity, PrimaryGeneratedColumn, Column, Index } from 'typeorm';
@Entity('users')
@Index(['email'], { unique: true })
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ type: 'varchar', length: 255 })
email: string;
@Column({ type: 'varchar', length: 255 })
name: string;
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
@Column({ type: 'timestamp', nullable: true })
lastLoginAt: Date | null;
@Column({ type: 'boolean', default: true })
isActiv