😺

7/1~7/21までに技術的にやったこと

2024/07/21に公開

クリーンアーキテクチャについて

クリーンアーキテクチャとは?

最近このアーキテクチャで実装を行っていて感じたこと

  1. コードが追いやすい
    どんな設計思想で書いているかを明確にすることで、どこに何が置かれていてどんな実装をしているかパッとコードを見ただけでわかる。内部ロジックがなんでそうなっているかは別として。

こんな風にコードのどのあたりを見ればわかるかすぐにわかるので保守しやすいように感じた。

  1. やってレビューしてもらってから腑に落ちた
    実際にusecase層にはロジック、repository層にはdbアクセスって決まりがあってもどこまでの処理をどこに持たせるのかみたいな部分はイマイチわかっていなかった。その点レビューいただいて、かなり腑に落ちたので簡単にまとめる

・usecase層
→ビジネスロジックのみ。なので、dbにアクセスするとかしない。エラーハンドリングも特にしないで基本的にthorowするだけ。あくまで、やりたいことを実現するだけにとどめる。どこからデータを取ってきて〜みたいなことはしない

・repository層
→dbへのロジックのみ。なので、基本的にはdbからデータを取得して返すだけ、細かくデータを加工したりとか余計なことをしない。そおいうのはusecase層に任せる。お前はただデータをとってきてくれってイメージ。だから基本的にはこのレスポンスモデルはdbのテーブル定義で返すことになる、変にpydanticでレスポンスモデルを作るよりもテーブルのオブジェクトで返しちゃった方がわかりやすい

  1. 依存性への理解が必要
    そもそも依存とは?
  1. 依存性の注入とは?

Pythonでいうとabcライブラリを使って、抽象基底クラスを定義して依存性の注入を行って実装することが多いように感じる

具体的な話

抽象基底クラス(entiry/repository層(dbへの根底となるもを実装する層))

from abc import ABC, abstractmethod

class DatabaseConnection(ABC):
    @abstractmethod
    def connect(self):
        pass

    @abstractmethod
    def execute_query(self, query: str):
        pass

抽象基底クラスを実装する具体的なクラス(インターフェースアダプタ層(dbへの具体的な実装))

import sqlite3

class SQLiteConnection(DatabaseConnection):
    def __init__(self, database: str):
        self.database = database
        self.connection = None

    def connect(self):
        self.connection = sqlite3.connect(self.database)
        print(f"Connected to {self.database}")

    def execute_query(self, query: str):
        cursor = self.connection.cursor()
        cursor.execute(query)
        return cursor.fetchall()

依存性注入を行う層(usecase層(ビジネスロジックを実装する))

class DataService:
    def __init__(self, db_connection: DatabaseConnection):
        self.db_connection = db_connection

    def get_data(self):
        self.db_connection.connect()
        result = self.db_connection.execute_query("SELECT * FROM example_table")
        return result

依存性を注入して使用する

sqlite_connection = SQLiteConnection("example.db")

data_service = DataService(sqlite_connection)

data = data_service.get_data()
print(data)

importでハマった話

import際以下のようにすると思うが

import 丸々フォルダ.ファイル名1

同じディレクトリだからと言って

import .ファイル名1

こうするとファイル名1が同じかどうかの判定が変わってしまい(別物と判定してしまう?)
で正しくファイル名1の実装をimportできなかった。

参考にした記事
https://zenn.dev/sre_holdings/articles/a57f088e9ca07d
https://qiita.com/mkgask/items/d984f7f4d94cc39d8e3c

Discussion