8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rubyのサンプルソースで見るヘキサゴナルアーキテクチャとDDD

Last updated at Posted at 2018-08-13

ここ最近Vaughn Vernonさんの『実践ドメイン駆動設計』(以下iDDD)を読んでいました。この本では数々のアーキテクチャを紹介してくれているのですが、そのなかでも特に「ヘキサゴナルアーキテクチャ」が取り上げられることが多いのですが、難解でなかなか理解できないでいました。

読み終わってから、いろいろ調べて見た結果、いくつか分かったことがあったため、簡単に紹介したいと思います。

ヘキサゴナルアーキテクチャとは

ヘキサゴナルアーキテクチャはAlistair Cockburnが提唱したアーキテクチャで、Ports and Adaptersとも呼ばれています。原文、及び日本語訳はこちら。

ヘキサゴナルアーキテクチャはシステムを外部と内部の2つの領域に分けてテストのしやすさ、外的環境の変化への適応のしやすさに重きをおいた設計です。内部というのはシステムのビジネスロジックそのもので、外部というのは外部からのリクエスト、例えばhttpやCLIコマンド、メッセージキューと、システムが使う外部の別のシステム、例えばMySQLやメールサーバなどが相当します。

ヘキサゴナルアーキテクチャではビジネスロジックは完全に外部から切り離されているため、それ単体でテストを実行することができます。また、MySQLを使ったテストなどもモックオブジェクトを使うことで簡単にテストを行うことができます。このようにヘキサゴナルアーキテクチャは外部環境を抽象化することが前提になっているため、TDDとも非常に相性が良さそうです。またMySQLからNoSQLに切り替えたりなど、外部環境が変わったとしても外部環境と内部のシステムを仲立ちする"Adapter"を変更するだけで良く、ビジネスロジックを変更する必要がないため、安定性の高いアーキテクチャとなっています。

DDDにおけるヘキサゴナルアーキテクチャとオリジナルのヘキサゴナルアーキテクチャ

iDDDでのヘキサゴナルアーキテクチャは理解するのが大変だったため、一度Cockburnさんの元記事を見ることにしました。

この記事では、入力した元値によって割引率と割引後の価格が計算されるアプリケーションが例として上げられています。またGithubにはそのアプリケーションをRuby+Rackで書き直したものが紹介されており、Rubyやウェブアプリケーションに馴染みのある方にはそちらのほうが理解しやすい内容となっています。

Ruby + Rackでのヘキサゴナルアーキテクチャ

config.ru
hex = SmallerWebHexagon.new(InCodeRater.new)
app = RackHttpAdapter.new(hex,"./src/views/")
run app

config.ruがアプリケーションの入り口になっています。
InCodeRaterは割引率を決定するビジネスロジックが埋め込まれたオブジェクトです。このアプリケーションではここを別の割引率算定ロジックを含んだオブジェクトに変更ことも可能です(ただしrateメソッドを実装していることが前提です)。この例ではドメイン駆動設計は考慮されていないので特にドメインの概念は登場しませんが、InCodeRaterはいわゆるビジネスロジックそのもので、iDDDでいうところのドメイン層に属するものです。少し形を変えていけば、DomainServiceEntityValueObjectにもなりうる内容です。

class InCodeRater
  def rate value
    case
      when value <= 100
        1.01
      when value > 100
        1.5
    end
  end
end

SmallerWebHexagonは引数によって渡された割引率算定オブジェクトを元に割引率と、割引後の価格を計算します。iDDDでいうところのアプリケーション層にあたり、内部でドメインオブジェクトを作成し、アプリケーションのためのタスクの調整をしています。

class SmallerWebHexagon

  def initialize rater
    @rater = rater
  end

  def rate_and_result  value
    rate = @rater.rate(value)
    result = value * rate
    return rate, result
  end

end

最後にRackHttpAdapterオブジェクトが生成され、Rackによってrunされます。

class RackHttpAdapter

  def initialize(hex_app, views_folder)
    @app = hex_app
    @views_folder = views_folder
  end

  def call(env) # hooks into the Rack Request chain
    request = Rack::Request.new(env)
    value =  path_as_number(request)

    rate, result = @app.rate_and_result  value

    out = {
        out_action:   'result_view',
        value:  value,
        rate:   rate,
        result: result
    }

    template_path = Pathname.new(@views_folder).join(out[:out_action]).sub_ext('.erb')
    page = html_from_template_file(template_path , binding)

    response = Rack::Response.new
    response.write(page)
    response.finish
  end

  private

  #以下省略

end

このとおり、Adapterはブラウザからのリクエストを受け取り、パラメータを取得します。
そしてアプリケーションが安全に動くようにパラメータを適切な形に変換します。
このプログラムでは省略されていますが、パラメータなどの入力データのバリデーションもこの層で行われると思います。
そしてrate, result = @app.rate_and_result valueでアプリケーションそのものを実行しています。

最後に、iDDDでというところのプレゼンテーション層の責務もここで実行されています。

この例を読むとヘキサゴナルアーキテクチャそのものは非常にシンプルな構造になっていることが分かります。
ただ、iDDDではヘキサゴナルアーキテクチャのアプリケーション層を、アプリケーション層とドメイン層の2つに分割しているため、構造が複雑になり、理解が難しくなっていると感じました。

まとめ

CockburnさんのサンプルとiDDDでのヘキサゴナルアーキテクチャのレイヤーの対応表を作ってみました。

Cockburnさんの例 iDDD
RackHttpAdapter Adapter 
SmallerWebHexagon Application
InCodeRater Domain

結論

  • iDDD(やその他の図書)を読んでヘキサゴナルアーキテクチャにハマったらCockburnさんの記事を読むといいと思います。
  • rubyやウェブアプリケーションに慣れている人であれば、こちらのサンプルを見ると理解が捗ると思います。
  • ヘキサゴナルアーキテクチャは意外とシンプルです。

今流行りのクリーンアーキテクチャもヘキサゴナルアーキテクチャを参考にしていることもあり、設計思想はとても似ています。今後のためにもぜひヘキサゴナルアーキテクチャを理解してみてください!

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?