DL販売のサイトを実装する記録【その1】

モチベーション

  • 設計周り勉強/練習したい
    • 議論のたたき台になるリポジトリがあると良さそう
    • SpringBootも一緒に素振りしたい
  • 題材はdlsiteを参考に実装してみる
    • いい感じに複雑そう
      • 売上、販売手数料、消費税
      • アクセスするユーザーが販売者になったり購入者になったりする
    • 画像とかのコンテンツを扱うので見た目がいい感じになって楽しそう
    • 検索とかあれこれするならその他ミドルウェア周りも一緒に出てきそう?

考えたこと

  • とりあえずリポジトリを作るところから
  • 料金周り考えてみる
    • 販売価格 = 卸価格 + 販売手数料になるらしい
      • 価格設定について
      • 卸価格はサークル(販売者)が決めた金額で、卸価格の税込み分が振り込まれる
      • 販売手数料は値段によって変動するっぽいけど割合が謎。どこにも書いてないのでとりあえず一律3割にした。
    • 販売価格のコンストラクタに卸価格を食わせると販売手数料が販売価格クラス側で計算されるみたいな感じにするか悩む
      • 販売価格から卸価格と販売手数料が決定される…?そうなるとこの形だと扱いづらい
      • ユーザーが触るであろう画面を考えてみる
        • 販売価格から決めたいパターンは販売価格を切りの良い数字にしたいとかそういうモチベーションがありそう
        • 卸価格から決めたいパターンは損益分岐点考えていい感じの値で売りたいとかそんな感じ?
        • 同人文脈なら卸価格からのパターンのほうが嬉しそう。並んでる商品見てみた感じ切りの良い値段で売ってるわけではないし。
        • 販売価格に卸価格を渡して販売手数料を計算してもらうことにした
    • 販売価格 = 卸価格 + 販売手数料 -> 基本価格 = 卸価格 + 販売手数料・・・?
  • ユーザー登録作ってみる
    • モデリング周りちょっと満足感出てきて画面が欲しくなったのでまずユーザー登録作ってみよに浮気
    • ところでdlsiteって他のサービスもやっていて、同じアカウントでサービス利用できるな…?
      • webパッケージにアカウントの概念入れると新しいサービス生まれると辛いのでは?
        • 最初からidentity周りはモジュール分けておくと良いって誰かが言っていた気がする
        • 新しいサービスでアカウント使いまわしたい。うんうんよくある。identityモジュールつくっちゃお
    • 生パスワードとハッシュ化されたパスワードややこしいので別の型にしたい
  • 画面周りを作るぞ
    • cssが面倒なのでscssを使いたくなる
    • web-uiディレクトリを作って、yarn initしてwebpackをいれる
    • gradleでwebのprocessResourcesするときにyarn buildして成果物をweb/build/...たいなところに突っ込むようにした

もやっと

  • メール送るみたいなちょっと重い処理は@Async使って別スレッドで非同期処理?
    • Async手軽だけどデプロイ時にジョブが飛んだりしそう
    • ジョブキューの仕組みなにか入れたほうがいいのかな
  • DomaのExternal Domain ClassでConverterの置き場に困った
    • Encryptedみたいなbyte[]を渡せるコンストラクタをpublicにしたくない
    • EncryptedはcoreモジュールにいるのでDomaを使ったクラス置けないし置きたくない
    • publicにして公開するよりはpackage-privateのままwebモジュールに同名パッケージ作ってConverter置くことにした
    • EncryptedConverter.java

今日はこんなかんじの画面ができたところで終わり。 f:id:orekyuu:20190728185406p:plain

はじめからちゃんと作るは難しい

今日は何かシステムを作るときにしっかり学習して設計してからコードを書いてリリースだ!みたいなのを聞いて、それは難しいんじゃないかなーという気持ちになったりした。
学習と設計(どう作るか作戦を考える)は全体を俯瞰して整理するには良いんだけど、細かいところは見えてこなくって手を動かしてみると「あれ?これって難しいんじゃないかな?」ってなったりする。
一方で勢いでコードを書き始めると一貫性がなくて「結局これなにが作りたかったんだっけ?」みたいな塊ができてしまったりする。

たぶん全体と部分を行ったり来たりする必要があるんだなー。JIGとか見てるとそんな感じがする。コードを書きながらJIGで全体をみたりドキュメントを書いてみたりしていったりきたりしながら実験と学習して整理するのに活かす。みたいなのがよさそう〜

あ、あとレガシーだから作り直したいってのも聞いたけど、作り直すことでキレイになるかというとそんなことはなくて、今の複雑さに対しての問題を解決できる答えがないから困っているんじゃないかなー。それなら必要なのは作り直しじゃなくて改善かもしれない
新しいものは今ある問題を解決して、新たな問題をまだ受けてないからキレイに見えるだけですぐに汚れちゃうから、戦っている問題がわからないまま挑むのはあまり意味がないような気がするんだよなー。

はじめからちゃんと作るというのはできるといいけど難しい〜・・・!

リフレクションやメタプロ的な機能は避けような

リフレクションとかメタプロ的なものを触るのは楽しい。わかる、わかるよ。
もちろん知識としては知っておくべきだし、知っておくとフレームワークやライブラリの裏側が想像できてトラブルシューティングのときに便利だ。

メタプロ的機能の辛いところ

まず静的型な言語やIDEの推論がいいかんじに働く言語の場合、あるコードの呼び出し箇所が分からなくなってしまう。静的型の言語の場合はコンパイルエラーが出せなくなってしまうので、コンパイル時に防げた不具合をランタイム時まで持ち込んでしまう。コンパイラIDEに仕事をさせたいと思ったときにメタプロ的機能は邪魔になってしまう。

次に互換性の問題。privateメソッドを呼び出したりモンキーパッチなどで振る舞いを変えたりした場合は、ライブラリの意図から外れた使い方をしているので互換性が壊れてしまっても文句を言えない。大体のライブラリは破壊的変更をChangeLogあたりに書いてくれるが、モンキーパッチやprivateメソッドを呼び出して書き換えていた場合はコードの変更にもっと敏感にならなければいけない。簡単に壊れてしまう。
バグを見つけたならPRを投げたりしてコミュニティに貢献しよう。バグ修正を取り込んでもらえたら他のコントリビューターがいい感じに保守してくれたりいい感じにしてくれるしみんな幸せになれる。

判断基準

上に挙げた辛いところを受け入れた上でコードが宣言的になるほどのメリットがあるかを考える。大体の場合は不要なはず。
ロギングみたいな横断的関心事を扱うときにAOP的機能を使うことがあるのかなくらい。それでもFilterでなんとかなったりするけど。

結論としては、メタプロ的機能は楽しいけど保守するコードで使うなら相応の覚悟を持ってやってねということ。

ORMに感じるもやっと感

SpringBootでWebアプリを作るとき、いつもDBアクセスに何を使うか悩む。Doma?Mybatis?JPA?
個人的にはどれもしっくり来ていなくて、なんだかんだでDomaを使っている。何にもやっとを感じているのか考えてみた。

ドメインモデルへのマッピング

ほとんどのWebアプリのドメインモデルは1:*のようなマッピングが存在していて、SQLの結果を親子関係を持った構造にマッピングしたい。
SQLはjoinなどで持ってくるとflatな構造なので頑張ってドメインモデルに詰め替える必要がある。これがとても面倒くさい。
Mybatisを使う場合、resultMapで関連のマッピングを定義する方法が用意されている。とはいえSQLXMLに書きたくないので、SQLの置き場としてはやっぱりDomaが好きだなーというお気持ち。
JPAはORMの都合をあまりドメイン層に持ち込みたくないのでなんだかなーということであまり使ってない。あと複雑なクエリは多かれ少なかれなんだかんだ書くことになるので、SQLを自分で書くタイプのほうがいいよなーって感じがする。

仮説:リレーションのマッピングができれば嬉しい?

基本的にDomaで事足りていて、あとはリレーションのマッピングをいい感じにしたいだけなのでは?ということはマッピングをするところだけライブラリにしてしまえば良さそう!
ということでKtMapperというライブラリを書いてみた。

github.com

レコードのオブジェクトをドメインモデルに変換するMapperをKotlinでDSLっぽく書けたら便利かもねという思想。

サンプルで書くとこんな感じ。

10連休終わるまでにもうちょっと素振りして検証してみよ〜