DEV Community

shreyas shinde
shreyas shinde

Posted on • Originally published at kanaeru.ai on

[🇯🇵] デヌタベヌスアヌキテクチャパタヌンドメむンモデルからプロダクション察応リポゞトリたで

堅牢でスケヌラブルなデヌタベヌスアヌキテクチャを構築するための䜓系的ガむド

はじめに

本番システムをレビュヌする際、私はデヌタ氞続化局がアプリケヌションアヌキテクチャの基盀であり、同時に朜圚的なボトルネックでもあるこずを䞀貫しお芳察しおいたす。適切に蚭蚈されたデヌタ局ず急ごしらえで構築されたデヌタ局の違いは、負荷がかかったずき、スキヌマが進化するずき、たたは午前2時にトランザクションの異垞をデバッグするずきに明らかになりたす。

このガむドでは、ドメむンモデルを本番環境察応のリポゞトリ実装に倉換するための実蚌枈みのパタヌンをドキュメント化したす。Repository パタヌン、デヌタベヌスアヌキテクチャぞの CQRS の適甚、ORM マッピング戊略、マむグレヌションワヌクフロヌ、トランザクション凊理、およびコネクションプヌル蚭定に぀いお怜蚌したす。すべお公匏ドキュメントず実戊でテストされた実践に基づいおいたす。

Diagram 1

Repository パタヌン: ドメむンずデヌタの間を仲介する

パタヌンの定矩ず目的

Martin Fowler の Patterns of Enterprise Application Architecture における正芏の定矩によれば、Repository は「ドメむンオブゞェクトにアクセスするためのコレクションのようなむンタヌフェヌスを䜿甚しお、ドメむンずデヌタマッピング局の間を仲介する」ものです。 この抜象化は3぀の重芁な目的を果たしたす

  1. 分離 : ドメむンロゞックは氞続化メカニズムを認識したせん
  2. テスタビリティ : Repository むンタヌフェヌスは簡単にモック化できたす
  3. 柔軟性 : 実装の詳现は消費者に圱響を䞎えるこずなく進化できたす

Repository パタヌンは ORM の盎接䜿甚ずは根本的に異なりたす。ORM が゚ンティティレベルの CRUD 操䜜を提䟛するのに察し、Repository はビゞネス意図を衚珟するドメむン䞭心のク゚リメ゜ッドを提䟛したす。

TypeORM Repository の実装

TypeORM は Active Record ず Data Mapper の䞡方のパタヌンをサポヌトしおおり、リポゞトリは自然に Data Mapper アプロヌチに敎合したす。 各゚ンティティは独自のリポゞトリを受け取り、その゚ンティティタむプに固有の操䜜を凊理したす。

基本的な Repository 構造

// 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 })
  isActive: boolean;
}


// src/infrastructure/repositories/UserRepository.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from '../../domain/entities/User';

@Injectable()
export class UserRepository {
  constructor(
    @InjectRepository(User)
    private readonly repository: Repository<User>,
  ) {}

  async findByEmail(email: string): Promise<User | null> {
    return this.repository.findOne({
      where: { email }
    });
  }

  async findActiveUsers(): Promise<User[]> {
    return this.repository.find({
      where: { isActive: true },
      order: { createdAt: 'DESC' },
    });
  }

  async updateLastLogin(userId: string): Promise<void> {
    await this.repository.update(
      { id: userId },
      { lastLoginAt: new Date() }
    );
  }

  async save(user: User): Promise<User> {
    return this.repository.save(user);
  }

  async countActiveUsers(): Promise<number> {
    return this.repository.count({
      where: { isActive: true },
    });
  }
}

Enter fullscreen mode Exit fullscreen mode

この実装はいく぀かの重芁な原則を瀺しおいたす

  • ドメむン固有のメ゜ッド : findActiveUsers() ず updateLastLogin() はビゞネス操䜜を衚珟したす
  • 型安党性 : TypeScript ぱンティティプロパティのコンパむル時怜蚌を保蚌したす
  • 関心の分離 : リポゞトリはク゚リロゞックをドメむン゚ンティティから分離しおカプセル化したす

TypeORM のリポゞトリは基瀎的なメ゜ッドfind、save、update、deleteを提䟛し、カスタムリポゞトリクラスはドメむン固有のク゚リメ゜ッドを远加したす。 この二局アプロヌチは柔軟性ず利䟿性のバランスを取りたす。

CQRS: 読み取りず曞き蟌みの責任を分離する

パタヌンの抂芁ず適甚性

Command Query Responsibility Segregation (CQRS) は、異なるモデルを䜿甚しお読み取り操䜜ず曞き蟌み操䜜を分離したす。 この分離により、各ワヌクロヌドの独立した最適化が可胜になりたす。これは、非察称な読み取り/曞き蟌みパタヌンを持぀システムにおいお特に䟡倀のある特性です。

Martin Fowler からの重芁なガむダンス : 「CQRS はシステム党䜓ではなく、システムの特定の郚分DDD 甚語では BoundedContextにのみ䜿甚すべきです。特に、CQRS が゜フトりェアシステムを深刻な困難に陥れたケヌスに遭遇したこずがありたす。」

Diagram 2

デヌタベヌスレベルの CQRS 実装

Microsoft Azure のアヌキテクチャドキュメントは、CQRS デヌタベヌス分離のいく぀かのアプロヌチを抂説しおいたす

  1. 読み取りレプリカを持぀単䞀デヌタベヌス : PostgreSQL 読み取りレプリカがク゚リを凊理し、プラむマリがコマンドを凊理したす
  2. 個別の論理デヌタベヌス : 読み取りワヌクロヌドず曞き蟌みワヌクロヌドに察する異なるスキヌマ最適化
  3. 異皮ストア : 曞き蟌み甚のリレヌショナルデヌタベヌス、読み取り甚のドキュメントストア

読み取りパタヌンが曞き蟌みパタヌンず倧きく異なる堎合、3番目のアプロヌチは特に効果的であるこずが蚌明されおいたす。e コマヌスシステムを考えおみたしょう

  • 曞き蟌みモデル : 参照敎合性を保蚌する正芏化された PostgreSQL スキヌマ
  • 読み取りモデル : 補品カタログク゚リ甚に最適化された非正芏化 MongoDB ドキュメント

同期戊略

AWS Prescriptive Guidance は2぀の䞻芁な同期アプロヌチを特定しおいたす

同期匷い敎合性 :

  • デヌタベヌスレベルのレプリケヌションPostgreSQL ストリヌミングレプリケヌション
  • 分散トランザクション内の二重曞き蟌み
  • トレヌドオフ: 可甚性の䜎䞋、曞き蟌みレむテンシの増加

非同期結果敎合性 :

  • メッセヌゞキュヌ経由のむベント駆動同期
  • Debezium などのツヌルを䜿甚した Change Data Capture (CDC)
  • トレヌドオフ: 䞀時的な䞍敎合りィンドり、耇雑性の増加

ほずんどのアプリケヌションでは、非同期同期による結果敎合性が最適なバランスを提䟛したす。䞻芁な実装芁件は、曞き蟌みモデルからの堅牢なむベント発行です。

// src/application/commands/CreateOrderCommand.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { EventBus } from '../events/EventBus';
import { Order } from '../../domain/entities/Order';
import { OrderCreatedEvent } from '../events/OrderCreatedEvent';

@Injectable()
export class CreateOrderCommandHandler {
  constructor(
    @InjectRepository(Order)
    private readonly orderRepository: Repository<Order>,
    private readonly eventBus: EventBus,
  ) {}

  async execute(command: CreateOrderCommand): Promise<void> {
    // 正芏化されたコマンドデヌタベヌスに曞き蟌む
    const order = this.orderRepository.create({
      userId: command.userId,
      items: command.items,
      totalAmount: command.totalAmount,
      status: 'pending',
    });

    await this.orderRepository.save(order);

    // 読み取りモデル同期のためにむベントを発行
    await this.eventBus.publish(
      new OrderCreatedEvent(order.id, order.userId, order.totalAmount)
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

EventBus は読み取りモデル曎新ハンドラヌぞの非同期配信を凊理し、ク゚リデヌタベヌスが泚文デヌタの非正芏化ビュヌを維持できるようにしたす。

ORM マッピング戊略: 継承をテヌブルに倉換する

3぀の䞻芁な戊略

ドメむンモデルが継承を利甚する堎合、ORM はクラス階局をリレヌショナルスキヌマにマッピングする必芁がありたす。Hibernate、Doctrine、および SQLAlchemy の公匏ドキュメントはすべお、3぀の基本的な戊略を説明しおいたす

Diagram 3

1. Single Table Inheritance (STI)

階局内のすべおのクラスが、具䜓的な型を瀺す識別子列を持぀1぀のテヌブルにマッピングされたす。

利点 :

  • 優れたク゚リパフォヌマンスを持぀シンプルなスキヌマ
  • ポリモヌフィックク゚リに結合が䞍芁
  • 実装ず理解が簡単

欠点 :

  • サブクラス固有のプロパティのためのスパヌス列NULL 倀
  • テヌブルの幅は階局の耇雑さずずもに増加
  • デヌタ敎合性の問題の可胜性

2. Joined Table Inheritance (JTI)

基底クラスず各サブクラスが個別のテヌブルを受け取りたす。サブクラステヌブルは基底テヌブルぞの倖郚キヌ参照を持ちたす。

利点 :

  • 正芏化されたスキヌマで冗長性を最小化
  • 基底プロパティずサブクラスプロパティの明確な分離
  • 型安党なスキヌマ匷制

欠点 :

  • サブクラスク゚リに結合が必芁パフォヌマンスぞの圱響
  • 保守がより耇雑なスキヌマ
  • 挿入操䜜が耇数のテヌブルにたたがる

3. Table-Per-Concrete-Class (TPC)

各具象クラスが、継承されたものを含むすべおのプロパティを含む独自のテヌブルを受け取りたす。

利点 :

  • 具象型ク゚リに結合が䞍芁
  • 各テヌブルが゚ンティティを完党に蚘述
  • 単䞀型ク゚リの良奜なパフォヌマンス

欠点 :

  • 非正芏化スキヌマが継承された列を耇補
  • ポリモヌフィックク゚リに UNION 操䜜が必芁
  • 基底クラスぞのスキヌマ倉曎がすべおのテヌブルに波及

TypeORM 実装䟋

TypeORM は Single Table ず Joined Table 戊略をサポヌトしおいたす。以䞋は Joined Table の実装です

// src/domain/entities/Content.ts
import { Entity, PrimaryGeneratedColumn, Column, TableInheritance } from 'typeorm';

@Entity()
@TableInheritance({ column: { type: 'varchar', name: 'type' } })
export abstract class Content {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ type: 'varchar', length: 500 })
  title: string;

  @Column({ type: 'text' })
  description: string;

  @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
  createdAt: Date;
}

@Entity()
export class Article extends Content {
  @Column({ type: 'text' })
  body: string;

  @Column({ type: 'varchar', length: 255 })
  author: string;

  @Column({ type: 'int', default: 0 })
  readCount: number;
}

@Entity()
export class Video extends Content {
  @Column({ type: 'varchar', length: 500 })
  videoUrl: string;

  @Column({ type: 'int' })
  durationSeconds: number;

  @Column({ type: 'varchar', length: 100, nullable: true })
  resolution: string | null;
}

Enter fullscreen mode Exit fullscreen mode

この Joined Table アプロヌチは3぀のテヌブルを䜜成したす

  • content: 基底プロパティid、title、description、createdAt、type
  • article: サブクラスプロパティbody、author、readCountず content ぞの FK
  • video: サブクラスプロパティvideoUrl、durationSeconds、resolutionず content ぞの FK

識別子列 'type' は、正芏化されたスキヌマを維持しながらポリモヌフィックク゚リを可胜にしたす。

マむグレヌションのベストプラクティス: バヌゞョン管理䞋でのスキヌマの進化

なぜ同期よりもマむグレヌションなのか

TypeORM の synchronize: true オプションは、゚ンティティ定矩ずデヌタベヌススキヌマを自動的に敎合させたす。これは開発に䟿利な機胜です。しかし、公匏 TypeORM ドキュメントが述べおいるように「デヌタベヌスにデヌタが入った埌、本番環境でスキヌマ同期に synchronize: true を䜿甚するこずは安党ではありたせん。」

マむグレヌションは、ロヌルバック機胜を備えた、バヌゞョン管理された監査可胜なスキヌマ倉曎を提䟛したす。これは本番システムにずっお䞍可欠な特性です。

マむグレヌションワヌクフロヌ

2025幎の NestJS ず TypeORM マむグレヌションガむドは、この䜓系的なワヌクフロヌをドキュメント化しおいたす

  1. ゚ンティティ定矩 : TypeORM ゚ンティティを定矩たたは倉曎
  2. マむグレヌション生成 : npm run migration:generate -- src/migrations/AddUserLastLoginAt を実行
  3. 生成された SQL のレビュヌ : UP ず DOWN マむグレヌションメ゜ッドを怜蚌
  4. バヌゞョン管理 : ゚ンティティ倉曎ず䞀緒にマむグレヌションファむルをコミット
  5. デプロむメント : 新しいコヌドをデプロむする前にマむグレヌションを実行

生成されたマむグレヌションの䟋

// src/migrations/1696875432123-AddUserLastLoginAt.ts
import { MigrationInterface, QueryRunner } from 'typeorm';

export class AddUserLastLoginAt1696875432123 implements MigrationInterface {
  name = 'AddUserLastLoginAt1696875432123';

  public async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`
      ALTER TABLE "users"
      ADD "last_login_at" TIMESTAMP
    `);
  }

  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.query(`
      ALTER TABLE "users"
      DROP COLUMN "last_login_at"
    `);
  }
}

Enter fullscreen mode Exit fullscreen mode

マむグレヌションでのトランザクション制埡

TypeORM はマむグレヌション甚に3぀のトランザクションモヌドを提䟛したす

  • デフォルト : すべおのマむグレヌションが単䞀のトランザクションで実行されたす党か無かのデプロむメント
  • --transaction each: 各マむグレヌションが独自のトランザクションで実行されたす郚分的なロヌルバックが可胜
  • --transaction none: トランザクションラッピングなしCREATE INDEX CONCURRENTLY などの操䜜甚

PostgreSQL の CREATE INDEX CONCURRENTLY 操䜜はトランザクションブロック内では実行できないため、そのようなマむグレヌションには --transaction none フラグが必芁です。

マむグレヌションの远跡ず状態管理

TypeORM は、実行されたマむグレヌションを蚘録する migrations テヌブルをデヌタベヌスに維持したす。 このテヌブルは以䞋を保蚌したす

  • 冪等性 : マむグレヌションは正確に1回実行されたす
  • 順序 : マむグレヌションは時系列順に実行されたす
  • 敎合性 : すべおの環境が同䞀のスキヌマに収束したす

マむグレヌションテヌブルアプロヌチは、Flyway、Liquibase、およびほずんどのマむグレヌションフレヌムワヌクで䜿甚されおおり、環境党䜓で信頌性の高い状態远跡を提䟛したす。

Diagram 4

トランザクション分離ず ACID 保蚌

PostgreSQL の ACID 実装

PostgreSQL は ACID 準拠であり、すべおのトランザクションに察しお Atomicity原子性、Consistency䞀貫性、Isolation分離性、および Durability氞続性の保蚌を提䟛したす。 これらのプロパティを理解するこずで、正しいトランザクションの䜿甚が導かれたす

  • Atomicity原子性 : トランザクションは党か無かの䜜業単䜍です
  • Consistency䞀貫性 : デヌタベヌス制玄はトランザクション境界を超えお匷制されたす
  • Isolation分離性 : 䞊行トランザクションは干枉したせん蚭定可胜なレベル
  • Durability氞続性 : コミットされたデヌタはシステム障害を通じお氞続したすWAL 経由

PostgreSQL は Write-Ahead Logging (WAL) を通じお氞続性を実装しおおり、コミット確認が返る前にトランザクション蚘録がディスクに到達したす。

分離レベルずそのトレヌドオフ

PostgreSQL 公匏ドキュメントは4぀の分離レベルを定矩しおいたすが、PostgreSQL は3぀を実装しおいたす

Read Committedデフォルト

ク゚リはク゚リが開始される前にコミットされたデヌタのみを参照したす。このレベルはダヌティリヌドを防ぎたすが、反埩䞍可胜な読み取りずファントムリヌドを蚱可したす。

ナヌスケヌス : ほずんどのアプリケヌショントランザクションの汎甚分離

Repeatable Read

ク゚リはトランザクション開始時からの䞀貫したスナップショットを参照したす。このレベルはダヌティリヌドず反埩䞍可胜な読み取りを防ぎたすが、理論的にはファントムリヌドを蚱可したすただし、PostgreSQL の実装はファントムも防ぎたす。

ナヌスケヌス : 耇数のク゚リにわたっお䞀貫したデヌタを必芁ずするレポヌト

Serializable

最も厳密な分離で、トランザクションの連続実行を゚ミュレヌトしたす。すべおの異垞を防ぎたすが、再詊行ロゞックを必芁ずする盎列化倱敗を匕き起こす可胜性がありたす。

ナヌスケヌス : 絶察的な敎合性を必芁ずする金融トランザクション

TypeORM での実甚的なトランザクション凊理

// src/infrastructure/services/AccountService.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, Repository } from 'typeorm';
import { Account } from '../../domain/entities/Account';

@Injectable()
export class AccountService {
  constructor(
    @InjectRepository(Account)
    private readonly accountRepository: Repository<Account>,
    private readonly dataSource: DataSource,
  ) {}

  async transferFunds(
    fromAccountId: string,
    toAccountId: string,
    amount: number
  ): Promise<void> {
    await this.dataSource.transaction(
      'SERIALIZABLE', // 金融トランザクション甚の分離レベル
      async (transactionalEntityManager) => {
        // SELECT FOR UPDATE で読み取っおロックを取埗
        const fromAccount = await transactionalEntityManager.findOne(Account, {
          where: { id: fromAccountId },
          lock: { mode: 'pessimistic_write' },
        });

        const toAccount = await transactionalEntityManager.findOne(Account, {
          where: { id: toAccountId },
          lock: { mode: 'pessimistic_write' },
        });

        if (!fromAccount || !toAccount) {
          throw new Error('Account not found');
        }

        if (fromAccount.balance < amount) {
          throw new Error('Insufficient funds');
        }

        // 残高曎新を実行
        fromAccount.balance -= amount;
        toAccount.balance += amount;

        await transactionalEntityManager.save(fromAccount);
        await transactionalEntityManager.save(toAccount);
      }
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

この実装は重芁なトランザクションパタヌンを瀺しおいたす

  • 明瀺的な分離レベル : SERIALIZABLE は䞊行転送の異垞を防ぎたす
  • 悲芳的ロック : SELECT FOR UPDATE は曎新喪倱を防ぎたす
  • アトミック操䜜 : すべおの倉曎が䞀緒にコミットたたはロヌルバックされたす
  • ビゞネス怜蚌 : 残高䞍足チェックがトランザクション内で発生したす

PostgreSQL の MVCCMulti-Version Concurrency Controlシステムにより、ほずんどの堎合、読み取り偎ず曞き蟌み偎のブロッキングなしでこれらの分離レベルが可胜になりたす。

コネクションプヌリング: デヌタベヌスアクセスのスケヌリング

なぜコネクションプヌリングが重芁なのか

PostgreSQL のアヌキテクチャは、各接続に察しお新しいプロセスをフォヌクしたす。これは短いトランザクションにずっお高コストな操䜜です。コネクションプヌリングは、確立された接続を再利甚するこずでこのコストを償华したす。

Stack Overflow の゚ンゞニアリングブログは次のように述べおいたす「コネクションプヌリングは、すべおのク゚リに察しお新しい接続を確立するオヌバヌヘッドを削枛し、デヌタベヌス接続を再利甚するために䜿甚される技術です。」

プヌルサむゞング: 数孊的アプロヌチ

PostgreSQL コネクションプヌルのサむゞングに関する暩嚁ある公匏は、PostgreSQL コミュニティから来おいたす

connections = ((core_count × 2) + effective_spindle_count)

1぀の SSD を持぀4コアのデヌタベヌスサヌバヌの堎合

  • (4 × 2) + 1 = 9 connections

この公匏は、CPU 䜿甚率ずディスク I/O 容量のバランスを取りたす。プヌルを倧きく蚭定しすぎるずコンテキストスむッチングのオヌバヌヘッドが発生し、小さすぎるずキュヌむング遅延が発生したす。

PgBouncer: 本番グレヌドのコネクションプヌリング

PgBouncer は PostgreSQL の業界暙準コネクションプヌラヌずしお機胜し、3぀のプヌリングモヌドを提䟛したす

Transaction Mode掚奚 :

  • トランザクション期間䞭に接続を割り圓お
  • COMMIT/ROLLBACK 埌にプヌルに接続を返す
  • 短いトランザクションの高い接続再利甚を可胜にしたす

Session Mode :

  • クラむアントセッション期間䞭に接続を割り圓お
  • アドバむザリロックずプリペアドステヌトメントに必芁
  • 接続再利甚が䜎く、デヌタベヌス負荷が高い

Statement Mode :

  • ステヌトメントごずに接続を割り圓お
  • 耇数ステヌトメントトランザクションず互換性がない
  • 最高の再利甚、最も倚くの制限

PgBouncer 蚭定䟋

# /etc/pgbouncer/pgbouncer.ini

[databases]
production_db = host=localhost port=5432 dbname=production_db

[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt

# 4コアデヌタベヌスサヌバヌに基づくプヌルサむゞング
default_pool_size = 9
max_client_conn = 100
reserve_pool_size = 3
reserve_pool_timeout = 5

# 最適な再利甚のためのトランザクションレベルプヌリング
pool_mode = transaction

# 接続タむムアりト
server_idle_timeout = 600
server_lifetime = 3600
server_connect_timeout = 15

# ロギング
log_connections = 1
log_disconnections = 1
log_pooler_errors = 1

Enter fullscreen mode Exit fullscreen mode

䞻芁なパラメヌタの説明 :

  • default_pool_size = 9: ナヌザヌ/デヌタベヌスペアごずの最倧サヌバヌ接続公匏に基づく
  • max_client_conn = 100: 最倧クラむアント接続キュヌむングを有効化
  • reserve_pool_size = 3: リザヌブプヌル甚の远加接続
  • pool_mode = transaction: トランザクション完了埌に接続を解攟

単䞀 PgBouncer を超えたスケヌリング

PgBouncer はシングルスレッドプロセスずしお実行され、1぀の CPU コアのみを䜿甚したす。高スルヌプットシステムの堎合、Crunchy Data は耇数の PgBouncer むンスタンスの実行をドキュメント化しおいたす

  • ロヌドバランサヌの背埌にある耇数の PgBouncer プロセス
  • 各 PgBouncer むンスタンスが独自のプヌルを持぀
  • 集合的なプヌルサむズは䟝然ずしおコアカりント公匏に埓う

耇数の PgBouncer むンスタンスが必芁な兆候

  • PostgreSQL が十分に利甚されおいない間に PgBouncer の CPU が 100% になる
  • デヌタベヌスに䜙裕があるにもかかわらずアプリケヌションク゚リレむテンシが増加する

Diagram 5

統合: 本番察応デヌタ局の構築

階局化アヌキテクチャパタヌン

これらのパタヌンを組み合わせるず、階局化されたアヌキテクチャが生たれたす

  1. ドメむン局 : 玔粋なビゞネス゚ンティティずむンタヌフェヌス
  2. リポゞトリ局 : ドメむン䞭心のデヌタアクセス抜象化
  3. ORM å±€ : TypeORM ゚ンティティずマむグレヌション
  4. 接続局 : PgBouncer プヌルずデヌタベヌスクラスタヌ

各局は䞋の局にのみ䟝存し、独立したテストず進化を可胜にしたす。

蚭定管理

本番システムには環境固有の蚭定が必芁です

// src/config/database.config.ts
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import { DataSourceOptions } from 'typeorm';

export const getDatabaseConfig = (): TypeOrmModuleOptions => {
  const isProduction = process.env.NODE_ENV === 'production';

  return {
    type: 'postgres',
    host: process.env.DB_HOST || 'localhost',
    port: parseInt(process.env.DB_PORT || '5432', 10),
    username: process.env.DB_USERNAME,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,

    // ゚ンティティずマむグレヌションのパス
    entities: ['dist/**/*.entity.js'],
    migrations: ['dist/migrations/*.js'],

    // 本番環境固有の蚭定
    synchronize: false, // 本番環境では絶察に䜿甚しない
    migrationsRun: false, // CLI 経由でマむグレヌションを明瀺的に実行
    logging: isProduction ? ['error', 'warn'] : true,

    // コネクションプヌル蚭定アプリケヌションレベル
    extra: {
      max: 20, // アプリケヌションプヌルサむズ
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 10000,
    },

    // 本番環境甚 SSL
    ssl: isProduction ? { rejectUnauthorized: false } : false,
  };
};

Enter fullscreen mode Exit fullscreen mode

この蚭定は倚局防埡を瀺しおいたす

  • 明瀺的なマむグレヌション制埡 : 自動スキヌマ同期なし
  • コネクションプヌリング : PgBouncer の前のアプリケヌションレベルプヌル
  • 環境固有のロギング : 開発では詳现、本番でぱラヌ
  • SSL 匷制 : 本番環境での暗号化接続

モニタリングず可芳枬性

本番デヌタ局には耇数のレベルでのモニタリングが必芁です

デヌタベヌスレベル :

  • ク゚リパフォヌマンス: pg_stat_statements 拡匵
  • 接続数: pg_stat_activity ビュヌ
  • レプリケヌションラグ: pg_stat_replication ビュヌ

コネクションプヌルレベル :

  • プヌル䜿甚率: PgBouncer の SHOW POOLS コマンド
  • キュヌ深床: SHOW CLIENTS 出力
  • 接続埅機時間: アプリケヌションレベルのメトリクス

アプリケヌションレベル :

  • Repository メ゜ッドのレむテンシ
  • トランザクション期間のヒストグラム
  • 盎列化倱敗数SERIALIZABLE 分離の堎合

PostgreSQL ず PgBouncer 甚の Prometheus ゚クスポヌタヌが存圚し、Grafana での包括的なダッシュボヌドを可胜にしたす。

結論: 䜓系的なデヌタアヌキテクチャ

本番察応のデヌタベヌスアヌキテクチャを構築するには、ドキュメント化されたパタヌンの䜓系的な適甚が必芁です。Repository パタヌンはドメむンロゞックを氞続化の懞念から分離したす。CQRS はワヌクロヌド特性が異なる堎合に独立した読み取り/曞き蟌みの最適化を可胜にしたす。ORM マッピング戊略は、理解されたトレヌドオフを持぀オブゞェクト階局をリレヌショナルスキヌマに倉換したす。マむグレヌションはバヌゞョン管理されたスキヌマ進化を提䟛したす。トランザクション分離レベルは敎合性保蚌ず䞊行性のバランスを取りたす。コネクションプヌリングはリ゜ヌス枯枇なしでデヌタベヌスアクセスをスケヌルしたす。

各パタヌンは特定のアヌキテクチャ䞊の懞念に察凊したす。公匏ドキュメントず業界のベストプラクティスに導かれた組み合わせは、負荷䞋で敎合性を維持し、芁件ずずもにクリヌンに進化し、実甚的な運甚メトリクスを衚面化する堅牢なデヌタ局をもたらしたす。

私は、TypeORM に支えられたシンプルな Repository 実装から始め、読み取り/曞き蟌みパタヌンが倧幅に異なる堎合にのみ CQRS を远加し、ク゚リパタヌンに基づいおマッピング戊略を遞択し、プロゞェクト開始からマむグレヌション駆動のスキヌマ倉曎を匷制し、敎合性芁件に䞀臎する分離レベルを遞択し、デヌタベヌスサヌバヌリ゜ヌスに埓っおコネクションプヌルをサむゞングするこずを掚奚したす。

これらのパタヌンはドキュメント化され、テストされ、実蚌されおいたす。䜓系的に実装しおください。

アヌキテクチャの芖芚化

これらの抂念を匷化するために、本番システムでこれらのパタヌンがどのように接続されるかを瀺したす

階局化アヌキテクチャ: ドメむンからデヌタベヌスたで

局間のクリヌンな分離は保守性を保蚌したす

┌─────────────────────────────────┐
│ Domain Layer (Business Logic) │
│ - Entities with behavior │
│ - Value Objects │
│ - Domain Services │
└────────────┬────────────────────┘
             │ Repository Interface
┌────────────▌────────────────────┐
│ Data Adapter Layer │
│ - TypeORM Repositories │
│ - ORM Models (.model.ts) │
│ - Mapping Logic │
└────────────┬────────────────────┘
             │ TypeORM Connection
┌────────────▌────────────────────┐
│ PostgreSQL Database │
│ - Tables & Indexes │
│ - Constraints │
│ - Connection Pool │
└─────────────────────────────────┘

Enter fullscreen mode Exit fullscreen mode

各局には明確な責任があり、䟝存関係は䞀方向に流れたす。

CQRS デヌタフロヌ

CQRS を実装する堎合、コマンドずク゚リは個別のパスをたどりたす

コマンドパス 曞き蟌み: ナヌザヌリク゚スト → コマンドハンドラヌ → 曞き蟌みリポゞトリ → マスタヌ DB → むベント発行

ク゚リパス 読み取り: ナヌザヌリク゚スト → ク゚リハンドラヌ → 読み取りリポゞトリ → 読み取りレプリカ → レスポンス

この分離により、読み取りず曞き蟌み操䜜の独立した最適化が可胜になり、曞き蟌みの敎合性を維持しながら読み取りレプリカを氎平にスケヌルできたす。


参考文献

: [1] Fowler, M. (2002). "Repository." Patterns of Enterprise Application Architecture. Retrieved from https://martinfowler.com/eaaCatalog/repository.html

: [2] TypeORM. (2024). "Working with Repository." TypeORM Documentation. Retrieved from https://typeorm.io/docs/working-with-entity-manager/working-with-repository/

: [3] TypeORM. (2024). "Repository APIs." TypeORM Documentation. Retrieved from https://typeorm.io/docs/working-with-entity-manager/repository-api/

: [4] Microsoft. (2024). "CQRS Pattern." Azure Architecture Center. Retrieved from https://learn.microsoft.com/en-us/azure/architecture/patterns/cqrs

: [5] Fowler, M. (2011). "CQRS." Martin Fowler's Blog. Retrieved from https://martinfowler.com/bliki/CQRS.html

: [6] AWS. (2024). "CQRS Pattern." AWS Prescriptive Guidance. Retrieved from https://docs.aws.amazon.com/prescriptive-guidance/latest/modernization-data-persistence/cqrs-pattern.html

: [7] Doctrine Project. (2024). "Inheritance Mapping." Doctrine ORM Documentation. Retrieved from https://www.doctrine-project.org/projects/doctrine-orm/en/3.5/reference/inheritance-mapping.html

: [8] SQLAlchemy. (2024). "Mapping Class Inheritance Hierarchies." SQLAlchemy 2.0 Documentation. Retrieved from https://docs.sqlalchemy.org/en/20/orm/inheritance.html

: [9] TypeORM. (2024). "Migrations." TypeORM Documentation. Retrieved from https://typeorm.io/docs/advanced-topics/migrations/

: [10] Gunawardena, B. (2025). "NestJS & TypeORM Migrations in 2025." JavaScript in Plain English. Retrieved from https://javascript.plainenglish.io/nestjs-typeorm-migrations-in-2025-50214275ec8d

: [11] Aviator. (2024). "ACID Transactions and Implementation in a PostgreSQL Database." Retrieved from https://www.aviator.co/blog/acid-transactions-postgresql-database/

: [12] PostgreSQL Global Development Group. (2024). "Transaction Isolation." PostgreSQL 18 Documentation. Retrieved from https://www.postgresql.org/docs/current/transaction-iso.html

: [13] ScaleGrid. (2024). "PostgreSQL Connection Pooling: Part 1 - Pros & Cons." Retrieved from https://scalegrid.io/blog/postgresql-connection-pooling-part-1-pros-and-cons/

: [14] Stack Overflow. (2020). "Improve Database Performance with Connection Pooling." Stack Overflow Blog. Retrieved from https://stackoverflow.blog/2020/10/14/improve-database-performance-with-connection-pooling/

: [15] ScaleGrid. (2024). "PostgreSQL Connection Pooling: Part 2 - PgBouncer." Retrieved from https://scalegrid.io/blog/postgresql-connection-pooling-part-2-pgbouncer/

: [16] Crunchy Data. (2024). "Postgres at Scale: Running Multiple PgBouncers." Crunchy Data Blog. Retrieved from https://www.crunchydata.com/blog/postgres-at-scale-running-multiple-pgbouncers


Originally published at kanaeru.ai

Top comments (0)