「エンジニアリングマネージャは必要か?」

昨日はコレに行ってきた。というか、主催側の一人かな。

devlove-kansai.doorkeeper.jp

そのダイアログの前にトイレに行ってたら話す場所がなくなっちゃったから相手してーって @yohhatu のところに行って二人で立ち話をしてた。

「じゃ、ちょっと深掘りしてみる?」みたいな感じで「エンジニアリングマネージャは必要か?」を考えてみようよってなった。

その結果としての僕の考えを書いておく。あとでエンジニアリングマネージャ関係のコミュニティを色々見てみようと思う。色々既に議論されてると思うので。

## 「エンジニアリングマネージャは必要か?」の僕の答え

必要だと思う。

理由は?ってことだけど、その前に僕が必要だと思うマネージャロールを書いておく。

## プロジェクトをやるときのマネージャ

僕はこの4つのマネージャロールがあるよなーって思った。

  • プロダクトマネージャ
  • ディレクター
  • デベロップメントマネージャ
  • プロジェクトマネージャ

### プロダクトマネージャ

プロダクトの方向性を決める人。

### ディレクター

UXとかデザインを決める人。

### デベロップメントマネージャ

技術的なことを決める人。

### プロジェクトマネージャ

プロジェクトの面倒を見る人。

## プロジェクトの外のマネージャ

として、メンバーの育成やキャリアパスの相談とかそういう人の成長のマネージャもいる。これは僕のいる会社ではジェネラルなマネージャと呼ばれてたりする。

## エンジニアリングマネージャはどこ?

って考えたときに。上記のマネージャの中で「今の組織に足りてない部分のマネージメントをやってる人」なんじゃないかなーって思った。

例えば、プロジェクトマネージャをやる人がいないチームで、デベロップメントマネージャがその部分もやってる、みたいな状態。

ふわっと色々やってるけど、僕って何のマネージャだっけ・・・?みたいな気持ちになってるところに「エンジニアリングマネージャ」って言葉が出てきて「あー!それかもー」ってなってるのかなぁって。

ということで、組織の形に合わせて今足りない部分を補いつつ複数のロールを担っている人、のことをエンジニアリングマネージャと呼んでいるのではないかと思った。ので、そういう人は必要だよなー、というかそういう人がいないと何も動かないよなー。って思ったのだった。

サービスエンジニアとサービス開発と3年後

最近何人かからキャリアパスの相談を受けてて、話をしているうちに自分の考えが少し整理できた気がするので忘れる前にメモ。雑記。

## サービスエンジニア

僕の今いる部署に求められてるエンジニアは、技術をコアにしたエンジニアじゃなくて、サービスをコアにしたエンジニアだと思ってる。図の左側。技術を突き詰めて「この技術で何ができるか」というよりも、サービスのことを考えて「このサービスのためにあの技術が使えないか」という感じ。

f:id:bufferings:20181205074057p:plain

## アプリ開発とサービス開発

Webアプリの開発経験が10年以上あります、って人よりも、新卒の2,3年目の人の方が頼りになったりして、どういうところなんだろうなぁ?って思ってたんだけど。アプリ開発スキル、と、サービス開発スキル、が別のスキルってことなのかもしれない。

サービスを開発するときは、アプリの機能を実装するだけじゃなくて、全体のアーキテクチャ、インフラ、ビジネス側の運用、開発側の運用、カスタマーサポートどうするか、社外との連携どうするか、モニタリングどうするか、とか全部含めて考える。それに、そもそもふわっとした要望を元に生み出していくし。正解がないから失敗することを受け入れて前進するしかない。

ので、経験が長くてもアプリ開発だけをやってきた人はサービス開発のスキルがあんまりないし、新卒2,3年目でもサービスを任されてるエンジニアはアプリ開発のスキルが低くてもサービス開発のスキルが身につき始めてる。そして、サービスのことを話すときには、サービス開発スキルがある方が頼りになる。ということなのかなと。

前者の場合も別に悲しむ必要はなくて、むしろ土台がしっかりしてるので、そこを軸足にしてサービス開発スキルに目を向けていけば成長速度は速いと思う。

## エンジニア出身マネージャ

もし、エンジニアの経験を活かしたマネージャになりたかったら、アプリ開発スキルよりも、サービス開発スキルの方が重要になってくるのかなと思った。Java 12でどんな機能が追加されるかまでは知らなくていいけど、サービス全体を見てポイントを押さえる方がメインになりそう。そこに自分の強みを持って、マネージメントのスキルを広げていけば良いのかなと。

## シニアエンジニア

もし、シニアエンジニアになりたかったら、アプリ開発とサービス開発の両方のスキルをのばしていけば良さそう。Java 12の機能は知ってる方がいい。僕はこっち。

## 3年後にはチームを離れるつもりで

とまぁ、キャリアパスのことをぼんやり考えるときには、「3年後には今のチームを離れるつもりで動いた方がいい」と思ってる(「3年は我慢しろ」という意味ではない。我慢するくらいならすぐ異動していいと思う)。その後は好きな領域にFA宣言でもしたらいいかなと。

「そうすると、残された時間はあと2年だよね?」「このチームで学べることがいっぱいあるって言ってたけど、あと2年でFAしようと思うと、あと2,3ヶ月中にはこれくらいは身につけておきたいよね?」みたいなの。それに加えて、そういうつもりで仕事をしてると、時間を無駄にできないし、ドキュメントを残したりナレッジ共有をしたり、後継者を育てることの優先度があがるから、強いチームになるなって。

あと、マネージャもそのつもりでいると、より魅力的なチーム作りをするようになるかなぁと思ったりするけど、それはまた別の話。

今日もいちにちがんばろー(๑•̀ㅂ•́)و✧

怖くないR2DBC その3 spring-data-r2dbc

怖くないR2DBCシリーズ。

bufferings.hatenablog.com

前回までで、この3つを見てきた。

r2dbc-spiがインターフェースで、r2dbc-postgresqlがそのPostgreSQL用実装。r2dbc-clientはr2dbc-spiを使いやすくするためのクライアントライブラリー。という感じだった。

f:id:bufferings:20181118081104p:plain

今日はもうひとつのクライアントライブラリーであるspring-data-r2dbcについて見てみる。

## spring-data-r2dbc?

r2dbc-clientはSpringには依存せずに便利機能を提供してたけど、spring-data-r2dbcはSpringを利用して便利機能を提供するクライアントライブラリーなんだろうなー。

github.com

## ORMじゃないよ

spring-data-r2dbcはORMになろうとはしてない。リアクティブなRDBアクセス用の組み立てキットみたいな感じ。好きなように必要に応じて組み立てることができるようなの。

じゃ、見ていこう。

## DatabaseClient

その中心になってるのはDatabaseClientクラス。こんな風にしてインスタンスを取得する。この前作ったgetPostgresqlConnectionFactory()をそのまま利用しといた:

  DatabaseClient getDatabaseClient() {
    return DatabaseClient.create(getPostgresqlConnectionFactory());
  }

  private PostgresqlConnectionFactory getPostgresqlConnectionFactory() {
    var configuration = PostgresqlConnectionConfiguration.builder()
        .host("localhost")
        .database("postgres")
        .username("postgres")
        .password("mysecretpassword")
        .build();
    return new PostgresqlConnectionFactory(configuration);
  }

で、このDatabaseClientクラスを使って色々やる。

## Generic SQL

まずはGeneric SQLから。汎用的な用途ってことかな。こんな風に書ける。

  private Mono<Integer> genericInsert() {
    var db = getDatabaseClient();
    Mono<Integer> count = db.execute()
        .sql("INSERT INTO weather (city, temp_lo, temp_hi) VALUES($1, $2, $3)")
        .bind("$1", "Osaka")
        .bind("$2", 5)
        .bind("$3", 18)
        .fetch()
        .rowsUpdated();
    return count;
  }

fetch()で実行。rowsUpdated()で結果を取得ってことかな。fetch()の戻り値の型はFetchSpec<Map<String, Object>>になってて、このFetchSpecインターフェースはこんな感じ。面白いなー。

public interface FetchSpec<T> {
    Mono<T> one();
    Mono<T> first();
    Flux<T> all();
    Mono<Integer> rowsUpdated();
}

Selectはこんな風に書けて:

  private Flux<Map<String, Object>> genericSelect1() {
    var db = getDatabaseClient();
    Flux<Map<String, Object>> result = db.execute()
        .sql("SELECT city FROM weather")
        .fetch()
        .all();
    return result;
  }

結果をごにょごにょしたい場合はfetch()の代わりにexchange()を使って、こうするってことなのかな?

  private Flux<String> genericSelect2() {
    var db = getDatabaseClient();
    Flux<String> result = db.execute()
        .sql("SELECT city FROM weather")
        .exchange()
        .flatMapMany(it -> it.extract((r, md) -> r.get(0, String.class)).all());
    return result;
  }

ふむふむ。もうちょっとちゃんとJavadocとかソースを読んだほうが良さそう。

## Insert

今度はGenericじゃないやつ。

  private Flux<String> insert() {
    var db = getDatabaseClient();
    Flux<String> cities = db.insert()
        .into("weather")
        .value("city", "Osaka2")
        .value("temp_lo", 10)
        .value("temp_hi", 13)
        .exchange()
        .flatMapMany(it -> it.extract((r, md) ->
            "city:" + r.get("city", String.class) +
                " temp_lo:" + r.get("temp_lo", Integer.class)).all());
    return cities;
  }

ふーむ。こっちの方がGenericのよりは読みやすいかね。

クラスを使うこともできる。ふーん。

  private Flux<String> insertWithDto() {
    Weather weather = Weather.create("Kyoto", 1, 30);
    var db = getDatabaseClient();
    Flux<String> cities = db.insert()
        .into(Weather.class)
        .using(weather)
        .exchange()
        .flatMapMany(it -> it.extract((r, md) ->
            "city:" + r.get("city", String.class)).all());
    return cities;
  }

  @Table("weather")
  private static class Weather {

    static Weather create(String city, Integer tempLo, Integer tempHi) {
      Weather weather = new Weather();
      weather.city = city;
      weather.tempLo = tempLo;
      weather.tempHi = tempHi;
      return weather;
    }

    public String city;
    @Column("temp_lo")
    public Integer tempLo;
    @Column("temp_hi")
    public Integer tempHi;
  }

## Select

Selectもこんな感じか。

  private Flux<Map<String, Object>> select1() {
    var db = getDatabaseClient();
    Flux<Map<String, Object>> rows = db.select()
        .from("weather")
        .orderBy(Sort.by(desc("temp_lo")))
        .fetch()
        .all();
    return rows;
  }

DTOを使うとこんな感じ。

  private Flux<Weather> select2() {
    var db = getDatabaseClient();
    Flux<Weather> rows = db.select()
        .from(Weather.class)
        .orderBy(Sort.by(desc("temp_lo")))
        .fetch()
        .all();
    return rows;
  }

今日はこのくらいにしとこ。・・・あれ?ここまでSpring関係なく単に便利だなーってだけじゃない???

課題は、MonoとFluxとそれ以前にStreamの使い方を僕がちゃんと分かってないとこだな。勉強しなきゃなー。

リポジトリーは、また次回。リポジトリーはSpring使ってそうよね。

## 今日のソースコード

DIとか使ってごにょっとしといた。

https://github.com/bufferings/hello-r2dbc/tree/07e521fa755f796b0ef78cc0122906dafef60509

娘とGoでプログラミング。FizzBuzzって色々詰まってるんやね。

FizzBuzzのルールを書いてあげたら、こういうのを書いてきたので「お父さんScratch飽きてきたからGoでやろ?」って軽い気持ちで言ってみたんだけど、いざ教えようと思うと結構色々伝えないといけなくて大変だったやw。FizzBuzzって色々詰まってるんやね。

f:id:bufferings:20181202185820j:plain

## RunとKill

A Tour of GoのPrintlnのところを開いて

https://go-tour-jp.appspot.com/welcome/1

へー。こんな風に書くのかーって思いながら(何回かやったことあるけど覚えてなかった)。

package main

import "fmt"

func main() {
    fmt.Println("Hello, 世界")
}

僕「Runを押してみな」

娘「ら・・らん?これ?どういう意味?」

僕「プログラムを動かすっていう意味」

娘「ボタン変わった!」

僕「それはKillだね。殺すって意味」

娘「へっ!?」

僕「あー。プログラムを途中で止めるっていうときに使われるんよ」

娘「へー。あ、動いた(∩´∀`)∩ワーイ」

## Println

僕「この『世界』の部分書き換えて」

娘「OK。み・・・つ・・・ゆ・・・き」

僕「じゃRunしてみ」

娘「『Hello, みつゆき』(∩´∀`)∩ワーイ」(みつゆきは僕の名前です)

僕「プリントラインすると文字がここにプリントされるってこと」

娘「ふーん。プリンターで出てくるみたいなものね。ラインは・・・線?」

僕「ここのラインは行って意味だね」

娘「へー」

## 値と変数

僕「次はforかなぁ」

って、A Tour of Goのfor文のところを開いて

https://go-tour-jp.appspot.com/flowcontrol/1

package main

import "fmt"

func main() {
    sum := 0
    for i := 0; i < 10; i++ {
        sum += i
    }
    fmt.Println(sum)
}

娘「Runしたら45になった!どういうこと?」

僕「このsumの値が45ってことだね」

娘「あ・・・た・・・い?あたいするってこと?」

僕「んー。sumって変数の中に45って値が入ってるんだけど・・・」

(しばらく色々説明)

娘「ふーん。私の『年齢』って変数の箱に『9歳』って値が入ってるの・・・かな?」

僕「そうね。まぁだいたいで大丈夫」

## インクリメント

僕「ってことで、forの部分の説明ね。このforは『繰り返す』ってこと」

娘「ふーん」

僕「iが0から始まって10未満の間は、この中の処理を繰り返すってこと」

娘「なか???」

僕「あぁ、この括弧と括弧の間の部分。で、一回終わったらiを1増やして、10未満かどうかをチェックして、10未満だったら、もう一回処理を実行するの」

娘「へー。このプラス2つの部分で増えるの?」

僕「そうそう。プラス2つで『インクリメント』って言って・・・んー、言葉はやっぱいいや。まぁ1増やすってこと」

娘「へー。算数のテストで1足すときに書いてみようかな?」

僕「あ、いや、それやめて。お父さんが先生に怒られる。プログラムのときだけね」

## for文

娘「分かんなくなってきた!どうなってるか想像できない!」

僕「じゃ、このiがどうなってるか見るために、forの中にPrintlnを書いてみて?」

娘「はーい」

(しばらく待つ)

娘「こう・・・かな?」

func main() {
    sum := 0
    for i := 0; i < 10; i++ {
        sum += i
        fmt.Println(i)
    }
    fmt.Println(sum)
}

僕「実行どうぞ」

娘「へー。0から9まで出たー」

娘「でも、この45って何?」

僕「じゃ、sumも出してみたら?」

## sum

娘「できたー」

func main() {
    sum := 0
    for i := 0; i < 10; i++ {
        sum += i
        fmt.Println(i)
        fmt.Println(sum)
    }
    fmt.Println(sum)
}

娘「Runね」

0
0
1
1
2
3
3
6
4
10
5
15
6
21
7
28
8
36
9
45
45

僕「iが0で、sumは0+0だから?」

娘「1かな?」

僕「あほなのかな?」

娘「あほちゃうし。0ね」

僕「そうだね。つぎはiが1になるから・・・(説明)」

娘「45になった!」

僕「そうだね。リモート調子悪いみたいだしローカルでやろうか」

## ローカル環境の準備

娘にあげた古いMacBookAirに

  • Goをインストールして
  • VS Codeをインストールして
  • Hello.goファイルにmain関数とfmt.Println("Hello")書いてあげて
  • ターミナルをVS Codeの下半分に表示しておいて
  • go build Hello.go./Hello実行とやってあげといた。

## 説明

僕「この下半分の方をクリックして矢印の上を2回押したらgo buildって出るからそれでビルドして」

娘「・・・びるど???上2回押してEnterね。できた。」

僕「ほら、このHello.goの隣にHelloってファイルができたでしょ?これが君が初めてビルドして作ったプログラムだね」

娘「(∩´∀`)∩ワーイ」

僕「そのあと上をもう2回押したら./Helloって出るからそれで実行ね」

娘「・・・じっこう???できた。ハロー!」

## forで1から100まで

僕「さっきのforループのプログラムをコピーしてきてちょっと修正しといたわー」

package main

import "fmt"

func main() {
    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }
}

僕「これ実行すると、0から9まで表示されるよね。ほら」

娘「うん」

僕「これで1から100まで表示するようにしてみて?」

娘「100までってことは、101までにすれば良いのね。できたー!」

僕「最初の数字は?」

娘「あ、0だ。じゃ、ここをこうしてっと。できたー!」

僕「いいね」

package main

import "fmt"

func main() {
    for i := 1; i < 101; i++ {
        fmt.Println(i)
    }
}

## if

僕「じゃ、15のときにFizzBuzzって表示するようにしてみようか。それには『もし』っていう意味の『if』を使うんだよ」

娘「『もし』ぶつかったら『ダメージを受ける』ってやりたいのよ!」

僕「それは良いね。お父さんはそれは書けないけど、君は今日のが書けたらそのうち書けるようになると思うよ」

娘「(∩´∀`)∩ワーイ」

僕「じゃif i == 15 {}って書いて」

娘「アイ・・・アイ・・・アイ・・・エフ・・・エフ・・・」

(しばらく待つ)

娘「書けたよー!」

僕「この括弧の中にFizzBuzzってプリントするように書いて?」

娘「はーい。さっきのやつよね・・・f・・・m・・・t」

(しばらく待つ)

娘「できたー!15の次にFizzBuzzが出てきたー!」

func main() {
    for i := 1; i < 101; i++ {
        fmt.Println(i)
        if i == 15 {
            fmt.Println("FizzBuzz")
        }
    }
}

僕「いいね。次は15の倍数のとこ全部の下にFizzBuzzを出そうか?」

## %

僕「パーセントマークを使うよ。if i % 15 == 0に書き換えて?」

娘「はーい」

(しばらく待つ)

娘「できたー」

func main() {
    for i := 1; i < 101; i++ {
        fmt.Println(i)
        if i % 15 == 0 {
            fmt.Println("FizzBuzz")
        }
    }
}

僕「実行してみ?」

娘「わー!15とか30とかのとこ全部にFizzBuzzがついた!」

僕「そう。これは15で割ったあまりが0って意味ね」

娘「ふーん。割ったあまりが・・・ゼロ・・・?分かるような分からないような・・・まいっか」

娘「それよりも、15の次にFizzBuzzを出すんじゃなくて、15のところをFizzBuzzに入れ替えなきゃ」

僕「いいところに気づいたね。じゃ、elseだね」

## else

僕「} else {って書いて、数字を出すやつをそっちに移動してみて。カット&ペーストを教えてあげよう。」

娘「カット・・・?難しいけど便利ね。実行・・・と。あ、15のところがFizzBuzzになったー!(∩´∀`)∩ワーイ」

func main() {
    for i := 1; i < 101; i++ {
        if i % 15 == 0 {
            fmt.Println("FizzBuzz")
        } else {
            fmt.Println(i)
        }
    }
}

## else if

僕「次はどうしよう?」

娘「3の倍数をFizzにしたい」

僕「いいね。じゃelse ifって書いて?」

娘「はーい」

僕「その後は『3の倍数のときだけ』ってつけて?分かるかな?」

娘「んー。パーセントかな・・・。こう・・・かな?」

package main

import "fmt"

func main() {
    for i := 1; i < 101; i++ {
        if i % 15 == 0 {
            fmt.Println("FizzBuzz")
        } else if i % 3 == 0 {
     
        } else {
            fmt.Println(i)
        }
    }
}

僕「実行してみなー」

娘「うん。あれー?3のところ消えた!」

僕「どうしてかな?」

娘「あ!書いてなかった!こうやって・・・できたー!」

## 5の倍数

娘「あとは5の倍数ね・・・else ifして・・・% 5して・・・」

func main() {
    for i := 1; i < 101; i++ {
        if i % 15 == 0 {
            fmt.Println("FizzBuzz")
        } else if i % 3 == 0 {
            fmt.Println("Fizz")
        } else if i % 5 == 0 {

        } else {
            fmt.Println(i)
        }
    }
}

僕「実行してみようか?」

娘「え!まだPrintln書いてないから!」

僕「どうなると思う?」

娘「え?5の倍数が消えるはず!」

僕「やってみよう?」

娘「うん・・・あれ?」

1
2
Fizz
4
Fizz
7
8
Fizz
11
Fizz

娘「5の倍数がFizzに・・・なった・・・?」

僕「えっ?あー。ふふふ。よーく見てみて?」

娘「あ・・・れ・・・?あー!5が消えてるんだ!分かりにくいわ!」

## Buzz

娘「じゃ、Buzzを書いて・・・こうだな!できたー!!!」

package main

import "fmt"

func main() {
    for i := 1; i < 101; i++ {
        if i % 15 == 0 {
            fmt.Println("FizzBuzz")
        } else if i % 3 == 0 {
            fmt.Println("Fizz")
        } else if i % 5 == 0{
            fmt.Println("Buzz")
        } else {
            fmt.Println(i)    
        }
    }
}

娘「ありがとうございました!」

僕「いえいえ(ヽ´ω`)グッタリ」

娘「ママー!プログラム書いたよ!ビルドして実行したらね、ほらFizzBuzzが動くよ!」

妻さん「び、びるど・・・?実行?ふぃず・・・ばず・・・?なんか全然分からないけど、す、すごいねー!」

娘「(∩´∀`)∩ワーイ」

スクラムマスターの評価って? #RSGT2019 の好きなとこ。僕が手伝うとスケジュールが遅れる話。 #agileradio

アジャイルラジオで喋ってきたー!うれしいー!ポッドキャストもあるよ。よかったら聞いてください!

agileradio.github.io

トピックはこんな感じ。

聞いてくれた人は #agileradio タグつけて感想つぶやいてくれたら喜びます。後半もお楽しみにー。マッタネー٩( 'ω' )و

怖くないR2DBC その2 r2dbc-client

これのつづき。

bufferings.hatenablog.com

## 1.0.0.M6

数日前に1.0.0.M6が出たので、この前はM5のソースを読んでたけど今日はM6のにする。

## r2dbc-clientを見てみる

https://github.com/r2dbc/r2dbc-client/tree/master/src/main/java/io/r2dbc/client

r2dbc-clientはr2dbc-spiを人が使いやすいようにしてくれるためのシンプルなライブラリー。とりあえずは、この2つのクラスを見ておけば良さそうかな?

  • Handle
  • R2dbc

## Handle

Connectionのラッパー。結果をPublisherじゃなくてMonoとかFluxで返してくれたり、ちょこちょこ便利にしてくれてるっぽい。

## R2dbc

このクラスがr2dbc-clientを使うときの中心になる。コネクションファクトリーを渡してインスタンスを生成。

R2dbc r2dbc = new R2dbc(new PostgresqlConnectionFactory(configuration));

使うかなーと思うのは次の4つ。

  • withHandle:ハンドルを使って処理をする。結果を返すやつ用。
  • useHandle:ハンドルを使って処理をする。結果を返さないやつ用。
  • inTransactionトランザクション内で処理をする。結果を返すやつ用。
  • useTransactionトランザクション内で処理をする。結果を返さないやつ用。

どれも、流れ的には

  1. コネクションをファクトリーから生成してハンドルを開く
  2. 処理をする
  3. ハンドルを閉じる

な感じ。エラーハンドリングもしてる。

### withHandle

というか、どれも最終的にはwithHandleを使ってて、そこに上記の処理が書いてある感じ。

    public <T> Flux<T> withHandle(Function<Handle, ? extends Publisher<? extends T>> f) {
        Objects.requireNonNull(f, "f must not be null");

        return open()
            .flatMapMany(handle -> Flux.from(
                f.apply(handle))
                .concatWith(ReactiveUtils.typeSafe(handle::close))
                .onErrorResume(ReactiveUtils.appendError(handle::close)));
    }

へー。エラー処理はonErrorResumeってのを使うんだねー。

### useHandle

へー。then()Mono<Void>を返すようにするのかー。

    public Mono<Void> useHandle(Function<Handle, ? extends Publisher<?>> f) {
        Objects.requireNonNull(f, "f must not be null");

        return withHandle(f)
            .then();
    }

### inTransaction

トランザクション内で処理をする」っていうハンドルのメソッドを呼び出してるだけ。

    public <T> Flux<T> inTransaction(Function<Handle, ? extends Publisher<? extends T>> f) {
        Objects.requireNonNull(f, "f must not be null");

        return withHandle(handle -> handle.inTransaction(f));
    }

### useTransaction

それのuseHandle版。

    public Mono<Void> useTransaction(Function<Handle, ? extends Publisher<?>> f) {
        Objects.requireNonNull(f, "f must not be null");

        return useHandle(handle -> handle.useTransaction(f));
    }

## ということで

Handleを引数にして処理をする関数を書けば良いみたいね。前回書いたこのコードは

  private Flux<String> sample() {
    var connectionFactory = getPostgresqlConnectionFactory();
    return connectionFactory.create()
        .flatMapMany(connection ->
            connection.createStatement("SELECT city FROM weather")
                .execute()
                .flatMap(result ->
                    result.map((row, metadata) ->
                        row.get("city", String.class)
                    )
                )
        );
  }

R2dbcクラスを使うと、こんな感じで書ける。この例だとあんまり変わらないけど、トランザクションとか使ってるともっと違うんかな。

  private Flux<String> sampleR2dbc() {
    var connectionFactory = getPostgresqlConnectionFactory();
    var r2dbc = new R2dbc(connectionFactory);
    return r2dbc.withHandle(h ->
        h.createQuery("SELECT city FROM weather").mapRow(
            (row, metadata) ->
                row.get("city", String.class)
        ));
  }

面白いなー。

およべさんとみんなとチーム開発の現場について雑談したー

##「およべさんと話をしたいなー」

「いいね」「どうせだから一般公開してやろうかー」ってところから始まったイベントで雑談してきた。

connpass.com

## みんなが主催者

みんなで色々とネタ出しをして、議題を決めて、わーって意見交換。

チームの育て方や、モブワークなどについて、雑談。

## すっきりしたー

終わった後は情報過多で頭の整理をしきれてない感じだった。↓同意。

でも、色々話が聞けて、話ができて、すっきりしたー。

## 確かにそうだな

あの場で話し合ったこと自体も良かったし、それに加えて最後におよべさんが言ってたのが確かにそうだなって思った。

「こういうことを話すことができる人たちと出会えたのが良かった」

## ありがとう

みんなと雑談できて色んな悩みや意見が聞けて楽しかった!

僕の話を聞いて「試してみようと思った」って言ってくれた人もいたし。嬉しい。

あと、あらた(@aratafuji)声でかいw(来てくれてありがとう)

また会おうねー。