Flask で大規模アプリケーションを設計するパターン

Flask アプリケーションが成長するにつれ、単一ファイルでは管理が困難になる。大規模アプリケーションを設計するためのパターンを紹介する。

アプリケーションファクトリパターン

create_app 関数でアプリケーションを生成する。これにより循環インポートを回避し、テスト時に異なる設定を渡せる。

# app/__init__.py
from flask import Flask

def create_app(config_name='default'):
    app = Flask(__name__)
    app.config.from_object(f'config.{config_name}')
    
    # 拡張の初期化
    from app.extensions import db, migrate, login_manager
    db.init_app(app)
    migrate.init_app(app, db)
    login_manager.init_app(app)
    
    # Blueprint の登録
    from app.views import main_bp, api_bp, admin_bp
    app.register_blueprint(main_bp)
    app.register_blueprint(api_bp, url_prefix='/api')
    app.register_blueprint(admin_bp, url_prefix='/admin')
    
    return app

推奨ディレクトリ構成

project/
├── app/
│   ├── __init__.py          # create_app
│   ├── extensions.py        # db, login_manager など
│   ├── models/
│   │   ├── __init__.py
│   │   ├── user.py
│   │   └── post.py
│   ├── views/
│   │   ├── __init__.py      # Blueprint をまとめてエクスポート
│   │   ├── main.py
│   │   ├── api.py
│   │   └── admin.py
│   ├── services/            # ビジネスロジック
│   │   ├── user_service.py
│   │   └── email_service.py
│   ├── templates/
│   └── static/
├── config.py
├── migrations/
├── tests/
├── run.py
└── requirements.txt

拡張の分離

拡張は extensions.py でインスタンス化し、create_app 内で init_app を呼ぶ。

# app/extensions.py
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager

db = SQLAlchemy()
migrate = Migrate()
login_manager = LoginManager()

サービス層の導入

ビュー関数にビジネスロジックを書かず、サービス層に分離する。

# app/services/user_service.py
from app.models import User
from app.extensions import db

class UserService:
    @staticmethod
    def create_user(username, email, password):
        user = User(username=username, email=email)
        user.set_password(password)
        db.session.add(user)
        db.session.commit()
        return user
    
    @staticmethod
    def get_by_email(email):
        return User.query.filter_by(email=email).first()
# app/views/main.py
from app.services.user_service import UserService

@main_bp.route('/register', methods=['POST'])
def register():
    user = UserService.create_user(
        request.form['username'],
        request.form['email'],
        request.form['password']
    )
    return redirect(url_for('main.login'))

設定の環境別管理

# config.py
import os

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY', 'dev')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

class DevelopmentConfig(Config):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///dev.db'

class ProductionConfig(Config):
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')

class TestingConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'

default = DevelopmentConfig

これらのパターンを組み合わせることで、保守性とテスタビリティの高いアプリケーションを構築できる。