技術書典5にて販売した "pospomeのサーバサイドアーキテクチャ" をBOOTHから購入できるようにしました。

技術書典5にて販売した "pospomeのサーバサイドアーキテクチャ" をBOOTHから購入できるようにしました。

以下から購入できます。
価格は技術書典と同じ1000円です。
booth.pm

技術書典に来てくださった方への特典として価格を少し上げたり、
内容を落としたりしようと思いました。

しかし、遠方のため行けない人がいたり、
予定があって行けない人がいたり、
行きたくても行けない場合もあるので、
特典というのも少し違うかなと思い、価格は据え置き、内容も同じという形で販売しました。

次の技術書典では当日来てくださった方向けの特典でも用意しようかなと思っています。
ちゃんとしたイラストを表紙にした物理本をプレゼントしたり、
一部の内容を落としたりしようかと。
落とす内容は独立して切り離せる内容にしないといけないので、次回はそれも考えて書く必要がありますね。

【10/5 更新】技術書典5にサークルとして参加します。

技術書典5にサークルとして参加することになったので、
書籍の詳細についてまとめました。

techbookfest.org

[追記] BOOTHから購入できるようにしました。
pospome.hatenablog.com


書籍のざっくり情報は以下です。

  • 書籍の目次はこちら
  • サーバサイドのアプリケーションアーキテクチャに関する書籍
  • ページ数は155ページ
  • PDF版のみ
  • 価格は1冊1000円
  • 購入していただけるとその場でQRコードが印刷されている紙を渡すので、そこからPDFをDLしてください。
  • サンプルコードはGo言語ですが、サーバサイドのアプリケーションアーキテクチャがメインなのでサーバサイドのアプリケーションエンジニアであればGo言語に関わらず役に立つはず
  • かんたん後払いには対応していません

当日まで可能な限り内容を精査するので、内容が一部変更されるかもしれません。
当日は見本誌を用意するので、実際に内容を確認してから購入してください。
詳しくは以下です。

概要

サーバサイドのアプリケーションアーキテクチャに関する書籍です。
サンプルコードはGo言語ですが、
アプリケーションアーキテクチャに関する内容がメインなので、
普段はGo言語以外の言語を利用している方も理解できると思います。
Go言語に特化した章も存在します。

かんたん後払い未対応

後払いの存在を忘れていて申請するのを忘れてしまいました・・・。
すみません・・・。

単なるポエム

普段自分が考えていることをアウトプットしたので、正しいとは限りません。
そもそもアプリケーションアーキテクチャは数値や実際の動作で正しさを保証しづらいものなので、
単なるポエムでしかないです。

参考にした書籍が2,3冊あるくらいでしょうか・・・。
ネットでもいろいろと調べようとしたのですが、
今回の書籍で扱う内容がネットに載っていない掘り下げたものであったり、
ネットだと言ってることが皆違うので参考にならなかったりしたので、
すぐに諦めて自分の考えをアウトプットするだけにしました。

そのため正しさを保証することはできません。
間違ってたらごめんなさい。

技術書典5以外での販売について

技術書典以外での販売は考えていません。
というのも、どの程度需要があるか分からないからです。
需要がないのに販売しても意味ないので、
技術書典5が終わっても需要ありそうだなと思ったら技術書典以外での販売を検討しようかと思っています。

書籍の内容をブログで公開することもありません。

勉強会などの登壇資料として書籍の内容を取り上げるかもしれませんが、
登壇資料としてまとめられるボリュームでもないので、
取り上げるとしても一部の内容のみになると思います。

PDF版のみ

以下の理由により物理本は用意していません。

  • サークル初参加なので、どのくらい用意すればよいのかが分からなかった。
  • スケジュールを守れなさそうだった。
  • 準備するのが面倒だった。

QRコードからのDL

購入していただけるとその場でPDFファイルへのQRコードが印刷されている紙を渡すので、そこからDLしてください。
本当はそれっぽいカードみたいなのを作ろうと思ったのですが、
執筆に追われ、そんなことしている余裕がなかったので紙になりました。
紙でもカードでもDLできるという点は一緒なので問題はなさそうだと思っています。

読むと頭が疲れる

実装パターンの書籍なので読むと頭が疲れます。
ポエムということもあり、わけわからんと思うかもしれません。
自分が普段書くブログくらいのクオリティです。

擬似コード

サンプルコードはGo言語ですが擬似コードです。
一部の実装を省略していたりします。
以下が実際に書籍に載せているコードです。

type UserRepository interface {
	InsertUser(user *User) error
}

type userRepository struct {
	conn *memcache.Conn
}

func NewUserRepository(conn *memcache.Conn) UserRepository {
	return &userRepository{
		conn: conn,
	}
}

func (u *userRepository) InsertUser(user *User) error { 
        //省略
        //memcache へ User を永続化する
}

擬似コードだからといって内容が理解できないことはないと思いますが、
当日は見本誌で具体的なコードを確認してみてください。

目次

目次は以下です。
内容は変わる可能性があります。

第1章 Webアプリケーションにおけるレイヤ構造パターン集
	はじめに ... 7
	レイヤとは? ... 7
	レイヤ構造のメリットとデメリット ... 8
	コントローラーパターン ... 9
		パッケージ構成
		レイヤの責務
		メリット
		デメリット
		使い所
	MVCパターン ... 12
		パッケージ構成
		レイヤ同士の依存関係
		レイヤの責務
		メリット
		デメリット
	MVC+サービスレイヤパターン ... 20
		パッケージ構成
		レイヤ同士の依存関係
		レイヤの責務
		サービスレイヤの実装
		メリット
		デメリット
	レイヤードアーキテクチャ ... 33
		パッケージ構成
		レイヤ同士の依存関係
		レイヤの責務
		レイヤ依存パターン
		メリット
		デメリット
	ヘキサゴナルアーキテクチャ ... 41
		レイヤ同士の依存関係
		ヘキサゴナルアーキテクチャの目的
		アプリケーションと技術を分離する仕組み
		パッケージ構成
		レイヤの責務
		ヘキサゴナルアーキテクチャの本質
	クリーンアーキテクチャ ... 54
		レイヤ同士の依存関係
		パッケージ構成
		レイヤの責務
		インプットポートとアウトプットポート
		インプットポートとアウトプットポート以外のポート
		クリーンアーキテクチャの本質
		ちゃんとしたクリーンアーキテクチャ実装は少ない
		メリット
		デメリット
	レイヤードアーキテクチャ+ポート ... 74
	その他のレイヤ紹介 ... 82
	レイヤの肥大化と正当性 ... 85
	レイヤ内をどう実装するのか ... 86
	どのレイヤを採用すべきか ... 88
	おわりに ... 88

第2章 WebアプリケーションにおけるGo言語のパッケージ構成
	はじめに ... 89
	なぜ分からないのか? ... 89
	Go言語のパッケージ構成 ... 90
		フラットパッケージとは
		シングルパッケージとは
	フラットパッケージの実装 ... 93
	フラットパッケージ vs レイヤ構造 ... 98
		テスト
		初期学習コスト
		継続的な学習コスト
		新規開発
		OSS開発
		パッケージのインポートとIDEの補完
		コードの質
	おわりに ... 105

第3章 レイヤ構造で学ぶパッケージの横割りと縦割り
	はじめに ... 106
	パッケージの横割りとは ... 106
	パッケージの縦割りとは ... 107
	パッケージの縦割りが抱える問題 ... 111
		共通実装
		機能間の依存
	縦割りを扱う際のポイント ... 119
		共通実装の扱い
		各レイヤ内実装
	縦割りとDB設計 ... 128
		依存関係のコントロール
		マイクロサービスへの切り出し
	パッケージの縦割りにおける機能パッケージの分割粒度 ... 129
	Go言語におけるフラットパッケージと縦割り ...130
	おわりに ... 131

第4章 詳解リポジトリパターン
	はじめに ... 132
	リポジトリパターンとは ... 132
	リポジトリパターンとDAOの違い ... 135
	複数のデータソースを扱う ... 137
	モデルの部分的なCRUD操作 ... 138
	Read操作におけるモデル以外の戻り値 ... 141
	実装に対する依存 ... 142
	ビジネスロジック ... 143
	トランザクション境界 ... 145
	トランザクションの抽象化 ... 145
	Go言語におけるグローバル変数を利用したDIの欠点 ... 151
	おわりに ... 154

decorator, presenter, exhibit という3つの実装パターンについて

@a_suenami さんのこのツイートの Decorator, Presenter, Exhibit が気になったので調べてみた。


結果、以下にまとまっていたので、記事中のコードを引用しながら色々と考えてみる。
http://mikepackdev.com/blog_posts/31-exhibit-vs-presenter





Decorator とは?

Decorator とは、おそらく GoFの DecoratorPattern のことだと思う。
元記事では最初の方に Car クラスで説明されている。

以下は元記事のコードだが、見れば大体分かると思う。

# ruby の SimpleDelegator によって Car のメソッドを delegate している。
class Decorator < SimpleDelegator
end

class Car
  def price
    1_000_000
  end
end

class CarWithHeatedSeats < Decorator
  #Car.price を CarWithHeatedSeats.price でオーバーライドしている
  def price
    super + 5_000
  end
end

car = Car.new
car.price #=> 1000000

#Car を CarWithHeatedSeats でラップしている。
#ラップに関しては ruby の SimpleDelegator がいい感じにやってくれている。
car = CarWithHeatedSeats.new(car)
car.price #=> 1005000

CarWithHeatedSeats で Car をラップすることで、
本来の Car.price の挙動に任意の挙動を加えることができる。
上記の例だと +5000 されているのが任意の挙動にあたる。

さらに、目的によっては以下のように Car に存在しないメソッドを実装して、
Car を拡張したオブジェクトを作ることもできる。

class CarWithHeatedSeats < Decorator
  #Car.price を CarWithHeatedSeats.price でオーバーライドしている
  def price
    super + 5_000
  end

  # Car を拡張するメソッド
  def hello
  	#省略
  end

end


目的別に CarWithHeatedSeats のような Decorator を作って Car をラップすれば、
Car を目的別に拡張することができる。
継承を利用しないオブジェクトの拡張手段といったところだろうか。

Presenter とは?

Presenter は "表示に関するロジック" を責務に持つ実装パターンであり、
結構一般的なパターンだと思う。

元記事では以下のように description という表示に関するロジックを持ったメソッドを実装している。

class CarPresenter < Decorator
  def description
    if price > 500_000
      "Expensive!"
    else
      "Cheap!"
    end
  end
end

car = CarPresenter.new(Car.new)
car.price #=> 1000000
car.description => "Expensive!"



"表示に関するロジック" とはなんだろう?
具体的には以下が該当するかなと思っている。

  • User クラスの status の値に応じて "プレミアム会員", "一般会員" のように文言を出し分ける。
  • int の金額をカンマ区切りの文字列にする。
  • User クラスの name に "様" を付ける。

こういった Viwe 周りのロジックはサーバサイドのビジネスロジック自体にはあまり関係ないかもしれないが、
ユーザーに見せる部分なので色々と気を使って加工することが多い。

これらをモデルである Car クラスに全て実装してもいいが、
Car クラスはどんどん肥大化するだろう。
この肥大化を避けたい場合、 Presenter を導入すると良い。

Exhibit とは?

Exhibit は "描画" を責務に持つ実装パターンである。

以下は記事中の Exhibit のコード。
何をやっているかは見れば大体分かると思う。

class CarWithTwoDoorsExhibit < Decorator
  def initialize(car, context)
    @context = context
    super(car) # Set up delegation
  end

  def additional_info
    "Some cars with 2 doors have a back seat, some don't. Brilliant."
  end

  def render
    @context.render(self)
  end
end

class TextRenderer
  def render(car)
    "A shiny car! #{car.additional_info}"
  end
end

class HtmlRenderer
  def render(car)
    "A <strong>shiny</strong> car! <em>#{car.additional_info}</em>"
  end
end

car = CarWithTwoDoorsExhibit.new(Car.new, TextRenderer.new)
car.render #=> "A shiny car! Some cars with 2 doors have a back seat, some don't. Brilliant."
car.price #=> 1000000

car2 = CarWithTwoDoorsExhibit.new(Car.new, HtmlRenderer.new)
car2.render #=> "A <strong>shiny</strong> car! <em>Some cars with 2 doors have a back seat, some don't. Brilliant.</em>"

CarWithTwoDoorsExhibit は特定の文言を テキスト or HTML で表示することができる。
表示する文言も additional_info() にて定義されている。
文言表示には Car が持っている情報が必要なので、コンストラクタでは Car を引数にとっている。

これを見たときに、
フルスタックな WAF についている HTML Helper, View Helper 的な印象を受けた。

自分は CakePHP を使った経験があるが、
以下のように HTML に PHP のタグを書いて form を表示していた。

<?php echo $this->Form->create($article); ?>


PHPの例は HTML の form を生成するためのヘルパーであるが、
"create() の引数に $article というオブジェクトを渡すことで、特定の HTML を描画する"
という性質は Exhibit に近いものがあるのではないか? と思っている。

Presenter と Exhibit の違いは?

  • Presenter = "表示に関するロジック" を責務に持つ
  • Exhibit = "描画" を責務に持つ

と説明したが、言葉で説明すると違いが抽象的になってしまい、なかなか難しい。

Presenter のセクションで以下は Presenter が持つべきロジックであると紹介しているが、

  • User クラスの name に "様" を付ける。

「"様" を付けるのはロジックというよりは、"描画" の責務ではないのか?」
と反論されると、
「そうかもしれない」と感じてしまう。

しかし、このような "値を加工するロジック" を Exhibit の責務にしてしまうと、
Presenter が持つべきロジックがかなり限定されてしまい、
Exhibit が肥大化してしまう可能性が高くなる。
個人的には Presenter に実装するだろう。

ということで、個人的な感覚になってしまって申し訳ないが、
Presenter は View に表示する変数を加工するための責務を持ち、
Exhibit はその変数を利用する View 周りのパーツ(HTMLのタグや文章)を描画する責務を持つイメージがある。

これは "View を描画する" という仕組みを考えたときに、
責務としてどう分割するか? を考えた結果である。
ココらへんの責務に関しては "責務を分ける必要性" というセクションでも触れるので、
そちらも読んで欲しい。

Decorator, Presenter, Exhibit の関係性

関係性としては以下になる。

  • Decorator = オブジェクトを拡張する実装パターン
  • Presenter = "表示に関するロジック" を責務に持つ Decorator
  • Exhibit = "描画" を責務に持つ Decorator

Presenter も Exhibit も Decorator という実装パターンを利用しているという点でいうと同じであるが、責務は異なる。

Presenter + Exhibit

Presenter, Exhibit はそれぞれ異なる責務を持つので、
当然ながら組み合わせることができる。

以下は記事中のコードだが、見れば大体分かるはず。
Presenter が内部に Exhibit を保持しており、
render() が利用できるようになっている。

class CarPresenter < Decorator
  def initialize(car)
    exhibit = rand(2) == 1 ? CarWithTwoDoorsExhibit : CarWithFourDoorsExhibit
    super(exhibit.new(car, TextRenderer.new))
  end

  def description
    if price > 500_000
      "Expensive!"
    else
      "Cheap!"
    end
  end
end

car = CarPresenter.new(Car.new)
car.description #=> "Expensive!"
car.render #=> "A shiny car! ..."

この CarPresenter は以下の機能を持つ。

  1. description() にて特定の文言を出し分ける。
  2. render() にて特定のテキストを出し分ける。

ユースケースとしては、CarPresenter を View に渡して、View 内で利用する想定なのだろうか?
以下は PHP のコードだが、こんなイメージ?

<html>
	<?php $car_presenter.description() ?>
	<?php $car_presenter.render() ?>
</html>



記事中には載っていないが、Exhibit が Presenter を持てば、
Presenter で加工した値を Exhibit が利用できるようになる。

以下は CarExhibit が CarPresenter を持ち、
render() にて CarPresenter.price() を利用するパターン。
個人的にこちらの方がしっくりくる。

#ruby書けないのでシンタックス間違っているかもしれません・・・。
class CarExhibit
  def initialize(car_presenter)
  	@car_presenter = car_presenter
  end

  def render
  	"this is " + @car_presenter.price()
  end
end



責務を分ける必要性

Presenter, Exhibit はそれぞれ別の責務を持ち、組み合わせることができると説明したが、
そもそも Presenter, Exhibit で責務を分ける必要はあるのだろうか?

直前のセクションで以下のように Exhibit に Presenter を持たせていたが、

class CarExhibit
  def initialize(car_presenter)
  	@car_presenter = car_presenter
  end

  def render
  	"this is " + @car_presenter.price()
  end
end



以下のようにPresenter.price() のロジックを直接 Exhibit に持たせてもいいのではないか?
こうすれば、わざわざ Presenter を実装する必要はなくなる。
Exhibit は "描画" を責務に持つので、描画に必要なメソッド(今回は render() になる)のみ公開している。
price() のようなロジックは private なメソッドにした方が良いだろう。

class CarExhibit
  #コンストラクタには Car クラスを渡す
  def initialize(car)
  	@car = car
  end

  def render
  	"this is " + price()
  end

  private
    def price
    	#CarPresenter.price() 相当のロジックを実装する
      @car.price() + 5000
    end
end



結論から言うと、 "描画する" という目的を達成するのであれば問題ないと思う。
わざわざ CarPresenter を用意する必要はなさそう。

では、Exhibit にロジックを実装すれば Presenter は不要なのか?
というと、そうでもない。
Presenter を用意することによって View で利用する値を各所で使い回しやすくなる。

例えば、
User クラスの status の値に応じて "プレミアム会員", "一般会員" のように文言を出し分ける
という仕様があった場合、
"プレミアム会員", "一般会員" のような文言は複数の画面で表示する必要があるかもしれない。

その場合、 Exhibit で実装してしまうと、文言を出し分けるロジックが複数の Exhibit でコピペされてしまう可能性がある。

具体的なコードで説明すると以下の price() のようなロジックが、

class CarExhibit
  #コンストラクタには Car クラスを渡す
  def initialize(car)
  	@car = car
  end

  def render
  	"this is " + price()
  end

  private
    def price
  	  #CarPresenter.price() 相当のロジックを実装する
      @car + 5000
    end
end


以下のように各 Exhibit にコピペされる可能性がある。

class XxxExhibit
  #省略

  def render
  	"Xxx is " + price()
  end
  
  private
    def price
      @car + 5000
    end
end

class YyyExhibit
  #省略
  
  def render
  	"<b>Yyy is " + price() + "</b>"
  end

  private
    def price
      @car + 5000
    end
end


Exhibit は "描画" の責務を持つので、
render() によって出力される文字列を利用するのが目的となる。
そして、render() には HTML のタグのような "特定のページでしか利用しない装飾" を扱う可能性があるので、
Exhibit 同士を組み合わせることが難しいケースがある。
そういった場合、安易にロジックをコピペすることで解決してしまうかもしれない。

では、 private になっている price() を public にしたらどうだろう?
public にすれば、Exhibit 同士を組み合わせることでコピペを防ぐことができる。

以下のように XxxExhibit にて CarExhibit.price() を参照するイメージ。

class XxxExhibit
  #コンストラクタに CarExhibit を渡す
  def initialize(car_exhibit)
  	@car_exhibit = car_exhibit
  end

  def render
  	"Xxx is " + @car_exhibit.price()
  end
  
end



しかし、CarExhibit には render() のように描画の責務を持つメソッドが実装されているので
CarExhibit を XxxExhibit に渡してしまうと、
"price() で算出される値のみ利用したい" という目的に対して、
CarExhibit の責務が大きすぎる気がする。

例えば、以下のように CarExhibit.render() を利用することも可能になってしまう。

class XxxExhibit
  #コンストラクタに CarExhibit を渡す
  def initialize(car_exhibit)
  	@car_exhibit = car_exhibit
  end

  def render
  	"Xxx is " + @car_exhibit.price()
  end

  #render() が利用できてしまう
  def xxx
  	"Xxx is " + @car_exhibit.render()
  end
  
end

一見便利そうに見えるが、
実際に Exhibit が持つメソッドは render(), price() だけとは限らない。
Car が持つそれぞれの値に対して price() のようなロジックを持つことも考えられる。

Exhibit オブジェクトが大きくなればなるほど想定外の依存が発生する可能性は高くなる。
「特定の Exhibit の特定のメソッドを修正したら想定外の Exhibit に影響してしまった」
なんてことがありえるかもしれない。

Exhibit のロジックを Presenter に切り出せば、各オブジェクトの責務は小さくなる。
基本的にクラスや関数は責務が限定されればされるほど再利用性が上がり、
修正による影響を予想しやすくなるので、
Exhibit にロジックを実装するのが常に正しいとは限らない。

このように Exhibit, Presenter で責務を分けるかどうか? というのはケースバイケースである。
自分たちのケースに合った最適な実装を選択しよう。

Presenter, Exhibit を導入する必要性

ここまで Presenter, Exhibit について説明してきたが、
そもそもこれらのパターンは必要なのだろうか? というのも考えなければいけない。

開発方針に依存するが、
サーバサイドで HTML をレンダリングするようなWebサービスでない限り、
View 周りの値の装飾や加工はクライアント側がやってくれるかもしれない。
その場合、サーバが View 周りのロジックを持つ機会というのは減ってくるだろう。

Presenter, Exhibit をわざわざ導入するまでもないケースだって当然ありえるはずなので、
そもそも必要なんだっけ? というのも考える必要がある。

ちょっとした加工であれば、
Controller でチャチャッとやってしまうというのもありだろう。
それが問題になったときに Presenter, Exhibit を導入すればいい。

ちなみに、自分は以下のように View 用の値を全て詰め込むオブジェクトを用意して、

package response

type UserListPage Struct {
	ID int
	Name string
}



モデルからそのオブジェクトを生成するような関数を実装するだけで済ますことが多い。
最初はこの程度で十分やっていけたりするし、
この程度であれば Controller にべた書きしてもいいと思う。

func NewUserListPage(user *User) *UserListPage {
	return &UserListPage {
		ID: user.ID,
		Name: user.Name + " 様", //ここで値の加工をしてしまう
	}
}



値の加工ロジックが複数箇所で必要になる場合は、
そのロジックを関数に切り出してまとめてしまう。

func NewUserListPage(user *User) *UserListPage {
	return &UserListPage {
		ID: user.ID,
		Name: displayUserName(user.Name), //切り出した関数を利用する
	}
}

//関数に切り出す
func displayUserName(name string) string {
	return name + " 様"
}



しかし、切り出し対象の関数が多いと、
切り出した関数に Prefix, Saffix が多くなってきて管理が辛くなってくることがある。
そうなると、 Presenter 相当のオブジェクトで管理したくなってくる。
1つのモデルに対して1つの Presenter を用意する感じ。

func NewUserListPage(user *User) *UserListPage {
	p := NewUserPresenter(user)
	return &UserListPage {
		ID: p.ID,
		Name: p.Name, //UserPresenter を利用する
	}
}

type UserPresenter struct {
	ID int
	Name string
}

func NewUserPresenter(user *User) *UserPresenter {
	//省略	
}



上記は Decorator を利用しているわけではないのだが、
NewUserListPage() という関数が Exhibit のような責務(= 描画の責務)を持っていて、
UserPresenter が Presenter のような責務(= 表示に関するロジック)を持っている感じになっている。

このように具体的な実装方法は異なっていても、
結局 "描画" と "表示に関するロジック" という責務に分ける感じになるので、
Presenter, Exhibit 相当のものを必要に応じて好みの実装方法で導入していけばいいと思っている。

まとめ

特にないです。

記事中では "View を描画する" という HTML を描画するようなケースを前提の内容でしたが、
JSON でも値の加工は必要なので、考え方自体は変わらないかなという感じです。

"すえなみチャンス" というのを聞いて "早川チャンス" を思い出しました。
全然別物なのですが、懐かしい気持ちになりました。

ruby のコードは雰囲気で書いたので、おそらく間違っていると思います。
なんとなーくなイメージが伝われば十分かなーと思い、横着してしまいました。

マサカリあれば、 twitter or ブログのコメント欄に連絡いただければと思います。

Go における FunctionalOptionPattern と MethodChaining について考える

きっかけ

この記事を書くきっかけは以下のブログで、
MethodChaining の代わりに FunctionalOptionPattern を利用したという記事。
https://www.calhoun.io/using-functional-options-instead-of-method-chaining-in-go/

この記事は @ono_matope さんのツイートで知った。




このツイートにリツイートした結果、
@hnakamur2 さんも加わり、3人でちょっとした議論をした。
議論の内容については以下を追ってもらえればと思う。
https://twitter.com/ono_matope/status/967788305830985728
*本体同士で面識もない方々とこういった議論ができるあたり、ツイッターって便利ですね。

せっかくなので、
上記ツイッター上でのやりとりを整理して、
自分なりの結論を書いてみようと思う。


FunctionalOptionPattern

これについては特に何も言及することはない。

FunctionalOptionPattern については以下を読むといい。
https://qiita.com/weloan/items/56f1c7792088b5ede136

この記事によると原典は以下なので、2014年からあったパターンみたい。
https://commandcenter.blogspot.jp/2014/01/self-referential-functions-and-design.html


MethodChaining

ここからが本題。

以下の記事は golang で MethodChaining の代わりに FunctionalOptionPattern を利用したという内容になっている。
https://www.calhoun.io/using-functional-options-instead-of-method-chaining-in-go/

ここからは自分の考えも交えて上記の記事を説明していくので、
記事中に載っていない内容を書いたり、
載っている内容を書かなかったりするかもしれません。
元記事を読んでから読み進めた方がいいと思います。


MethodChaining の問題点

なぜ MethodChaining の代わりに FunctionalOptionPattern を利用したかと言うと、
golang の戻り値でエラーを返すという言語仕様とMethodChainingの相性が悪いから。

java などの例外が存在する言語であれば、以下のようにメソッドをチェインすることができる。

User user = new User.Builder()
  .name("Michael Scott")
  .email("michael@dundermifflin.com")
  .role("manager")
  .nickname("Best Boss")
  .build();



しかし、golang の場合は戻り値でエラーを返すのでメソッドをチェインできない。
以下のように毎回エラーチェックをする必要がある。

var db *gorm.DB
var err error

db, err = db.Where("id = ?", 123)
if err != nil { ... }

db, err = db.Where("email = ?", "jon@calhoun.io")
if err != nil { ... }



このように golang のエラーハンドリングの言語仕様と MethodChaining の相性が悪いという点が問題となっている。


Error フィールドによる解決方法

golang の言語仕様と MethodChaining の相性が悪いという点はあるものの、
struct に Error フィールドを持たせることで、
それっぽく実装することができる。

記事中で例として扱われていたのは GORM 。

GORM は gorm.DB という struct に Error フィールドを持たせ、
MethodChainingによるエラーを格納しているらしい。
https://github.com/jinzhu/gorm/blob/48a20a6e9f3f4d26095df82c3337efec6db0a6fc/main.go#L15

MethodChainingが完了したタイミングで、
DB.Error が nil かどうかをチェックすることで、
エラーを補足できるようになっている。
http://gorm.io/docs/error_handling.html#Error-Handling

DB.Error のおかげで各メソッドで error を返す必要がなく、
メソッドをチェインすることができる。

if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {
  // error handling...
}



Error フィールドによる解決方法の問題

しかし、Error フィールドによる解決方法にも問題がある。

1. 各メソッドでエラーが発生しないような印象を受ける

DB.Error がエラーを格納するので、
チェイン対象の各メソッドは error を返さない。

そのため、そのメソッドに対してエラーにならない操作のような印象を受ける。
しかし、実際はエラーが発生する可能性がある。

おおげさに言うと、
エラーが発生するという危機感が薄れてしまう。


2. エラーチェックを忘れそう その1

golang のエラーハンドリングは戻り値でエラーを補足するので、
MethodChaining後に DB.Error のエラーチェックをするのを忘れそう。


3. エラーチェックを忘れそう その2

gorm は Open() する際は error を返す。

func Open(dialect string, args ...interface{}) (db *DB, err error) {
	//省略
}

https://github.com/jinzhu/gorm/blob/48a20a6e9f3f4d26095df82c3337efec6db0a6fc/main.go#L44

しかし、DB.Error のエラーチェックも必要なので、
「DB.Error には、gorm.Open() 時のエラーも格納されるはず」という思い込みがあると、
gorm.Open() 時に発生するエラーをハンドリングせず、
結果的に不具合につながる。

逆に DB.Error のチェックが不要な場合でもチェックするようなコードを書いてしまうこともある。

ちなみに、
gorm.Open() で発生するエラーが DB.Error に格納されるかどうかは調べていません。
あくまで「DB.Error には gorm.Open() のエラーが格納されない」と仮定した話です。


FunctionalOptionPattern による解決方法

FunctionalOptionPattern を利用すると、以下のような書き方になる。
MethodChaining の問題点は解消されているはず。

var user User
err = db.First(&user,
  Where("email = ?", "jon@calhoun.io"),
  Where("id = ?", 2),
)
if err != nil {
  panic(err)
}
fmt.Println(user)



MethodChaining としての解決方法

記事の内容としては、FunctionalOptionPattern を使おうっていう内容なんだけど、
記事中には以下の記載がある。

Note: One way to fix this would be to update methods like First and Find in GORM to return both the gorm.DB and an error when called, but I’m not 100% sure how this would affect the rest of the library. Instead we are going to explore an alternative approach that I prefer anyway

First(), Find() というメソッドが gorm.DB と error を返すようにする修正方法もある とのこと。

これはおそらく、以下のイメージだと思う。

db, _ := db.Open()
db, err := db.Where().Where().Find()
if err != nil {
	panic()
}



実装は以下。
MethodChainingの最後に Find() を実行させるので、
Find() だけが error を返せばいい。

package db

func Open() (*DB, error) {
	return DB{}, nil
}

type DB struct {
	err error
}

func (d *DB) Where() *DB {
	if xxx {
		d.err = errors.New("error!")
		return d
	}
	return d
}

func (d *DB) Find() (*DB, error) {
	return d, d.Err
}



クエリの構築であれば、
Build() で error を返すようにして、
Find() はチェインできるようにしてもいいと思う。

func (d *DB) Find() *DB {
	return d
}

func (d *DB) Build() (*DB, error) {
	return d, d.err
}



まあ、どちらにしろ、チェインの最後で error を返すようにしてあげればいいはず。

これによって、MethodChainingを利用するケースでも、
Error フィールドの問題点は解決することができる。


Error フィールドの問題点は本当に問題なのか?

元記事にある Error フィールドの問題点は、そもそも問題なのだろうか?

以下では、struct に Error フィールドを持たせることで、
エラーチェックの繰り返しを避けるテクニックが紹介されている。
https://blog.golang.org/errors-are-values

上記の記事でも紹介されているように、
標準パッケージの scan は Error フィールドを持ってるし、
https://github.com/golang/go/blob/f7ac70a56604033e2b1abc921d3f0f6afc85a7b3/src/bufio/scan.go#L38

エラーをチェックするためのメソッドも提供している。
https://github.com/golang/go/blob/f7ac70a56604033e2b1abc921d3f0f6afc85a7b3/src/bufio/scan.go#L90

エラーを戻り値で返すよりも、分かりやすいかどうか? という観点だと、
劣るかもしれないが、
少なくとも Error フィールドを持たせる実装がアンチパターンというわけではないはず。
なので、そもそも問題ではないのかもしれない。

元記事の筆者も より良い方法は FunctionalOptionPattern だよ と言っているだけで、
MethodChainingを否定しているわけではないと思う。
(わからんけど・・・


どちらを使うべきなのか?

正直、好みの問題なのかなーと思っている。

FunctionalOptionPattern はオプションの実装に interface を利用しているので、
ライブラリを利用する側が独自の実装を適用させることができる。
なので、不特定多数が利用するライブラリであれば、 FunctionalOptionPattern の方が適している気もする。

しかし、MethodChaining もメソッドの挙動を interface によって差し替えるような実装にすることで、
同じようなことが可能なので、
実装の差し替えは決定打にはならないなーというのが正直なところ。

FunctionalOptionPattern の方が実装が若干面倒な気がするけど、
MethodChaining に interface を導入した場合、
どちらも大して変わらないと思う。

個人的には、

MethodChaining = オブジェクトを組み立てるための実装パターン
FunctionalOptionPattern = オプションを指定するための実装パターン

という認識がある。

どちらを使っても目的は達成できるが、
そもそも用途が異なるかなーと思っているので、
とりあえず、上記の認識通りに使い分けていこうかなーと思っている。

そして、MethodChaining を利用する場合、
MethodChaining としての解決方法 を適用するかどうかも気になってくるが、
これもどっちでもいいと思う。

ただ、個人的には、戻り値で戻ってくる方が分かりやすいかなーと思うので、
適用可能であれば適用するかなーというところ。

まとめ

FunctionalOptionPattern or MethodChaining を悩むんだったら、
もっと他に悩むべきコードがありそうなので、
そっちを優先した方がいいと思う。