依存性注入(DIパターン)
DIパターンとは、システム開発においてソフトウェア設計のパターンの1つです。クラス、オブジェクトの依存関係を外部から注入します。
例えば、あるクラスAが必要なクラスBのオブジェクトを直接生成して使用してしまうと、クラスBの修正や追加機能が発生した場合にクラスAの修正も行う必要が出てきます。
DIパターンはこのクラス間の依存関係を外部から注入することで、クラスAの修正は不要になります。
また、DIパターンによりテストも容易に実施することが可能です。
FastAPI 依存性注入サンプルコード
from sqlalchemy.orm import Session
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from fastapi import FastAPI, Depends, HTTPException
from contextlib import contextmanager
Base = declarative_base()
# データベース接続用の関数(セッション作成)
@contextmanager
def get_db():
db: Session = SessionLocal() # データベースセッションを作成
try:
yield db
finally:
db.close() # セッションをクローズ
# サンプルテーブル
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
email = Column(String, unique=True, index=True)
# リポジトリクラス
class UserRepository:
def __init__(self, db: Session):
self.db = db
def create(self, name: str, email: str) -> User:
db_user = User(name=name, email=email)
self.db.add(db_user)
return db_user
def get_by_id(self, user_id: int) -> User:
return self.db.query(User).filter(User.id == user_id).first()
def get_by_email(self, email: str) -> User:
return self.db.query(User).filter(User.email == email).first()
# サービスクラス(トランザクション管理を含む)
class UserService:
def __init__(self, user_repository: UserRepository):
self.user_repository = user_repository
def create_user(self, db: Session, name: str, email: str) -> User:
try:
# 新しいユーザーを作成
db_user = self.user_repository.create(name, email)
db.commit() # トランザクションのコミット
db.refresh(db_user) # 作成したユーザー情報をリフレッシュ
return db_user
except Exception as e:
db.rollback() # エラーが発生した場合はロールバック
raise HTTPException(status_code=500, detail=f"An error occurred: {e}")
def get_user_by_id(self, db: Session, user_id: int) -> User:
return self.user_repository.get_by_id(user_id)
def get_user_by_email(self, db: Session, email: str) -> User:
return self.user_repository.get_by_email(email)
app = FastAPI()
# リポジトリを FastAPI の依存関係として設定
def get_user_repository(db: Session = Depends(get_db)) -> UserRepository:
return UserRepository(db)
# サービスクラスを FastAPI の依存関係として設定
def get_user_service(user_repository: UserRepository = Depends(get_user_repository)) -> UserService:
return UserService(user_repository)
# エンドポイントでサービスクラスを利用
@app.post("/users/")
def create_user_endpoint(name: str, email: str, db: Session = Depends(get_db), user_service: UserService = Depends(get_user_service)):
return user_service.create_user(db=db, name=name, email=email)
プログラムの説明
Depends
を使った依存性注入 (DI) は、FastAPI の強力な機能で、アプリケーションの各コンポーネントに必要な依存関係を自動的に注入する方法です。このコードでは、Depends
を使ってリポジトリやサービスをエンドポイント関数に注入しています。
依存性注入の具体例
1. リポジトリの依存関係注入
def get_user_repository(db: Session = Depends(get_db)) -> UserRepository:
return UserRepository(db)
get_user_repository
は、データベースセッション (db: Session
) を受け取って、UserRepository
のインスタンスを返します。db
はDepends(get_db)
で、get_db
関数から提供されます。get_db
はデータベースセッションを作成し、エンドポイントで使用できるようにします。- これにより、
UserRepository
のインスタンスが FastAPI のエンドポイントで使えるようになります。
2. サービスの依存関係注入
def get_user_service(user_repository: UserRepository = Depends(get_user_repository)) -> UserService:
return UserService(user_repository)
get_user_service
は、UserRepository
を受け取り、UserService
のインスタンスを返します。user_repository
はDepends(get_user_repository)
で、先ほど定義したget_user_repository
から提供されます。- このように、
UserService
も依存関係として注入されます。
3. エンドポイントでの依存性注入
@app.post("/users/")
def create_user_endpoint(name: str, email: str, db: Session = Depends(get_db), user_service: UserService = Depends(get_user_service)):
return user_service.create_user(db=db, name=name, email=email)
- エンドポイント
create_user_endpoint
では、db
とuser_service
の依存関係が注入されます。 db
はDepends(get_db)
によってデータベースセッションが提供され、user_service
はDepends(get_user_service)
によってサービスクラスが提供されます。
依存性注入の効果
- 簡潔さ:
Depends
を使うことで、必要な依存関係を手動で渡す必要がなくなり、コードがシンプルになります。 - 再利用性: 一度定義した依存関係は他のエンドポイントやクラスでも使い回しができ、コードの重複を避けられます。
- テストのしやすさ: 依存性注入を使うことで、モックやスタブを使ってテストが容易になります。
結論
Depends
は、FastAPI が提供する依存性注入の仕組みで、コンポーネント間の依存関係を自動的に解決し、コードの可読性、再利用性、テストのしやすさを高めます。