オニオンアーキテクチャとは
オニオンアーキテクチャは、ソフトウェアアプリケーションの設計パターンです。このアーキテクチャは、オニオンのような同心円状のレイヤーで視覚的に表現され、その名前が付けられています。このパターンは、依存関係の制御を厳密に行い、依存関係は内側に向かって流れ、相互作用は外側のレイヤーから中心に向かって行われます。
このアーキテクチャの主な目的は、ソフトウェア設計における緊密な結合や関心事の分離などの一般的な落とし穴に対処し、アプリケーションの構造における保守性と適応性を可能にすることです。
原則
オニオンアーキテクチャは、いくつかの原則に基づいています。
-
ドメインが中心
アプリケーションの核心はドメインであり、基本的なビジネスルールやロジックを包括します。これには、ビジネスエンティティや値オブジェクトが含まれます。オニオンアーキテクチャでは、ドメインエンティティは他の要素に依存しません。直接に関連するプロパティとメソッドを含んでおり、純粋です。 -
内側への依存
アーキテクチャのレイヤーは、内側のレイヤーにのみ依存し、外側のレイヤーには依存しません。アイデアは、ドメインとアプリケーションレイヤーをUI、インフラストラクチャ、テストなどの外部の関心事から独立させることです。結合が中心に向かうことを確認することで、外側のレイヤーを簡単に交換や変更できるようにし、内側のレイヤーに影響を与えないようにします。 -
インフラストラクチャからの隔離
オニオンアーキテクチャの別の原則は、インフラストラクチャに関する関心事を外側のレイヤーにカプセル化することです。したがって、データベースソフトウェアやユーザーインターフェースなどにどのような変更があっても、インフラストラクチャの組織と操作には内側のレイヤーに影響を与えません。このアプローチにより、インフラストラクチャに関連する依存関係なしにコアコンポーネントのテストが容易に行えます。 -
アプリケーションのコアコードの独立性
全てのアプリケーションのコアコードは、ユーザーインターフェース、データベース、Webサーバー、その他の外部要素なしでテストできます。コアに焦点を当てることで、アプリケーションの保守性が向上し、スケーラビリティが向上し、エラーの発生が少なくなります。
オニオンアーキテクチャのレイヤー
オニオンアーキテクチャでは、アプリケーションはさまざまな責任を持つ複数のレイヤーに分割され、それぞれは直接内部または外部のレイヤーと通信します。
ドメインモデルレイヤー
ドメインモデルレイヤーは、オニオンアーキテクチャの中心に位置します。このもっとも内側のレイヤーは、アプリケーションのドメインに関連するビジネスルール、ポリシー、エンティティをカプセル化します。ドメインモデルには、ドメインに適用される実世界の概念と動作を直接表現するビジネスエンティティや値オブジェクトが含まれます。
ドメインオブジェクトは、データベースやUIなどの外部の関心事を含めず、永続化に関して無知であるべきです。ビジネスドメインに関連する重要なプロパティや動作をカプセル化しているため、純粋です。
ドメインサービスレイヤー
ドメインモデルレイヤーのすぐ外側には、ドメインサービスレイヤーがあります。このレイヤー内のサービスは、複数のドメインエンティティや値オブジェクトを含むビジネスロジックに関わる操作を処理します。ドメインオブジェクトに自然にフィットしないビジネスロジックをカプセル化します。
ドメインサービスは、複数のエンティティ間でのタスクの調整、複雑な計算の実行、複数のエンティティにまたがるビジネスルールの適用などに使用される場合があります。ドメインオブジェクトと同様に、ドメインサービスはインフラストラクチャの関心事から隔離されるべきです。
アプリケーションサービスレイヤー
アプリケーションサービスは、ドメインと外部の世界の間のやり取りを組み立てる役割を果たします。ビジネスルールや知識を含まず、トランザクション管理やドメインイベントのトリガリングなどのタスクに責任を持ちます。
アプリケーションサービスは、ドメインレイヤーとインフラストラクチャレイヤーを調整します。ビジネスルールに関連する操作を実行するためにドメインサービスやドメインエンティティを呼び出し、永続化、キャッシュ、メッセージ送信などのタスクを処理するためにインフラストラクチャレイヤーと対話します。
インフラストラクチャ、テスト、およびユーザーインターフェースレイヤー
オニオンアーキテクチャのもっとも外側のレイヤーには、ユーザーインターフェース、テスト、インフラストラクチャのタスクなどが含まれます。これらは、進化する技術や要件による時間の経過に伴い変更されやすいソフトウェアの領域です。そのため、これらはコアのビジネスルールから分離されており、内部レイヤーに対する変更の影響を受けません。
インフラストラクチャのサブレイヤーには、リポジトリやデータアクセスオブジェクトを通じたデータ永続化やネットワーク通信などの関心事がカプセル化されます。
テストのサブレイヤーには、開発を推進しアプリケーションの正確性を保証するための全てのテストが含まれます。ユニットテスト、統合テスト、エンドツーエンドテストなどがこれに含まれます。
最後に、ユーザーインターフェースのサブレイヤーは、プレゼンテーションロジックやユーザー入力の処理など、全てのユーザーの相互作用を担当します。これには、Webインターフェース、REST API、デスクトップアプリケーションなどが含まれます。
Pythonでのオニオンアーキテクチャの実装
Pythonでオニオンアーキテクチャを実装するには、アーキテクチャの異なるレイヤーを表すクラスを定義する必要があります。ドメインモデル、ドメインサービス、アプリケーションサービス、およびインフラストラクチャのように。以下では、オニオンアーキテクチャに従ってシンプルなアプリケーションの構造をどのように作成するかについて説明します。
続行する前に、仮想的なドメインを考えてみます。今回は、基本的な注文処理システムを考えます。
ドメインモデルレイヤー
まず、ドメインエンティティを定義します。この例では、Order
とProduct
のクラスが考えられます。これらのクラスは、インフラストラクチャやアプリケーションサービスに関連するロジックを含まず、ビジネスロジックにのみ焦点を当てています。
class Product:
def __init__(self, id, name, price):
self.id = id
self.name = name
self.price = price
class Order:
def __init__(self, id, product, quantity):
self.id = id
self.product = product
self.quantity = quantity
self.total = self.calculate_total()
def calculate_total(self):
return self.product.price * self.quantity
ドメインサービスレイヤー
次に、ドメインサービスを実装します。このサービスには、複数のドメインエンティティに関与するビジネスロジックが含まれます。この例では、注文に対して割引を計算するDiscountService
を考えてみます。
class DiscountService:
def apply_discount(self, order, discount_percentage):
if discount_percentage < 0 or discount_percentage > 100:
raise ValueError("Invalid discount percentage")
order.total -= order.total * (discount_percentage / 100)
return order
アプリケーションサービスレイヤー
アプリケーションサービスレイヤーは、ドメインとインフラストラクチャの橋渡し役となります。この例では、注文の作成と割引の適用を担当するOrderProcessingService
があります。
from domain_services import DiscountService
class OrderProcessingService:
def __init__(self, order_repository, product_repository):
self.order_repository = order_repository
self.product_repository = product_repository
self.discount_service = DiscountService()
def process_order(self, order_id, product_id, quantity, discount_percentage):
product = self.product_repository.get(product_id)
order = Order(order_id, product, quantity)
self.discount_service.apply_discount(order, discount_percentage)
self.order_repository.save(order)
インフラストラクチャレイヤー
インフラストラクチャレイヤーには、データベースからのデータアクセスに使用されるリポジトリなど、データベースからのデータアクセスのためのリソースが含まれる場合があります。実際のシナリオでは、SQLやNoSQLデータベースへのクエリが含まれることがありますが、簡単にするためにメモリ内のリストを使用します。
class OrderRepository:
def __init__(self):
self.orders = []
def save(self, order):
self.orders.append(order)
class ProductRepository:
def __init__(self):
self.products = []
def get(self, product_id):
for product in self.products:
if product.id == product_id:
return product
参考