技術日記というか週報5/13-17
今週(先週)はリファクタリングいっぱいしてた
クリーンアーキテクチャやる上でDBトランザクションどうするかという問題についていくつか考えてた
(とりあえず有名なやつ貼っとく)
- インターフェース層でトランザクションを管理できるようにして、コントローラーからユースケースを呼ぶタイミングでトランザクションを貼る。
- リポジトリレベルでトランザクションを貼る
- なんとかしてユースケースからトランザクションを使えるようにする
- ユースケースとリポジトリの間にゲートウェイ的なものを用意してそこでトランザクションを貼る
それぞれについてより細かく書くと
1は割と簡単にできる方法。(もともと自分間違えてたけど)DBクエリを書いたりするのはインターフェース層で、コントローラーと同じ層になるので、コントローラーの中でもいわゆるORマッパー等の機能を使うことができる。なのでコントローラーからユースケースを呼ぶ際にトランザクションを貼ることができる。ただ若干気持ち悪い気もする。しかし、トランザクションという概念がRDBのものなのでそれとユースケースをつなぐという意味でもインターフェース層にあってもまあいいかなーとも思った。ちなみにもともと自分はFramework&Driversの層にクエリも書くもんだと思っていて、なかなかこのやり方に気が付かなかった。
2はユースケースから呼ばれるリポジトリ(インターフェース層)にトランザクションを貼るやり方。個人的にユースケースレベルでトランザクションを貼りたかったのでこのやり方も微妙だと思ってた。ただDDDの集約の考え方を取り入れるとうまくいきそうとも思った。集約とは整合性を保ちながらデータを更新するオブジェクトのまとまりという感じらしく、リポジトリを集約単位で切ればリポジトリレベルでのトランザクションはありだと思った。ただ集約を見極めるのが難しそうだとも思った。もともとDDDの中で出てきた考えだからしっかりとDDDを実践した中で取り入れる考えなんじゃなかろうかと思って今回は見送った。
3はまあやりようはあるのかなーとも思ったけどめんどくさそうだからやめた
4はリポジトリと同じインターフェース層(トランザクションの制御ができる層)にゲートウェイを設けるやり方。ユースケースから直接リポジトリは呼ばずに、ゲートウェイの中で同一トランザクションで処理したいものを書いていく。これも簡単にできるしリポジトリにトランザクションの責務を持たせずに済むけど、トランザクションのパターンが増えるごとにゲートウェイのパターンも増えていく。ちなみにググってて思ったけどこれはCQRSのコマンドってものに近いんだろうか?
結論現状は一旦1のパターンですすめている(簡単だし十分役割を果たせてると思うから)
GraphQL Java使ってたけどGraphQL Java Toolを使ってなかった。 DataFetcherのマッピング(resolverって名前になってたけど)を自動でやってくれたりして記述量が減って楽になった。
スクレイピングのjavascriptを使ってたけどtypescriptに書き直した。 今どきのjavascript新規プロジェクトは最初からtypescriptでやるもんらしい? とりあえずやはり型がある方がしっくり来る。 もともとtypescriptを入れることは考えていたけど、jsでどこまで行けるかなーって感じでjsのみで書きすすめていた。 RDBと接続するところで。あ、これ型ないと無理だわ...って気づいた(気づくの遅い) typeormというORマッパーもあっていい感じだ
以上
GraalVMが気になる
Node.jsで作ったスクレイピングアプリをDockerコンテナで起動してみた
(qiitaにも書いたけどこっちにも載せとこう...)
やりたかったこと
- サーバーレス環境でスクレイピングアプリを動かしたかった
- Knative試したかったのでコンテナ化する必要があった
nodejsでスクレイピングするアプリをサクッと作る
まずは好きなところにディレクトリ切って下記コマンドを叩く
npm init
出てくる質問は全てEnter連打した
次にスクレイピングに必要な selenium-webdriver
をインストール
https://www.npmjs.com/package/selenium-webdriver
npm install selenium-webdriver
scrapingするプログラムを書いていきます。今回はgoogle chromeが既にインストールされている前提で話を進めます。
vim example.js
const { Builder, By, Capabilities } = require('selenium-webdriver'); const capabilities = Capabilities.chrome(); capabilities.set('chromeOptions', { args: [ '--headless', '--no-sandbox', '--disable-gpu', '--window-size=1980,1200', // other chrome options ], }); (async function example() { console.log('start google scraping'); const driver = await new Builder().forBrowser('chrome').withCapabilities(capabilities).build(); try { await driver.get('https://www.google.com/?hl='); const text = await driver.findElement(By.xpath('/html')).getText(); console.log(text); } finally { await driver.quit(); console.log('finish scraping'); } }()); const sleep = time => new Promise((resolve) => { setTimeout(() => { resolve(); }, time); });
puppeteerもサクッと立ち上げるのには良さそうだったけどブラウザがchromium縛りになるのが嫌だったので今回はWebdriver使いました
npm scriptsを定義する
package.json
のscriptsに以下のようにstartタスクを追加します
"scripts": { "start": "node ./example.js", "test": "echo \"Error: no test specified\" && exit 1" },
試しにスクレイピングを走らせてみます
npm start
fuga@hoge ~/D/g/scraping-sample> npm start > scraping-sample@1.0.0 start /Users/a12711/Documents/git-localrepository/scraping-sample > node ./example.js start google scraping Gmail 画像 ログイン 日本 プライバシー規約設定 広告ビジネスGoogleについて finish scraping
うまくスクレイピングが動いてそうです
Dockerfileを作る
今回はサクッと作りたかったので、seleniumのstandalone-chromeのイメージを元にして、nodejsをインストールするDockerfileを作成しました
FROM selenium/standalone-chrome:3.141.59 WORKDIR /usr/src/app COPY package*.json ./ USER root RUN curl -SL https://deb.nodesource.com/setup_8.x | bash RUN apt-get install -y nodejs RUN npm install COPY . . CMD npm start
Docker Build
dockerが入っていない方はdocker desktop等でdocker入れてください
Dockerイメージを作成する
docker build -t sample/robot .
コンテナ起動
docker run sample/robot
fuga@hoge ~/D/g/scraping-sample> docker run sample/robot > scraping-sample@1.0.0 start /usr/src/app > node ./example.js start google scraping Gmail 画像 ログイン 日本 プライバシー規約設定 広告ビジネスGoogleについて finish scraping
先程と同様のログが出ています これでスクレイピングのアプリをDockerコンテナとして動かすことができました
技術日記5/7-5/10
protocol buffersのversion3でrequired, optionalがなくなっていることを知った
そのため定義してあるフィールドには必ず値が入るようにしなければならない
とはいえoptionalを表現したいときはある
自分は別フィールドで has_~
といった感じで表現することにした
.protoファイル例
message Sample { bool has_hoge = 1; string hoge = 2; }
そもそもなんでrequiredがなくなったのかというと
requiredを使うと過去互換を取りにくくなるからとのことだった
最初に設計したときは必須だと思っていても、将来的にそのフィールドが必須であり続ける保証はない
でも、一度requiredにしてしまうと後から削除するのは難しいからとのこと
ふむ、わかるようなわからないような
今更だけど排他ロックと共有ロックのちがいを理解した
簡単に言うと読み取り(SELECT)を許すかの違い
排他ロックではUPDATE、DELETE、SELECTができない
共有ロックならSELECTは通る
SELECT~FOR UPDATEは排他ロック
この文は取得した値を利用して更新をかける際に使われることを想定している
クリーンアーキテクチャでのDBトランザクションどうするか問題にハマっている
blockchainゼミに入ったからblockchainの記事を漁っていた
blockchainの将来性が大変気になる
本当にこの技術は将来使われるのかとか
でも既にblockchainを使用したサービスはいくつも誕生しているみたい
例えばYouTubeのような動画配信サービスのDtube
ただこれらのサービスが既存のサービスからシェアを奪う未来はくるのだろうか
blockchainを使ってうまくクリエイターに還元する流れができると良さそうだけど、その還元金額が既存のサービスを上回るような設計が必要そう
もっとアーキテクチャのこと勉強したいから下記の書籍を購入した
エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)
- 作者: エリック・エヴァンス,今関剛,和智右桂,牧野祐子
- 出版社/メーカー: 翔泳社
- 発売日: 2011/04/09
- メディア: 大型本
- 購入: 19人 クリック: 1,360回
- この商品を含むブログ (131件) を見る
分散システムデザインパターン ―コンテナを使ったスケーラブルなサービスの設計
- 作者: Brendan Burns,松浦隼人
- 出版社/メーカー: オライリージャパン
- 発売日: 2019/04/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る