Somurie Engineer's Blog

AWS lambdaでKotlinHelloWorld

はじめに 先に書いておくと、色々試した結果↓このサイトがかなりわかりやすい。 ServerlessFrameworkでtemplateを使用してプロジェクトを作成でき、gradleやserverless.ymlも作成してくれるので、templateを使うべき。 Kotlinプロジェクトをgradle buildでzip化してServerlessFramework(awsやgcpへのプロビジョニング等を共通化して行うフレームワーク)でaws上にデプロイするのだけど、一連のテンプレートを作成してくれるのは本当に楽。自力でやろうとしてかなり時間がかかった。(理解は深まったが) https://dev.classmethod.jp/articles/easy-deploy-of-lambda-with-serverless-framework/ 以下、templateを使わずに色々と苦戦した経緯を記録する。 Kotlinプロジェクトを生成する 以下コマンドを実行する。自動でKotlinプロジェクトが生成される gradle init --type=kotlin-application ちなみに2020/5/23時点でAWSLambdaのJava対応バージョンは8と11のみ。 サンプルの実行(一旦動作確認 プロジェクトをbuild、App.ktのmainメソッドを実行すると’HelloWorld’が表示されることを確認します。 ※ちなみにIntelliJで毎回忘れるんですが、以下より使用するSDKのバージョン設定等をする ProjectStructure -> Project -> Project SDK build.gradleの修正 作成した案件をIntelliJなどIDEで開き、build.gradleに以下を追加する。依存関係を取り込むにはIntelliJの場合 Gradleビュー -> Reimport All Gradle Projects を押下。 // AWS implementation 'com.amazonaws:aws-lambda-java-core:1.2.0' implementation 'com.amazonaws:aws-lambda-java-events:2.2.7' implementation 'com.amazonaws:aws-java-sdk-s3:1.11.647' SAM CLIでlambdaのペイロードを確認 AWSのサーバーレスアプリケーションの作成と管理を簡単に行うためにSAM CLIを使用します。 まずはsam local generate-eventを使用してサンプルペイロードを生成します。 特に何か作成されるわけではなく、lambdaが受け取るペイロードのサンプルなので出力された値を参考に実装します。 https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-local-generate-event.html sam local generate-event apigateway aws-proxy --method POST 実装 lambda関数のハンドラーはRequestHandleを実装したクラスのhandleRequestメソッドです。 https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/java-handler.html 今回は例として、JSONで受け取りStringを返すメソッドを作ってみます。 まずJSONを扱うために以下をbuild.gradleのdependencyに追加します。 implementation 'com.fasterxml.jackson.module:jackson-module-kotlin' Gradleでzipを作成 以下の通りgradle build実行時にzipを生成するよう記述します。 以下の記述をしてもしなくてもbuild/distributionにzipファイルは作成されていた。なんでだろう・・・。 task buildZip(type: Zip) { from compileKotlin from processResources into('lib') { from configurations.

書籍「ドメイン駆動設計入門」をKotlin+TDDで試してみた感想

はじめに 成瀬さんの書籍「ドメイン駆動設計入門」を読んで気づいた点、ポイントをまとめました。 自分用メモなので新たに気づいた点や忘れかけていた点を洗い出しています。 実装に関してはKotlinとTDDを初めて使ってみました。普段はJavaがメインですが、Kotlinを使ってみての感想も後ろにまとめてあります。 書籍を読んで気づいた点・ポイント ドメイン駆動設計を選ぶ理由 ドメイン駆動設計は初期開発速度は劣るが、サービスの変化、開発途中における要件の変化などへの対応が柔軟。長期目線で見たときにドメイン駆動設計の優位性がある。 値オブジェクト 値オブジェクトの分割基準として、ドメイン内で分ける必要がある単位で分割する。ルールを作成する必要がある単位など、取り扱ううえでまとめる必要があるかがポイント。 細かくしすぎるとメンテナンス性が低くなる。 ある程度、不変性を保つことができる範囲で作成する。 値オブジェジェクトを使用することで使用法に強制力を持たせるため、バグ混入を防ぐことに繋がる。また、ロジックを1箇所に集めることになるため、メンテナンス性も高い。 エンティティ DB設計時にER図でエンティティという名が用いられるがドメイン駆動では別の意味を表している。 エンティティには値オブジェクトにはないライフサイクルの概念が存在する。すなわちエンティティはシステム利用中に値が変化したり削除されたりする。 ドメインサービス ドメイン駆動設計で表されるサービスはドメインサービスとアプリケーションサービスの2つ。明確に分ける必要がある。(エヴァンス本ではサービスは1括りである) ドメインサービスには値オブジェクトやエンティティに書くと不自然になるものを記述する。 値オブジェクトやエンティティと異なり自身のふるまいを変更する記述は書かれず、複数のドメインオブジェクト(エンティティ及び値オブジェクト)を横断して操作する処理を記述する。 エンティティに書くことができる処理をドメインサービスに書いてしまうとドメイン欠乏症に陥る。エンティティの責務をドメインサービスが奪わないようにする。 命名規則としては「ドメイン名 + Service」が良さそう。自分の知っているプラクティスではパッケージによってdomainやserviceを分けているが、本書ではXxxDomain.Services.XxxServiceのようにドメイン単位でサービスを分けてることを例に挙げている。 リポジトリ リポジトリはドメイン駆動設計以外でも頻繁に目にする考え方。オブジェクトの永続化、再構築を行う。 重複確認existsメソッドはリポジトリではなくドメインサービスに実装すべき。何をもって重複とするかはドメインの責務。 永続化する単位で処理を行う。一部しかupdateしない場合でも永続化単位を引数として受け取る。 リポジトリクラスを切り替えることでインメモリのDBを使用し、テストを行う記述があったがコーディング量が多くなるため接続先DBを切り替えるだけの方が実用的な気がした。 アプリケーションサービス ユースケースを実現するオブジェクト。メソッド1つがふるまい1つを表すイメージ。 アプリケーションの返り値がドメインの場合、プレゼンテーション層でドメインを操作される可能性がある。返り値は画面表示用のクラスにするなど、境界を保つことも検討する(クリーンアーキテクチャのイメージ)。開発コストや規模との兼ね合い。 updateのように何かしら命令を送る場合、引数を修正することを避けるためにcommandクラスというDTOを引数とすると疎結合になる。 凝集度を測る指標としてLCOMがある。インスタンス変数が全てのメソッドで使用されているかを基に測る指標。 インスタンス変数を使用しているメソッドが偏っている場合はクラス分割を検討する一因となる。 サービスとはクライアントのために何かを行うもの。自身のふるまいを持たない。 ドメインにおける活動をドメインサービス、アプリケーションとして成り立たせるためのサービスをアプリケーション・サービスといった形で領域を明確に分けする。 ファクトリ IDの採番はどこの責務か。DBをの自動採番、ファクトリ、リポジトリの3つが挙がっているが、自動採番することを開発者内の暗黙の了解とするのが最適としている。 著者はリポジトリの責務は永続化と再生成が主と考えているためリポジトリ内の採番を推奨していない。。 ドメインオブジェクトがふるまいとして定義できるようであればあるドメインオブジェクトが他のドメインオブジェクトを生成するようにファクトリをメソッドとして定義することができる。 データの整合性を保つ 整合性を保つための手段として特定の技術基盤に依存することは避けるべき。DBに頼った場合、整合性の条件変更の対応が漏れる可能性がある(例えばテーブル内のユニーク条件が変化した場合、プログラム上の記載がなくDBのユニークキー設定変更は漏れやすい) アプリケーションサービスでトランザクション管理をすると、RDBに依存していることになる。JavaのSpring上の実現方法としては、AOPを実現する@Transactionalアノテーションを付与することでメソッド内のトランザクション整合性を担保することができる。 アプリケーションを1から組み立てる 集約 DomainのクラスをRepositoryで永続化に使用するDataClassに変換する際、Domainのフィールドを直接取得するのではなく通知オブジェクトを経由してDataClassを生成するパターンがある。Domainのフィールドに直接アクセスさせないための強制力がかなり強い。 Scalaだとフィールドのアクセス権限をInterface単位で設定でき、Interfaceを実装するクラスからのアクセスのみを許可することができる。 リポジトリは集約の単位で作成する。永続化の単位が集約であるため。 集約の単位でデータをロックしてしまうため、大きすぎる集約は厳禁。 仕様 仕様を全てDomainクラスに盛り込んでしまうと肥大化してしまい変化が容易ではなくなる。 仕様クラス(〜Specificationクラス)として括りだすことで扱いやすくなる。(リポジトリに仕様クラスの処理を移譲するとパフォーマンス上の問題もある) 検索に関わる複雑・特殊な条件の場合はリポジトリを使わない方法もとり得る。ドメインの防衛を第一に考えず、アプリケーションの猟奇はプレゼンテーション(利用者の利便性)を第一に考える。例えばドメイン内の操作でN+1問題が発生しそうだったりページング処理などがあれば1回のSQLクエリで取得できるようにするなど、例外的な対応も検討する。 アーキテクチャ アーキテクチャはドメイン知識が流出しないように記述すべき箇所を示す方針。 レイヤードアーキテクチャはオブジェクトを4つの層(プレゼンテーション・アプリケーション・インフラストラクチャ・ドメイン)に分け、それぞれの層が責務を持つ。また、記載した4つの層の順に上位層から下位層へ依存が存在し、逆の依存は許されない。 ヘキサゴナルアーキテクチャはアプリケーションに対するその他のインターフェース・保存媒体を取り外しできるようにすることがコンセプト。インターフェースを利用して依存性逆転の原則を実現するため、レイヤードアーキテクチャとは依存関係が異なる。 クリーンアーキテクチャのコンセプトはヘキサゴナルアーキテクチャと同じ。ビジネスルールをカプセル化したモジュールを中心に据え、依存関係を制御する。加えて、具体的な実装方法を明示している。 アーキテクチャを適用することで開発方針が決まるため、システム開発中に考える問題を少なくすることができるため有用。 ドメイン駆動設計の扉を開こう ユビキタス言語を使う。updateなのかchangeなのか、システマチックな言葉ではなくドメインエキスパートが使う言葉に合わせる。 同じ言葉でも視点が変わることにより必要な属性は異なる。境界づけられたコンテキストを適用し、別のエンティティを作る方が賢明。大規模になればなるほど同一のエンティティで全ての動きを賄うことは困難。 Kotlinで試し書きした感想 総じて書きやすかったです。Javaよりコード記述量が少なく、nullSafeな書き方ができる点がDDDに向いているなと思います。 nullSafeだとコンパイルエラーとしてnull混入を防ぐことができる点は本当に助かります。 nullSafeな言語は記述量がかなり少ない。DDDに向いている 値オブジェクト書きやすい。プライマリコンストラクタが便利 safe call (?