【Flask】Blueprint・controller・serviceの違いと使い方

2024/11/03に公開

はじめに

こんにちは!
情報系の学部に通うインターン生です!

インターン先で、「コントローラとサービスを使って、app.pyのプログラムを分割して!」と先輩から指示があったので、ファイルの分割に取り組みました。
その際、今までの個人開発では、ファイル分割をしたことがなかったので、その方法を調べました!

コントローラーとサービス、それぞれの違いと使い方について調べたのでここにまとめます!

コード(ファイル)を分割する意味

コードの見通しが良くなる
コードを役割ごとに分割することで、各ファイルがシンプルになり、見通しが良くなる。これにより、全体を把握しやすくなり、コードの理解が早くなる。

コードの再利用性が向上する
サービス層にビジネスロジックをまとめておくことで、他のコントローラーやモジュールからも共通のロジックを使いやすくなり、同じ機能を複数回書く必要がなくなる。

保守性が向上する
コードが役割ごとに分割されていると、変更が必要な際に影響範囲を特定しやすくなる。たとえば、ビジネスロジックを変更する場合、サービス層のみを変更すればよく、UIやルーティングに関する部分(コントローラー)は影響を受けにくくなる。

テストのしやすさ
各機能が分割されていると、単体テストが行いやすくなる。

協力作業が容易になる
異なるファイルやモジュールを担当しやすくなり、並行して作業することが可能になるため、効率的な開発を行える。

Blueprintとは

Flaskで使える、アプリの機能をいくつかのファイルに分割することができる機能
ただ関数を別のファイルに記述するだけなら必要ないが、アクセスされた際のルートを定義する際に用いる。

controllerとは

ユーザーからのリクエストを処理し、適切なレスポンスを返す役割を担う部分
複雑なビジネスロジックを記述しないようにする。あくまでリクエストの受付とレスポンスの作成に集中し、データの処理や変換、ビジネスロジックはサービスに任せる。
controllersというフォルダの中に格納するのが一般的。

@app.route('/')
def root():
    return 'Hello world!'

serviceとは

ビジネスロジックやデータ処理など、アプリケーションの主要な機能を実装する部分
コントローラーから呼び出され、具体的な処理を担当する。
servicesというフォルダの中に格納するのが一般的。

def add_numbers(a, b):
    return a + b

実際に作ってみる

ただアクセスしたらWEBページが表示されるプログラムをコントローラーとサービスを用いて作成した。

処理の流れ

  1. app.pyでFlaskアプリケーションを初期化し、page_bp(Blueprint)をルートに登録する。
  2. ルートURLにリクエストがあると、page_controller.pyのhome関数が呼び出される。
  3. home関数がpage_service.pyのget_home_page関数を呼び出す。
  4. get_home_page関数がHTMLコンテンツを生成して返す。
  5. home関数がサービス層から受け取ったHTMLコンテンツをレスポンスとして返す。

フォルダ構成

C:
│─app.py
│
├─controllers
│  │─page_controller.py
│  └─__pycache__
└─services
    │─page_service.py
    └─__pycache__

プログラムファイル

app.py
from flask import Flask
from controllers.page_controller import page_bp

app = Flask(__name__)

# コントローラー(page_bp)を登録し、ルートパス('/')に紐づける
app.register_blueprint(page_bp, url_prefix='/')

if __name__ == "__main__":
    app.run(debug=True) 

二行目で、controllersというフォルダのpage_controllerというファイルのpage_bpというBlueprintのインスタンスをインポートしている。
五行目は、/にアクセスされた際にpage_bpにルーティングを転送するという意味。

page_controller.py
from flask import Blueprint
from services.page_service import get_home_page

# Blueprintを作成し、'page_bp'という名前で登録
page_bp = Blueprint('page_bp', __name__)

@page_bp.route("/")
def home():
    # サービス層のget_home_page関数を呼び出し、その結果をレスポンスとして返す
    return get_home_page()

二行目で、servicesというフォルダのpage_serviceというファイルのget_home_pageという関数をインポートしている。

page_bp = Blueprint('page_bp', __name__)
page_service.py
# Flaskのパッケージからrender_template_string関数だけをインポートして利用
from flask import render_template_string

# ホームページのHTMLコンテンツを生成する関数
def get_home_page():
    # シンプルなHTMLコンテンツを定義
    html_content = """
    <!DOCTYPE html>
    <html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>シンプルなページ</title>
    </head>
    <body>
        <h1>ようこそ!</h1>
        <p>これはサービス層を使って表示されるシンプルなページです。</p>
    </body>
    </html>
    """
    # render_template_stringでHTMLを直接返す
    return render_template_string(html_content)

ここでは、呼び出されたらただHTMLを返すだけのget_home_page()を定義している。

結果

以下のようにきちんと、アクセスした際にget_home_page()で定義しているHTMLファイルが表示されていることがわかる。

最後に

ファイル分割の方法がわかったので、実際にインターン先のプログラム、私のポートフォリオにも組み込んでみようと思います!

大学の課題(Java)でファイルの分割を習い、実装することはあったのですが、学外での開発では実装したことがなかったのでいい経験になりました!

今後もプラスになることは積極的に取り入れて実施していきたいと思います!

最後までご覧いただきありがとうございました!

参考元

Discussion