😺
7/1~7/21までに技術的にやったこと
クリーンアーキテクチャについて
クリーンアーキテクチャとは?
最近このアーキテクチャで実装を行っていて感じたこと
- コードが追いやすい
どんな設計思想で書いているかを明確にすることで、どこに何が置かれていてどんな実装をしているかパッとコードを見ただけでわかる。内部ロジックがなんでそうなっているかは別として。
こんな風にコードのどのあたりを見ればわかるかすぐにわかるので保守しやすいように感じた。
- やってレビューしてもらってから腑に落ちた
実際にusecase層にはロジック、repository層にはdbアクセスって決まりがあってもどこまでの処理をどこに持たせるのかみたいな部分はイマイチわかっていなかった。その点レビューいただいて、かなり腑に落ちたので簡単にまとめる
・usecase層
→ビジネスロジックのみ。なので、dbにアクセスするとかしない。エラーハンドリングも特にしないで基本的にthorowするだけ。あくまで、やりたいことを実現するだけにとどめる。どこからデータを取ってきて〜みたいなことはしない
・repository層
→dbへのロジックのみ。なので、基本的にはdbからデータを取得して返すだけ、細かくデータを加工したりとか余計なことをしない。そおいうのはusecase層に任せる。お前はただデータをとってきてくれってイメージ。だから基本的にはこのレスポンスモデルはdbのテーブル定義で返すことになる、変にpydanticでレスポンスモデルを作るよりもテーブルのオブジェクトで返しちゃった方がわかりやすい
- 依存性への理解が必要
そもそも依存とは?
- 依存性の注入とは?
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できなかった。
参考にした記事
Discussion