保守性と拡張性を両立する理想的な設計手法
ソフトウェア開発では、長期間にわたって 変更しやすく、バグが少なく、拡張しやすいアーキテクチャ を設計することが求められます。そこで登場するのが、クリーンアーキテクチャ(Clean Architecture) です。
本記事では、クリーンアーキテクチャの基本概念とオブジェクト指向設計(OOP)との関係を説明し、Javaでの実践例を紹介します。
1. クリーンアーキテクチャとは?
クリーンアーキテクチャは、Robert C. Martin(通称 Uncle Bob)によって提唱されたソフトウェアアーキテクチャの設計手法です。
1-1. クリーンアーキテクチャの基本概念
クリーンアーキテクチャの基本的な構造は、以下の4つのレイヤーから成り立っています。
- エンティティ(Entities) - ビジネスルールやドメインモデルを定義
- ユースケース(Use Cases) - アプリケーションの振る舞いを記述
- インターフェースアダプター(Interface Adapters) - ユースケースと外部インターフェースの橋渡し
- フレームワーク & ドライバ(Frameworks & Drivers) - データベースやWebフレームワークなどの外部依存
以下の図のように、依存関係は 内側(エンティティ)から外側(フレームワーク) に向かってのみ許可されます。
+------------------------------------------+
| フレームワーク & ドライバ (Spring, DB, UI) |
+------------------------------------------+
| インターフェースアダプター (Controller, Presenter, Repository) |
+------------------------------------------+
| ユースケース (Application Logic) |
+------------------------------------------+
| エンティティ (Business Logic) |
+------------------------------------------+
ポイント:
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);
}
}
ポイント:
CreateOrderUseCase
はOrderRepository
の具体的な実装には依存せず、インターフェースに依存することで柔軟性を確保。
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での具体的な実装方法を紹介しました。
次回の記事では、以下のトピックを取り上げる予定です:
質問や取り上げてほしいテーマがあれば、ぜひコメントでお知らせください!