🧅
Hexagonal Architecture
はじめに
こんにちは!ビクトルともうします。
おそらく、クリーンアーキテクチャやスリーレイヤーアーキテクチャについて聞いたことがあるかもしれませんが、こんかいHexagonal Architectureについて話したいと思っています。
このパターンはレイヤーごとに分離し、絶縁するためのadaptersとportsと呼ばれます。クリーンアーキテクチャの方法論のように、入力と出力、そしてビジネスロジックからの分離を維持します。入力データの実装によってアプリケーションのコアを保護し、DRYのために我々のビジネスロジックは他のadapterに使うことができます。
まずadaptersとportsを説明しましょう。
adapters
ユーザーとの対話が開始される部分は、portを介して行われます。たとえば、RESTやGRPCコントローラーなどです
ports
我のアダプターに関するエントリです。これらはアプリケーションが他のサービスとの間で通信することを可能にします。たとえば、データベース、サービス、またはメッセージブローカーです。
actorsにたいしてタイプは二つがあります。
driven actors
アプリケーションによってトリガーされるものです。例えば、データベースadapterなどです。
driver actors
driverはインタラクションを開始するものです。アダプタを使用し、ポートを介してデータを渡します。例えば、HTTP handler。
実装してみよ!
さ!大好きなTODO
アプリでどうやってHexagonal Architectを行っていきましょうか?
// Taskドメイン
type TaskID string
type TaskName string
type Task struct {
ID TaskID
Name TaskName
}
// Taskサービスのport
type TaskService interface {
Create(task domain.Task) error
Get() domain.Task
}
// Taskのリポジトリのport
type TaskRepository interface {
Create(task domain.Task) error
Get() domain.Task
}
type TaskHTTPHandler struct {
TaskService
}
// HTTP handler adapter
func (th TaskHTTPHandler) CreateTaskHandler(w http.ResponseWriter, r *http.Request) {
// リクエストbodyをとってモデルに変更
err := th.Create(task)
// エラーハンドリングやってリスポンすを返す
}
type TaskKafkaHandler struct {
TaskService
}
func (th TaskKafkaHandler) CreateTaskInput(data []byte) error {
// イベントやメッセージからdataを取ってモデルに変更
err := th.Create(task)
// エラーハンドリング
}
この方法をクリーンアーキテクチャのレイヤーと比較することができますが、adapterを接続するためにportを使用し、ドメインモデルをアーキテクチャの中心に置いています。
Pros & Cons
Pros
- コードを異なるレイヤーに分割する。
- ポートは別の入出力で再利用できます。
- DDDを使用してドメインをモデル化できます。
- スケーラビリティ。
Cons
- 多きなプロジェクトでは、モデル化するときに注意しないと困難になる可能性があります。
Happy coding!
Discussion