🧅

Hexagonal Architecture

2023/11/21に公開

はじめに

こんにちは!ビクトルともうします。

おそらく、クリーンアーキテクチャやスリーレイヤーアーキテクチャについて聞いたことがあるかもしれませんが、こんかいHexagonal Architectureについて話したいと思っています。

このパターンはレイヤーごとに分離し、絶縁するためのadaptersportsと呼ばれます。クリーンアーキテクチャの方法論のように、入力と出力、そしてビジネスロジックからの分離を維持します。入力データの実装によってアプリケーションのコアを保護し、DRYのために我々のビジネスロジックは他のadapterに使うことができます。

まずadaptersportsを説明しましょう。

adapters

ユーザーとの対話が開始される部分は、portを介して行われます。たとえば、RESTやGRPCコントローラーなどです

ports

我のアダプターに関するエントリです。これらはアプリケーションが他のサービスとの間で通信することを可能にします。たとえば、データベース、サービス、またはメッセージブローカーです。

hexagonal architecture diagram

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