okpy

Pythonエンジニア兼テックリーダーが、多くのプロジェクトとチーム運営から得た実践的な知識を共有するブログです。

ソフトウェア開発の理想的な設計手法: クリーンアーキテクチャの基本概念


保守性と拡張性を両立する理想的な設計手法

ソフトウェア開発では、長期間にわたって 変更しやすく、バグが少なく、拡張しやすいアーキテクチャ を設計することが求められます。そこで登場するのが、クリーンアーキテクチャ(Clean Architecture) です。
本記事では、クリーンアーキテクチャの基本概念とオブジェクト指向設計OOP)との関係を説明し、Javaでの実践例を紹介します。

 


1. クリーンアーキテクチャとは?

クリーンアーキテクチャは、Robert C. Martin(通称 Uncle Bob)によって提唱されたソフトウェアアーキテクチャの設計手法です。

1-1. クリーンアーキテクチャの基本概念

クリーンアーキテクチャの基本的な構造は、以下の4つのレイヤーから成り立っています。

  1. エンティティ(Entities) - ビジネスルールやドメインモデルを定義
  2. ユースケース(Use Cases) - アプリケーションの振る舞いを記述
  3. インターフェースアダプター(Interface Adapters) - ユースケースと外部インターフェースの橋渡し
  4. フレームワーク & ドライバ(Frameworks & Drivers) - データベースやWebフレームワークなどの外部依存

以下の図のように、依存関係は 内側(エンティティ)から外側(フレームワーク に向かってのみ許可されます。

+------------------------------------------+
| フレームワーク & ドライバ (Spring, DB, UI) |
+------------------------------------------+
| インターフェースアダプター (Controller, Presenter, Repository) |
+------------------------------------------+
| ユースケース (Application Logic) |
+------------------------------------------+
| エンティティ (Business Logic) |
+------------------------------------------+

ポイント:

  • 内側のレイヤー(エンティティ)は、外側のレイヤーに依存しない。
  • ビジネスロジックと外部依存(DB, API, UI)を分離することで、保守性と拡張性を向上させる。

2. クリーンアーキテクチャJavaで実装する

ここでは、注文管理システム(Order Management System) を題材にして、クリーンアーキテクチャの実装例を紹介します。

2-1. エンティティ層(Entities)

エンティティは、ビジネスルールを表す最も内側のレイヤーです。

public class Order {
    private Long id;
    private String customerName;
    private double totalAmount;

    public Order(Long id, String customerName, double totalAmount) {
        this.id = id;
        this.customerName = customerName;
        this.totalAmount = totalAmount;
    }

    public Long getId() { return id; }
    public String getCustomerName() { return customerName; }
    public double getTotalAmount() { return totalAmount; }
}

2-2. ユースケース層(Use Cases)

ユースケース層は、アプリケーションの振る舞いを定義します。
「注文を作成するユースケース を実装します。

public interface OrderRepository {
    void save(Order order);
}

public class CreateOrderUseCase {
    private final OrderRepository orderRepository;

    public CreateOrderUseCase(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    public void execute(Order order) {
        // ビジネスルールの適用(例:最低注文金額のチェック)
        if (order.getTotalAmount() < 1000) {
            throw new IllegalArgumentException("注文金額は1000円以上でなければなりません");
        }
        orderRepository.save(order);
    }
}

ポイント:

  • CreateOrderUseCaseOrderRepository の具体的な実装には依存せず、インターフェースに依存することで柔軟性を確保。

2-3. インターフェースアダプター層(Interface Adapters)

この層では、ユースケース層と外部システムをつなぐ役割を果たします。
リポジトリの実装(JPAを使用) を記述します。

import org.springframework.stereotype.Repository;

@Repository
public class JpaOrderRepository implements OrderRepository {
    private final SpringDataOrderRepository springDataOrderRepository;

    public JpaOrderRepository(SpringDataOrderRepository springDataOrderRepository) {
        this.springDataOrderRepository = springDataOrderRepository;
    }

    @Override
    public void save(Order order) {
        OrderEntity entity = new OrderEntity(order.getId(), order.getCustomerName(), order.getTotalAmount());
        springDataOrderRepository.save(entity);
    }
}

2-4. フレームワーク & ドライバ層(Frameworks & Drivers)

Spring Boot を使って、REST API を作成します。

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/orders")
public class OrderController {
    private final CreateOrderUseCase createOrderUseCase;

    public OrderController(CreateOrderUseCase createOrderUseCase) {
        this.createOrderUseCase = createOrderUseCase;
    }

    @PostMapping
    public String createOrder(@RequestBody OrderRequest request) {
        Order order = new Order(null, request.getCustomerName(), request.getTotalAmount());
        createOrderUseCase.execute(order);
        return "注文が正常に作成されました";
    }
}

ポイント:

  • OrderController は、ユースケース(CreateOrderUseCase) に依存しており、データベースの具体的な実装には依存しない。

3. クリーンアーキテクチャのメリット

保守性の向上

  • 各レイヤーが独立しているため、変更の影響範囲が限定される。
  • ビジネスロジックの変更がUIやデータベースに影響を与えない。

テスト容易性

import static org.mockito.Mockito.*;

public class CreateOrderUseCaseTest {
    @Test
    public void testCreateOrder() {
        OrderRepository mockRepository = mock(OrderRepository.class);
        CreateOrderUseCase useCase = new CreateOrderUseCase(mockRepository);

        Order order = new Order(1L, "Alice", 1500);
        useCase.execute(order);

        verify(mockRepository).save(order);
    }
}

拡張性


4. まとめ

クリーンアーキテクチャは、システムの 柔軟性・保守性・拡張性 を高める強力な設計手法です。本記事では、クリーンアーキテクチャの概念と、Javaでの具体的な実装方法を紹介しました。

次回の記事では、以下のトピックを取り上げる予定です:

質問や取り上げてほしいテーマがあれば、ぜひコメントでお知らせください!