クリーンアーキテクチャでWebアプリ開発|中小企業が実践できる設計入門

kento_morota 15分で読めます

クリーンアーキテクチャとは?Webアプリ開発での基本

「Excelでの管理が限界に来ている」「外注したシステムが使いにくい」「担当者が辞めたらシステムが分からなくなる」——中小企業のIT担当者が抱えるこうした課題を、クリーンアーキテクチャは解決できます。

クリーンアーキテクチャとは、ソフトウェア設計の第一人者ロバート・C・マーチンが提唱した設計思想です。その核心は「ビジネスロジックを中心に置く」という考え方にあります。

従来のシステム開発では、使用する技術やフレームワークを先に決めて、それに合わせてシステムを作ることが一般的でした。しかし、クリーンアーキテクチャではまず「ビジネスで何をしたいか」を明確にし、技術はその後に選ぶという順序を重視します。これにより、5年後、10年後も使い続けられるシステムを作ることができます。

従来のWeb開発との違い

従来の「フレームワーク中心」の開発には、以下の課題がありました。

  • フレームワークのバージョンアップに追従できず、古いシステムになる
  • 別のフレームワークに移行しようとすると、全面的な作り直しが必要
  • ビジネスロジックとフレームワークのコードが混在し、どこに何があるか分かりにくい
  • テストが難しく、変更のたびに不具合が発生するリスクが高い

一方、クリーンアーキテクチャでは、ビジネスロジックが独立しているため、フレームワークを変えてもコアな部分は再利用可能です。コードの役割が明確に分かれているため、新しい担当者でも理解しやすく、段階的な改善が可能です。

中小企業にこそ有効な理由

中小企業だからこそクリーンアーキテクチャが役立つ理由があります。

属人化を防げる:システムを理解している人が一人だけというケースは少なくありません。コードの役割が明確に分かれていれば、引き継ぎや新人教育がスムーズになります。

無駄な開発コストを削減できる:「とりあえず動けばいい」という開発を続けると、後から修正するたびに大きなコストがかかります。最初から整理された設計にしておけば、技術的負債を減らし、長期的なコスト削減につながります。

自社に合ったシステムを作れる:高額なSaaSを導入したものの、自社の業務に合わなかったという経験はありませんか?クリーンアーキテクチャで自社開発すれば、本当に必要な機能だけを持った「ちょうどいい」システムを作れます。

適しているWebアプリの種類

クリーンアーキテクチャは万能ではありません。以下のようなWebアプリに向いています。

向いているケース:
- 顧客管理、案件管理、予約システムなど、長期的に使い続ける業務システム
- 見積計算、在庫管理、ワークフローなど、複雑なビジネスルールを持つアプリ
- 小さく始めて、徐々に拡張していく予定のシステム
- Web、スマホアプリ、APIなど、複数のチャネルで使うアプリ

向いていないケース:
- 検証目的で1〜2ヶ月だけ使うプロトタイプ
- 情報表示だけの静的なサイト
- 単純なCRUD(登録・読取・更新・削除)だけのシステム

重要なのは、「将来的に変更や拡張が必要になるか」という視点です。1年以上使い続ける予定があり、機能追加や改善を行う可能性があるなら、検討する価値があります。

クリーンアーキテクチャの4層構造

クリーンアーキテクチャの最大の特徴は、システムを4つの層に分けて整理するという点です。依存の方向が一方向(外側から内側へ)になるように設計することで、「どこに何を書けばいいか」が明確になります。

外側:フレームワーク・ドライバー層(Web、DB、外部API)
 ↓
  :インターフェースアダプター層(コントローラー、プレゼンター)
 ↓
  :ユースケース層(アプリケーション固有の処理)
 ↓
内側:エンティティ層(ビジネスルールの中核)

外側の層は内側の層を知っていますが、内側の層は外側の層を知りません。これにより、ビジネスルールが技術的な詳細に影響されない設計が実現します。

エンティティ層:ビジネスルールの中核

エンティティ層は、最も内側にある、システムの心臓部です。ここには、ビジネスの本質的なルールやデータ構造を定義します。技術的な詳細(DBやフレームワーク)を一切知らないため、どんな技術を使っても変わりません。

ECサイトの「注文」エンティティの例:

class Order {
  private items: OrderItem[];
  private totalAmount: number;

  // ビジネスルール:注文金額の計算
  calculateTotal(): number {
    return this.items.reduce((sum, item) => 
      sum + (item.price * item.quantity), 0);
  }

  // ビジネスルール:割引の適用
  applyDiscount(rate: number): void {
    if (rate < 0 || rate > 1) {
      throw new Error('割引率は0〜1の範囲で指定してください');
    }
    this.totalAmount *= (1 - rate);
  }
}

中小企業では、顧客ランクによる割引率の計算、在庫の引当ルール、見積の有効期限判定などをエンティティ層に集約します。

ユースケース層:業務フローの実装

ユースケース層は、「このアプリケーションで何ができるか」を定義する層です。エンティティ層のビジネスルールを組み合わせて、具体的な機能を実現します。

「注文を作成する」ユースケースの例:

class CreateOrderUseCase {
  async execute(input: CreateOrderInput): Promise<CreateOrderOutput> {
    // 1. 顧客情報の取得
    const customer = await this.customerRepository.findById(input.customerId);

    // 2. 商品情報の取得と在庫確認
    const products = await this.productRepository.findByIds(input.productIds);

    // 3. 注文エンティティの作成
    const order = new Order({ customer, items: this.createOrderItems(products) });

    // 4. 顧客ランクに応じた割引適用
    if (customer.isVip()) {
      order.applyDiscount(0.1);
    }

    // 5. 注文の保存
    await this.orderRepository.save(order);

    return { orderId: order.id, totalAmount: order.totalAmount };
  }
}

Webからの入力か、スマホアプリからの入力かは関係なく、「注文を作成する」という処理の本質だけを表現します。

インターフェースアダプター層:外部との橋渡し

インターフェースアダプター層は、外部(Webやデータベース)とユースケースをつなぐ層です。外部からのデータをユースケースが理解できる形に変換し、ユースケースの結果を外部が期待する形に変換します。

注文作成のコントローラーの例:

class OrderController {
  async createOrder(req: Request, res: Response) {
    try {
      // HTTPリクエストからデータを取り出す
      const input: CreateOrderInput = {
        customerId: req.body.customerId,
        productIds: req.body.products.map(p => p.id)
      };

      // ユースケースを実行
      const output = await this.createOrderUseCase.execute(input);

      // 結果をHTTPレスポンスに変換
      res.status(201).json({ success: true, data: output });
    } catch (error) {
      res.status(400).json({ success: false, message: error.message });
    }
  }
}

この層のおかげで、ユースケースはHTTPやJSONを知らなくても済みます。REST APIからGraphQLに変更する場合も、ユースケース層は変更不要です。

フレームワーク・ドライバー層:技術の実装

フレームワーク・ドライバー層は、最も外側の層で、具体的な技術を使う場所です。Webフレームワーク、データベース、外部APIなどを実装します。

Prismaを使ったリポジトリの実装例:

class PrismaOrderRepository implements OrderRepository {
  async save(order: Order): Promise<void> {
    await this.prisma.order.create({
      data: {
        id: order.id,
        customerId: order.customerId,
        totalAmount: order.totalAmount,
        items: { create: order.items.map(item => ({
          productId: item.productId,
          quantity: item.quantity
        }))}
      }
    });
  }
}

この層は技術的な詳細が集まる場所です。フレームワークのバージョンアップや、データベースの変更があっても、この層だけを修正すれば対応できます。

クリーンアーキテクチャでWebアプリを開発するメリット

テストしやすく品質が安定する

各層が独立しているため、自動テストが書きやすい構造になっています。ビジネスロジックは、データベースやWebフレームワークなしでテストできるため、テストの実行速度が速く(数秒で数百のテストが完了)、変更のたびに全ての機能を手動で確認する必要がありません。

describe('CreateOrderUseCase', () => {
  it('VIP顧客には10%の割引が適用される', async () => {
    const mockCustomerRepo = {
      findById: jest.fn().mockResolvedValue(new Customer({ isVip: true }))
    };

    const useCase = new CreateOrderUseCase(mockCustomerRepo);
    const result = await useCase.execute({ customerId: '1', productIds: ['1'] });

    expect(result.totalAmount).toBe(900); // 1000円の10%割引
  });
});

フレームワークの乗り換えが容易

Webアプリ開発の世界は変化が激しく、5年前に最新だったフレームワークが今では古くなっているケースも珍しくありません。クリーンアーキテクチャでは、フレームワークは「外側の層」にしか存在しないため、乗り換えが比較的容易です。

フレームワーク変更時の影響
エンティティ層 影響なし
ユースケース層 影響なし
インターフェースアダプター層 一部変更(コントローラーの書き方)
フレームワーク・ドライバー層 全面変更

つまり、ビジネスロジックの70〜80%は再利用できるということです。Express→Next.js、Vue.js→React、REST API→GraphQLといった変更があっても、「注文を作成する」「在庫を確認する」といったビジネスロジックは変更不要です。

属人化を防ぎチーム開発がスムーズに

コードの役割が明確に分かれているため、新しい担当者でも理解しやすい構造になっています。

  • 「ビジネスルールはエンティティ層」「業務の流れはユースケース層」と決まっている
  • ユースケース一覧を見れば、「このシステムで何ができるか」が分かる
  • エンティティ層→ユースケース層→と段階的に学習できる

新人教育では、1週目にエンティティ層のビジネスルールを理解、2週目にユースケース層の業務フローを理解、3週目に実際のコード修正にチャレンジ、という体系的な学習が可能です。

ビジネスルール変更に強い

ビジネスルールがエンティティ層に集約されているため、変更の影響範囲が明確です。「VIP顧客の割引率を10%から15%に変更」という変更は、ユースケース層の1箇所だけで済みます。画面の表示ロジックやデータベースの構造は変更不要です。

実践手順:クリーンアーキテクチャでWebアプリを開発する

ステップ1:ビジネスルールの洗い出し

クリーンアーキテクチャの第一歩は、「自社のビジネスで何が重要か」を明確にすることです。技術の話は後回しにして、まずは業務の本質を理解します。

具体的な作業:
1. 現在の業務フローを可視化する(Excelや紙で行っている作業を書き出す)
2. ビジネスルールを抽出する(「〜の場合は〜する」というルールを書き出す)
3. エンティティ(主要な概念)を特定する(「顧客」「商品」「注文」など)

予約管理システムの例:
- エンティティ:顧客(名前、電話番号、会員ランク)、予約(予約日時、人数、ステータス)
- ビジネスルール:「予約日の3日前まではキャンセル可能」「VIP会員は優先予約可能」

ステップ2:ユースケースの定義

次に、「このシステムで何ができるか」を明確にします。ユーザーがシステムを使って達成したいことを、ユースケースとして定義します。

予約管理システムのユースケース例:
- 予約を作成する
- 予約をキャンセルする
- 予約状況を確認する
- 顧客情報を更新する

各ユースケースについて、入力(何が必要か)、処理(何をするか)、出力(何を返すか)を明確にします。

ステップ3:インターフェースの設計

ユースケースが定義できたら、「どうやってユーザーに提供するか」を設計します。Web画面、API、LINE連携など、複数のインターフェースを用意することも可能です。

設計のポイント:
- ユースケースは変えずに、インターフェースだけを追加できる
- 同じユースケースを、Web画面とスマホアプリの両方から使える

ステップ4:技術選定と実装

最後に、具体的な技術を選定して実装します。中小企業のWebアプリ開発では、以下のような技術スタックが推奨されます。

推奨技術スタック:
- フロントエンド・バックエンド:Next.js + TypeScript
- データベースORM:Prisma
- データベース:PostgreSQL
- 外部サービス:SendGrid(メール送信)、Stripe(決済)

重要なのは、これらの技術を変更しても、ビジネスロジック(内側の層)は影響を受けないという点です。

よくある疑問と回答

「過剰設計では?」という懸念

「うちのような小さなシステムに、そんな大げさな設計は必要ない」という意見はよく聞かれます。確かに、1週間で使い捨てるプロトタイプには過剰かもしれません。

しかし、1年以上使い続ける予定があり、機能追加や改善を行う可能性があるなら、最初から整理された設計にしておく方が、長期的にはコストが低くなります

「とりあえず動けばいい」という開発を続けた結果、後から修正するたびに大きなコストがかかり、最終的に全面的な作り直しが必要になるケースは少なくありません。

小規模プロジェクトでも採用すべきか

小規模プロジェクトでも、以下の条件に当てはまるなら採用を検討する価値があります。

  • 1年以上使い続ける予定がある
  • 段階的に機能を追加していく予定がある
  • 複数の担当者が関わる可能性がある
  • ビジネスルールが明確に存在する

逆に、以下のような場合は、よりシンプルな設計でも十分です。

  • 1〜2ヶ月だけ使う検証用のプロトタイプ
  • 情報表示だけの静的なサイト
  • ビジネスルールがほとんどない単純なCRUD

学習コストとリターンのバランス

クリーンアーキテクチャの学習には、確かに時間がかかります。しかし、一度理解すれば、どんなプロジェクトでも応用できる普遍的な知識になります。

学習の進め方としては、以下をおすすめします。

  1. 小さなプロジェクトで試してみる(顧客管理など)
  2. 実際に動くサンプルコードを写経する
  3. 段階的に適用範囲を広げていく

最初から完璧を目指す必要はありません。まずはエンティティ層とユースケース層だけを意識するところから始めるのも有効です。

まとめ:自社に合ったWebアプリ開発を実現するために

クリーンアーキテクチャは、長期的に使いやすく、メンテナンスしやすいWebアプリを作るための設計思想です。中小企業にとって、属人化の防止、無駄な開発コストの削減、自社に合ったシステムの構築という点で大きなメリットがあります。

重要なのは、完璧を目指すのではなく、自社に「ちょうどいい」レベルで取り入れることです。小さく始めて、ビジネスの成長に合わせてシステムも進化させていくアプローチが、中小企業には最適です。

Harmonic Societyでは、中小企業向けの「ちょうどいい」業務システムを短期間・低コストで構築するサポートを提供しています。クリーンアーキテクチャを活用したWebアプリ開発について、まずはお気軽にご相談ください。

#クリーンアーキテクチャ#Webアプリ
共有:

ちょっとした業務の悩みも、気軽にご相談ください。

まずは話だけ聞いてもらう