📙

Go+Reactのアプリ開発メモ(クリーンアーキテクチャ)

に公開

クリーンアーキテクチャの勉強を兼ねて、アプリを作ろうと思いました。
その過程で自分自身が混乱しないようにするために記事にします。

全体像

レイヤー層

レイヤー名 役割 用語
handler層 URLと1対1 Controller/Handler
usecase層 ユースケースごとのロジック Usecase/Interactor
service層 ビジネスロジック、個別機能実行 Service
repository層 DBとのやりとり Repository

データ構造

名前 役割 用途 使用する処理層
model DBと対応 Gorm用、永続化 Repository
domain アプリの本質的なデータ構造 ビジネスルール Usecase/Service
dto 外部とのデータ受け渡し APIリクエスト/レスポンス Handler
types(TS) フロントの型 UI表示 フロント

命名規則

言語 命名規則 使用例 使用場所
Go PascalCase UserName 構造体、フィールド名
Go camelCase getUser() 変数名、非公開フィールド
TS PascalCase UserFormProps 構造体、コンポーネント名
TS camelCase userEmail 変数名、関数名
JSON snake_case user_id APIリクエスト/レスポンス

変換と処理の流れ

タイミング 次の処理 変換前 変換後 言語 備考
リクエスト バックエンド camelCase snake_case JSON
Router Handler snake_case PascalCase(DTO構造体) Go JSONタグによる変換
Handler Usecase DTO構造体 domain構造体 Go 変換してから受け渡す
Usecase Service - - Go domain構造体
Service Repository - - Go domain構造体
Repository Service domain構造体 model構造体 Go 変換してから返却
Service Usecase - - Go domain構造体
Usecase Handler domain構造体 DTO構造体 Go 変換してから返却
Handler フロント DTO構造体 snake_case Go JSONタグによる変換
レスポンス リクエスト snake_case camelCase TS 型変換

レイヤー層(処理層)について

レイヤー層

レイヤー名 役割 用語
handler層 URLと1対1 Controller/Handler
usecase層 ユースケースごとのロジック Usecase/Interactor
service層 ビジネスロジック、個別機能実行 Service
repository層 DBとのやりとり Repository

handler層

ルートとhandlerは1対1の関係です。
処理ごとにhandlerに関数を設けました。

責務は、外部とのやりとりです。
リクエストを受け取り、JSONとしてフロント側に返す役割です。
それ以外の処理はusecase層に渡します。

handler層ではDTO構造体としてデータを保持し、
usecase層へ渡すときに、domain構造体に変換してから渡します。

usecase層

handlerからdomain構造体のデータを受け取ります。
serviceとの橋渡しを行います。

トランザクション処理を行うときは、この層で行いました。
それにより、複数同時登録or更新にて、どれか1つでも処理が失敗するとロールバックされます。
service層との役割を完全に理解しきれていないので手探り状態です。

service層

usecase層からデータをもらい、repository層へ渡します。
1番記述が少ないと感じている層です。

データの保存や画像の保存もこの層で行います。
1つのデータに対して何かしらの処理を行うのがservice層だと思っています。

repository層

DBとのやりとりを行う場所です。
domain構造体を受け取り、model構造体へ変換、返却時はdomain構造体に戻します。

CRUD処理(取得、登録、更新、削除)を担当してくれます。
DBとrepositoryは1対1のイメージです。
そのため、他のテーブルからデータを取りたいときは別repositoryを呼び出します。

外部キーで繋がっている別テーブルに登録をするとき、
repository層でトランザクションを使うことでデータの整合性を保つようにしてます。

usecase層は全体のデータ管理、repositoryは外部キーのあるデータの整合性を保つ役割だと思っています。

フロントエンドについて

TypeScriptを使用し、フロントを作成しています。
変数名はキャメルケースを使うのが一般的らしいので、それに倣ってます。
しかし、APIはスネークケースを使うのが一般的らしいです。

そのため、Goへリクエスト/レスポンスするときは、キャメルケース、スネークケースへの変換をしてます。
思ったより面倒なので、共通化して対応しました。
変換用のライブラリがあるのでそれを使ってます。

npm install camelcase-keys snakecase-keys

Discussion