パパエンジニアのポエム

奥さんと娘ちゃんへの愛が止まらない

ドメイン駆動設計をざっくり把握したい②

前回の続き。
DDDの戦術的設計をざっくりまとめる。

戦術的設計

レイヤ化アーキテクチャ

アーキテクチャパターンの一つで、下位層から順番にインフラストラクチャ層、ドメイン層、アプリケーション層、UI層に分離される。
原則として下位層にしか依存してはいけない。

依存関係逆転の原則(DIP)

「上位のモジュールは下位のモジュールに依存してはならない。どちらのモジュールも抽象に依存すべきである。
抽象は、実装の詳細に依存すべきではない。実装の詳細が、抽象に依存すべきである。」

  • DDDにおけるDIP

インフラストラクチャ層のコンポーネントは、ドメイン層が定義するインターフェースに依存するべきである。
具体的には、インターフェースIRepositoryドメイン層に実装し、具象暮らすRepositoryをインフラストラクチャ層に実装する。

エンティティ

一意なモノを表現する概念のことで、実装時は一意に特定するポイントとなる属性や振る舞いだけに注目することが重要。
ビジネスロジックを内包した一般的なクラス群の呼称、getterとsetterしかないドメインモデル貧血症というアンチパターンに注意する。

ドメインサービス

とあるドメインにおいてのサービスは、そのドメインに特化したタスクをこなす、ステートレスな操作のこと。

  • 注意
    • アプリケーションサービスと混同しないこと
    • エンティティの振る舞いをドメインサービス化してしまうとドメインモデル貧血症に陥る

集約

集約とは、エンティティのまとまりを表し、整合性を保ちながらデータを更新する単位のこと。
責務は、エンティティ群の生成/読み込み/変更/保存等のライフサイクル管理。

ファクトリ

ファクトリとは、集約を生成する責務をもつ概念のこと。
特定の集約にて、別のエンティティ(集約も含む)を生成することが多い。

リポジトリ

リポジトリとは、エンティティから構成される集約の保存と取得の責務をもつ概念のこと。
依存関係逆転の原則の説明にあるように、リポジトリのインターフェースは、集約と同じドメイン層のパッケージに配置。
リポジトリの具象クラスはインフラストラクチャ層に配置する。

アプリケーション

ここでいうアプリケーションとは、
ドメインモデル、ユーザーインターフェース、アプリケーションサービス、インフラストラクチャのコンポーネント全体を指す。

ユーザーインターフェース

JavaのSpringや.NETのASP.NETなどのWAFがこの層に属する。
ドメインモデルのユーザーインターフェースへのレンダリングは、DTOを用いることが多い(当然他の方法もある)。
アプリケーションサービスが集約インスタンスを取得し、それをDTOレンダリングする。
ドメインモデルに依存するかどうかはケースバイケース。

アプリケーションサービス

アプリケーションサービスとは、ドメインモデルの調整を行う責務をもつコンポーネント
ユーザーインターフェースドメインモデルの橋渡し役となるので、それぞれの層の依存関係を調整するのが大切。

インフラストラクチャ

インフラストラクチャは、アプリケーション全体の技術的機能を提供する責務をもつ。
DIPをい、アプリケーション内のあらゆるクライアントは抽象に依存するようにし疎結合化を図る。

ドメイン駆動設計をざっくり把握したい①

DDDをざっくりまとめておく必要があったので、メモがてらまとめる。

前提

「ソフトウェア開発とは、恐ろしく複雑である」という事実を真正面から向き合うための方法論。
プログラマが泣いた建築士がプログラマーのごとく働かねばならぬとしたらという秀逸な例え。

デジタルサービスを事業としている会社にとって「ソフトウェアは事業そのもの」といえる。
経営がソフトウェアを理解するのも大事だし、開発者が経営を理解するのも大事。
それがそのまま対象ドメインでの競争力になる。

DDDとは

DDD(ドメイン駆動設計)とは、Domain Driven Design の略でソフトウェア開発手法です。
GoFのようなデザインパターンではなく、ソフトウェア設計の概念です。

DDDの登場人物

  • 開発者
    • ソフトウェアを開発する人
  • ドメインエキスパート
    • ソフトウェアが対象とする業務領域(ドメイン)について業務知識を有する人

DDD導入メリット(事業)

開発費用がコストから事業投資へ置き換わる。

  • 開発者とドメインエキスパートが共通言語(ユビキタス言語)を用いて視点を揃えることにより、ワンチームであたかも顧客が開発するようにソフトウェアを開発できる
  • ドメインエキスパートですら理解が浅い業務ドメインの知識を、チームで共有し開発者視点も含め検討することにより、顧客の理想を超えるソフトウェアを開発できる
  • 対象ドメインの複雑さをドメインモデルというDDDの概念を用いて緩和する事ができる
  • DDDは、設計がコードであり、且つコードが設計であると表現されるように、開発時の翻訳コストが減り、実装時の課題を設計段階で気づくことが出来る
  • DDDは、アジャイル開発が前提なので、イテレーティブでインクリメンタルな開発を行うことが出来る

DDD導入メリット(人物)

各人の望むことが手に入る可能性が高まる。

  • 開発者
    • 適切で正しいソフトウェア開発手法を用いて、スキルアップしながら楽しく開発したい
  • シニア開発者
    • 開発者とドメインエキスパートの距離を縮め、事業価値を向上させたい
  • ドメインエキスパート
    • 開発者とコミュニケーションをスムーズに行い、顧客に喜ばれるソフトウェアを開発したい
  • マネージャー
    • 開発者が業務知識を得て、ドメインエキスパートが開発力を理解し、協力して結果を出してほしい

DDD導入メリット(具体例)

  • 従来
  • ドメインエキスパートが話す言葉と開発者の言葉が違い、実装内容にズレが生じる
  • 1つの大きなシステムのため、実装(プログラム)が肥大化する
  • 処理主体のトランザクションスクリプトでは、変更を入れる際のリスクと工数が肥大化する
  • 1つの大きなシステムのため、DB(永続化層)が肥大化する

  • DDD

  • ドメインエキスパートと開発者で共通言語(ユビキタス言語)で会話したとおりに実装できる
  • ドメインとコンテキストを適切に分割することで、業務知識ごとに機能を分離することが出来る
  • 業務知識を抽象化したドメインモデルを用い開発を行うため、見通しがよく変化に強くなる
  • ドメインとコンテキストを適切に分割することで、永続化ストレージ(RDB/NoSQL等)を適切に分離することが出来る

DDD用語集

  • ドメイン
    • ソフトウェアが対象とする業務領域のこと
  • ドメインモデル
    • 業務知識に則り、ドメインの名前や振る舞いが、適切に反映されている概念的なモデル
    • ドメインモデルをコードに落とし込むとソフトウェアモデル(OOPの場合エンティティモデル)となる
  • 境界づけられたコンテキスト
  • ユビキタス言語
    • ドメインエキスパートや開発者を含めたチームで共有する言語のこと
    • 名詞だけでなく形容詞や動詞も含まれる
    • 境界づけられたコンテキスト内で一意であれば良い
  • 戦略的設計
    • ユビキタス言語、境界づけられたコンテキスト、コンテキストマップを用いた抽象的なソフトウェア設計の総称
  • 戦術的設計
    • レイヤ化アーキテクチャ、エンティティ、サービス等の具体的なソフトウェア設計の総称

PHPとnginxとMySQLをdocker-composeでミニマム構築

ちょっとPHP触ってみたくなったのでnginxMySQLなWebアプリケーションをdocker-composeで構築してみる。

PHPの環境構築

PHPの実行環境をnginxで構築する場合、PHP-FPMを使いやり取りを行うらしい。
nginx と PHP-FPM の仕組みをちゃんと理解しながら PHP の実行環境を構築する - Qiita

php.iniファイルが↓

[Date]
date.timezone = "Asia/Tokyo"

[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"

Dockerfileが↓

FROM php:fpm-alpine

COPY php.ini /usr/local/etc/php/

RUN docker-php-ext-install mysqli pdo pdo_mysql mbstring

nginxの環境構築

nginxのパラメータは正直ググりながら適当にやったら動いたという代物笑。
本格的に使うときにちゃんと調べる。

server {
    listen 80;
    server_name _;
 
    root  /var/www/html;
    index index.php index.html;
 
    access_log /var/log/nginx/access.log;
    error_log  /var/log/nginx/error.log;
 
    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }
 
    location ~ \.php$ {
        fastcgi_pass php:9000;
        fastcgi_index index.php;    
        fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include       fastcgi_params;
    }
}

index.php

何を表示しようかと思ったけど、とりまDBに接続できてればよいかという発想で書いた。

<?php

printf("%s\n", date("Y/m/d H:i:s"));

$db = new mysqli("php-db", "root", "zaqroot", "test");

if ($db->connect_errno) {
  echo $db->connect_errno . " : " . $db->connect_error;
  exit();
}

$arr = $db->query("SELECT * FROM hoge");
while ($val = $arr->fetch_assoc()) {
  var_dump($val);
}

$db->close()

?>

docker-compose

肝心のdocker-composeファイルがこちら。
MySQLlatestを使ってます。

version: '3'
services:
  nginx:
    image: nginx:alpine
    container_name: php-nginx
    ports:
      - 8000:80
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - ./www/html:/var/www/html
    depends_on:
      - php
  php:
    build: ./php
    container_name: php-app
    ports:
      - 9000:9000
    volumes:
      - ./www/html:/var/www/html
    depends_on:
      - db
  db:
    image: mysql:latest
    container_name: php-db
    ports:
      - 13306:3306
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      - "MYSQL_USER=root"
      - "MYSQL_ROOT_PASSWORD=zaqroot"
      - "TZ=Asia/Tokyo"
    volumes:
      - ./mysql/data:/var/lib/mysql

出来たものがこちら

GitHub - yuki-toida/php-tutorial

感想

全然わからないが、なんとなくPHPを生のまま書くのは死ねる気がする。
一旦Laravel使ってみてからじゃないと評価できなそう。
というわけで次回はLaravel使う。

ライブ動画配信システムの構成図書いてみた

全部GCPで、
ストリーミングサーバはWowza Streaming Engine
アプリケーションサーバElixir/Phoenix
とりあえずザックリ全体像だけ
誰かの役に立てればいいなと

f:id:yuki-toida:20181115143426p:plain

Cloud Storage FUSEを使ってGCSをGCEにマウントする

Cloud Storage FUSE とは

GCSを、Linux または OS X システム上でファイル システムとしてマウントするためのGoogle謹製OSS

Cloud Storage FUSE  |  Cloud Storage  |  Google Cloud

インストール

gcsfuse/installing.md at master · GoogleCloudPlatform/gcsfuse · GitHub

export GCSFUSE_REPO=gcsfuse-`lsb_release -c -s`
echo "deb http://packages.cloud.google.com/apt $GCSFUSE_REPO main" | sudo tee /etc/apt/sources.list.d/gcsfuse.list
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
sudo apt-get update
sudo apt-get install gcsfuse

マウントする

GCSのバケット名を bucket-vod とするとし、マウント元を/home/bucket-vodとすると、

sudo mkdir /home/bucket-vod
sudo gcsfuse -o nonempty bucket-vod /home/bucket-vod

これで、/home/bucket-vodbucket-vod にマウントされる。

GCE起動時にgcsfuseを実行する

GCEのカスタムメタデータstartup-script に同じコマンドをを仕込む。
これで、サーバー再起動等行っても正常にマウントされる。

sudo gcsfuse -o nonempty bucket-vod /home/bucket-vod

GCE で Wowza Streaming Server をつくる② GPUアクセラレーション編

前回の続き。
この記事ではGPUをGCEで使えるようにする設定を行う。

NVIDIA Driver をダウンロードする

Nvidiaの公式サイトから下記画像の沿ってドロップダウンを選択しダウンロードする。

  • Tesla
  • K-Series
  • Tesla K80
  • Linux 64-bit

f:id:yuki-toida:20181114162448p:plain

GCEでNVIDIA Driver を使うためのセットアップ

Set up NVIDIA NVENC accelerated encoding on Debian

この辺を参考にしつつ、ウルトラググりながら依存ライブラリをインストールする。

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install build-essential
sudo apt-get install linux-headers-$(uname -r)
sudo apt-get install dkms

blacklist.confblacklist nouveauを追加する。 vimで編集します。

sudo vim /etc/modprobe.d/blacklist.conf

ここで忘れずにGCE再起動。

NVIDIA Driver をGCEにアップロードしインストールする

GCEインスタンス名をwowzaとし、ドライバをNVIDIA-Linux-x86_64-396.26.runとすると、
下記SCPコマンドでアップロード可能。

gcloud compute scp ./NVIDIA-Linux-x86_64-396.26.run wowza:~/

GCEターミナル内でドライバを実行権限をつけてインストールする。

chmod +x NVIDIA-Linux-x86_64-396.26.run
sudo ./NVIDIA-Linux-x86_64-396.26.run

動作確認

nvidia-smi コマンドでGPUの使用状況が取得できれば正常にインストール完了。
一筋縄ではいかなかったので、ググラビリティが試されるかも。

f:id:yuki-toida:20181114164703p:plain

GCE で Wowza Streaming Server をつくる① GCE起動編

Wowza Streaming Engine(WSE)のライセンス等は既に取得済みとして進める。
この記事では、WSEのOSイメージを作成し、GCEのイメージとして登録し、
ファイアーウォールのルールを設定し疎通できるようにし、GPUアクセラレーション前提でGCEを作成する。

WSEのOSイメージを作成する

GCPでホストされているWSEのバージョン4.7.6を使用する。
OSイメージ名はwowza-476とする。

PROJECT_ID={個別入力}
IMAGE=wowza-476
IMAGE_URI="http://storage.googleapis.com/wowzamediasystems/wse/WowzaStreamingEngine-4.7.6-byol-20180809.image.tar.gz"

gcloud compute images create $IMAGE --project=$PROJECT_ID --source-uri=$IMAGE_URI

これでGCEのOSイメージとしてwowza-476が使えるようになる。

ファイアーウォールのルールを作成する

WSEのRTMPプロトコルとWSE Manager(管理画面)のAPIアクセスを有効にするために、ファイアウォールルールを作成する。
tcp:554,tcp:1935,tcp:8084-8089を通すために追加する。

PROJECT_ID={個別入力}
RULE=wowza
TCP_STREAMING="tcp:554,tcp:1935,tcp:8084-8089"

gcloud compute firewall-rules create $RULE --allow=$TCP_STREAMING --project=$PROJECT_ID

GCE作成

GPUアクセラレーションを行いたいので、GPUタイプのマシンを選択する。
東京リージョンでは使えないので、台湾リージョンのものを使う。

  • リージョン
    • asia-east1
  • マシンタイプ
    • n1-standard-8
  • マシンタイプGPU
  • カスタムイメージ
    • wowza-476
  • カスタムメタデータ
    • WZA_serverLicenseKey : {ライセンススキー}
    • WZA_managerUsername : {管理画面ログイン名}
    • WZA_managerPassword : {管理画面ログインパス}
  • ネットワークタグ
    • wowza

これで、GCEでWowzaStreamingEngine サーバーを構築完了。