50
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

レイヤードアーキテクチャって何?

50
Last updated at Posted at 2025-12-21

はじめに

こんにちは!any株式会社でエンジニアをしている「しばちゃん @k-shiba-chan 」です!
この記事はanyプロダクトチームAdventCalendar2025、22日目です!

この記事では、レイヤードアーキテクチャについて各層のサンプルコード(TypeScript)を書きながら説明していきます!

きっかけ

プロジェクトのREADME.mdなどを見ると、よく「このプロジェクトでは、レイヤードアーキテクチャを採用しています」みたいに書いてあったりしませんか?
「あー聞いたことはある」「けど、具体的になんだっけ?」みたいな状態から脱却するため、まとめることにしました!

想定読者

  • 「レイヤードアーキテクチャって何?」と聞かれて、うまく説明できない方
  • アーキテクチャという言葉は聞いたことがあるけど、実際どう使うのかピンときていない方
  • 現在レイヤードアーキテクチャに触れ始めたばかりで、各層の役割を整理したい方

なぜアーキテクチャを学ぶ必要があるのか

個人的に新しいプロジェクトに参画したとき、まず理解すべきなのがアーキテクチャ(設計の全体像) だと思っています。

アーキテクチャを知らないと起こること

1. コードを読むのに時間がかかる

  • 処理の流れを追う方法が分からない
  • 「この機能の実装、どこに書いてあるんだろう?」と迷子になる
    例:図書館で料理本を探しているのに、文学コーナーをずっと探しているような状態

2. 実装する場所を間違える

  • 見当違いな場所に実装してしまう
  • 責務が違う層にコードを書いてしまう
  • レビュワーに指摘されて手戻りが発生する

3. チーム開発の効率が悪くなる

  • レビュワーの負担が増える
  • コードレビューのコメントが増えて、マージまで時間がかかる

レイヤードアーキテクチャを学ぶメリット

レイヤードアーキテクチャの大枠を知っておくことで、以下のメリットがあると考えます。

1. コードを読む効率が上がる
「この処理は大体ドメイン層にあるはず」と見当がつくようになる

2. 適切な場所に実装できる
各層の責務を理解しているので、迷わず実装できる

3. プロジェクト特有のアーキテクチャも理解しやすい
基本を知っていれば、応用版のアーキテクチャも理解しやすい

レイヤードアーキテクチャとは

レイヤードアーキテクチャは、ソフトウェアを役割ごとに層(レイヤー)を分けて整理する設計方法です。

役割ごとに層を分けることで、コードを整理し、保守しやすくします。
具体的には、以下の4つの層に分けて実装します。

  • プレゼンテーション層:ユーザーとのやり取り
  • アプリケーション層:アプリケーションとしての処理の流れ
  • ドメイン層:ビジネスのルールや知識
  • インフラ層:データベースや外部サービスとの連携

image.png

プレゼンテーション層

プレゼンテーション層は、ユーザーとアプリケーションの接点となる層です。
Webアプリケーションであれば、APIのエンドポイント(Controller)がこの層に該当します。

責務

  • リクエストを受け取る
  • アプリケーション層(ユースケース)を呼び出す
  • レスポンスを返す
// presentation/todo-controller.ts
import { CreateTodoUseCase } from '../application/create-todo.usecase';

export class TodoController {
  constructor(private readonly createTodoUseCase: CreateTodoUseCase) {}

  async createTodo(requestBody: { title: string }): Promise<{ status: number; body: unknown }> {
    const { title } = requestBody;

    const todo = await this.createTodoUseCase.execute(title);

    return {
      status: 201,
      body: todo,
    };
  }
}

アプリケーション層

アプリケーション層は、ユースケース(何をするか)を実現する層です。
「Todoを作成する」「Todoを完了にする」といった、アプリケーションとしての処理の流れを定義します。

責務

  • ドメイン層のオブジェクトを組み合わせて使う
  • リポジトリを使ってデータを永続化する
  • 「何をするか」を定義する(「どうやるか」はドメイン層に任せる)
// application/create-todo.usecase.ts
import { Todo, TodoRepository } from '../domain/todo';
import { randomUUID } from 'crypto';

export class CreateTodoUseCase {
  constructor(private readonly todoRepository: TodoRepository) {}

  async execute(title: string): Promise<Todo> {
    // ID生成
    const id = randomUUID();
    
    // ドメイン層の Todo を生成
    const todo = new Todo(id, title);

    // リポジトリを使って保存
    await this.todoRepository.save(todo);

    return todo;
  }
}

ドメイン層

ドメイン層は、ビジネスのルールや知識を表現する層です。
「Todoのタイトルは必須」「完了済みのTodoは再度完了にできない」といった、ビジネス上の制約やルールを実装します。

責務

  • ビジネスルールの実装
  • データの整合性を保つ
  • 常に正しい状態のオブジェクトのみ存在させる
// domain/todo.ts
export class Todo {
  constructor(
    public readonly id: string,
    private _title: string,
    private _completed: boolean = false,
  ) {
    this.validateTitle(_title);
  }

  get title(): string {
    return this._title;
  }

  get completed(): boolean {
    return this._completed;
  }

  // ビジネスルール:タイトルの検証
  private validateTitle(title: string): void {
    if (!title || title.trim().length === 0) {
      throw new Error('タイトルは必須です');
    }
    if (title.length > 100) {
      throw new Error('タイトルは100文字以内です');
    }
  }

  // ビジネスルール:完了処理
  complete(): void {
    if (this._completed) {
      throw new Error('既に完了しています');
    }
    this._completed = true;
  }
}

// ドメイン層のリポジトリインターフェース
export interface TodoRepository {
  save(todo: Todo): Promise<void>;
  findById(id: string): Promise<Todo | null>;
}

インフラ層

インフラ層は、データベースや外部サービスとの連携を担当する層です。
ドメイン層で定義されたリポジトリのインターフェースを、実際の技術(PostgreSQL、MySQLなど)を使って実装します。

責務

  • データの永続化(保存・取得)
  • 外部APIとの通信
  • ファイルシステムへのアクセス
// infrastructure/in-memory-todo-repository.ts
import { Todo, TodoRepository } from '../domain/todo';

export class InMemoryTodoRepository implements TodoRepository {
  private store = new Map<string, Todo>();

  async save(todo: Todo): Promise<void> {
    this.store.set(todo.id, todo);
  }

  async findById(id: string): Promise<Todo | null> {
    return this.store.get(id) ?? null;
  }
}

まとめ

この記事では、レイヤードアーキテクチャの基本と各層の役割について、TypeScriptのサンプルコードを交えて解説しました!

各層の役割の振り返り

  • プレゼンテーション層:リクエスト/レスポンスの入出力
  • アプリケーション層:ユースケース(何をするか)の実現
  • ドメイン層:ビジネスルール(どうやるか)の実装
  • インフラ層:データベースなどの技術的な実装

レイヤードアーキテクチャは、ソフトウェア設計の基礎となる考え方です!
基礎を理解しておくことで、ドメイン駆動設計(DDD) などの、より実践的な設計手法も学びやすくなります!
筆者もこれからドメイン駆動設計について学んでいく予定なので、また記事にまとめていきたいと思います!

最後まで読んでいただき、ありがとうございました!

参考文献

50
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?