doc.dev1x.org

アプリケーションアーキテクチャ設計に関する覚書(Python)

1. 前提条件

2. 基本的な考え方

MVC + Uアーキテクチャ(Model/View/Controller + Usecase)

ドメイン層はアプリケーション層/インフラ層から独立

3. プロジェクト構造

プロジェクトディレクトリの構造

.
├─ doc/
├─ docker/
├─ schema/
├─ src/
├─ static/
├─ template/
├─ test/
├─ .env
├─ .gitignore
├─ docker-compose.yaml
├─ poetry.lock
├─ poetry.toml
├─ pyproject.toml
└─ README.md

doc/

docker/

schema/

src/

static/

template/

test/

.env

.gitignore

docker-compose.yaml

poetry.lock

poetry.toml

[virtualenvs]
in-project = true

pyproject.toml

README.md

4. src以下の構造

src以下のディレクトリツリー

src/app
├─ config/
├─ controller/
├─ exception/
├─ middleware/
├─ model/
├─ repository/
├─ usecase/
├─ validator/
├─ view/
├─ app.py
├─ cmd.py
└─ job.py

config

import os

BASE_URL = os.environ.get('BASE_URL', '')
APP_HOME = os.environ.get('APP_HOME', '')
TASK_STATUS = [
    {
        'name': 'new',
        'screen_name': '新規',
        'code': '01',
        'order': 1,
    },
    {
        'name': 'working',
        'screen_name': '着手',
        'code': '02',
        'order': 2,
    },
    ...
]
...

controller

exception

middleware

model

repository

usecase

validator

view

app.py

# Flaskの場合
app.add_url_rule("/", view_func=index)
# Bottleの場合
import bottle
app = bottle.Bottle()
app.route('/', ['GET'], index)

cmd.py

# coding: utf-8

import click
from usecase import xxx_usecase

@click.group()
@click.pass_context
def main(context):
    pass

@main.command()
def xxx():
    xxx_usecase.run()

if __name__ == '__main__':
    main()

job.py

# coding: utf-8

import schedule
import time
from usecase import xxx_usecase

# 毎時0分に処理実行
schedule.every().hour.at(":00").do(xxx_usecase.run)

if __name__=='__main__':
    while True:
        schedule.run_pending()
        time.sleep(1)

5. バリデーション

外部入力のバリデーション

データの整合性の関するバリデーション

6. CQRS

更新系と参照系ではドメインモデルを分ける

7. テスト

ユニットテスト

結合テスト

8. FAQ

Clean Architectureにしていない理由は?

リポジトリが永続化手段に依存しているのはよいのか?

コントローラの責務が多いように見えるが?

何故○○(任意のフルスタックフレームワーク)を使わないのか?

各モジュールの名前空間は複数形にしないのか?