11:コントローラーには処理を書かない

main() 関数や Webフレームワーク の コントローラー (DjangoのView)に処理を書きすぎてはいませんか?

具体的な失敗

ここではWebフレームワークDjangoで、 View関数 に書かれた処理を例に説明します。

@login_required
def item_list_view(request, shop_id):
    shop = get_object_or_404(Shop, id=shop_id)
    if not request.user.memberships.filter(role=Membership.ROLE_OWNER, shop=shop).exists():
        return HttpResponseForbidden()

    items = Item.objects.filter(shop=shop,
                                published_at__isnull=False)
    if "search" in request.GET:
        search_text = request.GET["search"]
        if len(search_text) < 2:
            return TemplateResnponse(request, "items/item_list.html",
                                     {"items": items, "error": "文字列が短すぎます"})
        items = items.filter(name__contains=search_text)
    prices = []
    for item in items:
        price = int(item.price * 1.1)
        prices.append(f"{price:,}円")
    items = zip(items, prices)
    return TemplateResponse(request, "items/item_list.html", {"items": items})

このプログラムは item_list_view 関数に処理を書きすぎています。

cover

(中略)詳細は書籍 自走プログラマー をご参照ください

ベストプラクティス

コントローラーでは値の入出力と、処理全体の制御のみ行うべきです。 コントローラーに細かい処理まで実装すると、コントローラーに書かれるプログラムが多くなりすぎます。 それでは処理全体の見通しが悪くなるだけでなく、 単体テスト もしにくくなります。 上記の item_list_view View関数内の処理もほとんどは別の関数や コンポーネント に分離して実装すべきです。

処理をそれぞれ分離したあとの item_list_view 関数は以下のようになります。

リスト 1.1 views.py
 @login_required
 def item_list_view(request, shop_id):
     shop = get_object_or_404(Shop, id=shop_id)
     validate_membership_permission(request.user, shop, Membership.ROLE_OWNER)

     items = Item.objects.filter(shop=shop).published()
     form = ItemSearchForm(request.GET)
     if form.is_valid():
         items = form.filter_items(items)
     return TemplateResponse(request, "items/item_list.html",
                             {"items": items, "form": form})

cover

(中略)詳細は書籍 自走プログラマー をご参照ください