Yoshi.dev

技術系の趣味、またはやった仕事やそこから学んだことを忘れないために

技術日記というか週報5/13-17

今週(先週)はリファクタリングいっぱいしてた

クリーンアーキテクチャやる上でDBトランザクションどうするかという問題についていくつか考えてた

https://blog.cleancoder.com/uncle-bob/images/2012-08-13-the-clean-architecture/CleanArchitecture.jpg

(とりあえず有名なやつ貼っとく)

  1. インターフェース層でトランザクションを管理できるようにして、コントローラーからユースケースを呼ぶタイミングでトランザクションを貼る。
  2. リポジトリレベルでトランザクションを貼る
  3. なんとかしてユースケースからトランザクションを使えるようにする
  4. ユースケースリポジトリの間にゲートウェイ的なものを用意してそこでトランザクションを貼る

それぞれについてより細かく書くと
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マッパーもあっていい感じだ

以上

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 ソフトウェア開発の実践)

エリック・エヴァンスのドメイン駆動設計 (IT Architects’Archive ソフトウェア開発の実践)

分散システムデザインパターン ―コンテナを使ったスケーラブルなサービスの設計

分散システムデザインパターン ―コンテナを使ったスケーラブルなサービスの設計

技術日記4/22-4/26

具体的な機能の実装を書き始めた
クリーンアーキテクチャのおかげでいい感じにフレームワークやDB周りと疎結合なコードが保てている気がする
ただやはり若干今までより書くコードの量は増えてしまう感じが生産性に影響しすぎないか不安はある

マイクロサービス開発ではよりテストドリブンな開発をしていく必要がありそう
サービスをつないでデバッグとかしていると時間がかかるので
テストドリブン的に各メソッドを実装していって、後でまとめて結合テストをする感じかな
クリーンアーキテクチャは結合がうまく外れてテストが書きやすくなるからマイクロサービスと相性がいいかもしれないと思った