AWSで実現するモダンアプリケーション入門 を読んだ
自身のモダンアプリケーション?の経験
何となく経験出来ているような・・
- Unit Testのカバレッジ100%を実現
- CircleCIで以下を実行
- Unit Test
- E2E Test
- TestがAll Greenの場合はデプロイを実行
- Twelve-Factor AppなAppを作成したので、イミュータブルなデプロイを実現
- (Unit Testのカバレッジ100%なので)GitHubのDependabotを有効にして、Pull Requestをmerge
感想
- 構成が良い。最初に言葉の定義をして、次にモダンでは無いアプリケーションとこのアプリケーションが抱える具体的な課題も明確にしている。ピラミッドストラクチャーですね!
- モダンでは無いアプリケーションをモダン化する際に既存のアーキテクチャを無理矢理変更しない点が実際の現場に寄り添っている感じで良い
- コンテナやサーバレスの前にオブザーバビリティを取り上げてるのも素敵。過去に、NewRelicやDatadogを導入してAPMの画面を見せたら、「え?こんな情報が見れるんですか?」的な反応をする人が多かったです
- オブザーバビリティとモニタリングの違いについて簡潔に説明してるのも良い
- コンテナとサーバレスを比較して、サーバレスの方が開発に集中出来るとあるが、APIのエンドポイントが多いサービスだとコンテナ上でWebアプリケーションフレームワークを動かして、エンドポイント量産する方が集中出来るような。もう少し具体例が欲しい・・
- アプリケーション側が発火したEventはSNS。AWSのリソースが発火したEventはEventBridgeと勝手に理解していたのだが、少し混乱してきた
- 開発のベストプラクティスやAPIの冪等性など、AWSのサービス以外の知識が増える
ECサイトで決済代行のAPIを利用した際に注文番号をRequestに付与していた事を思い出しました - 自分自身が大量データの扱いについて悩んでいたので、要件にあったデータベースの選択は助かりました
- (ADOTなど)全く知らないサービスを知ることが出来た。NewRelicやDatadogがサポートしていない言語はADOTを使用すれば良いのだろうか?
まとめ
モダンなアプリケーション開発したいが、周りの理解が得られず悩んでいる方はこの本で布教活動を開始できるくらいの良書です
Software Design 2023年2月号 ドメイン駆動設計入門 を読んだ
自身のドメイン駆動設計の理解や状況
- 実践ドメイン駆動設計 | ヴォーン・ヴァーノン, 髙木 正弘 |本 | 通販 | Amazon は読んだ
- 値オブジェクト、サービスは実務で使用した
ドメインモデル貧血症 - Martin Fowler's Bliki (ja) なサービスもいくつか実装したと思います・・ - ユビキタス言語をビジネス側と共有したことが無い
- 境界付けられたコンテキストは使用したことが無い
CMSやECなどの比較的理解が容易なドメインのWebアプリケーション開発が多いかも知れません - 主に使用しているフレームワークはPHPのLaravel
- 多分、エンティティはEloquentでしょう?
- EloquentはActiveRecordパターンなのでリポジトリとは相性悪そう
感想
- DDDの目的(事業の変化とともに成長と進化を続ける)を意識していなかった p19
実務では、アジャイルな開発・テスト自動化・CICDパイプラインで成長と変化に対応している
DDDはソフトウェアアーキテクチャとして使用しているが、この視点は欠けていた - 業務とプログラムのトレーサビリティを確保 p23
業務知識・モデル・プログラムのトレーサビリティを確保しないと、事業の変化にシステムが追いつかないですよね・・
普段から後続のフェーズを意識した成果物を作成しているので、これは大丈夫 - 「複雑な業務ロジックに焦点を合わせる」のドメイン駆動設計の開発のやり方 p27
- 複雑な業務ロジックが少ないシステムで全ての処理にDDDを適用!となると、フルスタックフレームワークの長所を消してしまう短所の方が上回るので注意が必要
- 複数のレイヤーで構成。かつ、各レイヤー間の値を受け渡しにDTOを使っていた設計を見直しした記憶が蘇ってきました
- 業務ルールからエンティティ・値・コレクション・区分オブジェクトを抽出する方法が纏まっていてとても参考になる p35-40
- 特に業務の概要からエンティティを見つける点に関しては、業務知識とモデルのトレーサビリティ確保の点でとても重要
- 実務ではECサイトの注文番号を値オブジェクトにして、Eloquent:ミューテタ/キャスト 8.x Laravel で取り出せるようにしていました
- SIer時代、システムのリニューアル時は現地で現行システムの具体的な運用(画面操作して頂くなど)を見せて頂きたいと依頼してました。見学は良いですね p53
- WBSで予めリファクタリングのタスクを入れるのは自分も難しいと感じた。機能開発時に要リファクタリングみたいなTODOコメントを残して、これらが溜まったらリファクタリングしているので、予めスケジューリングするのは難しい p59
まとめ
「複雑な業務ロジック」を対象していることが理解出来たのが良かった
業務知識・モデル・プログラムのトレーサビリティが最も重要だと感じたので、これらのサンプルがあれば嬉しかった
37signals Dev — Vanilla Rails is plenty を読んだ ServiceじゃなくてMix-inで良いのかも知れない
自身のフレームワーク経験
- 昔のJava FW. Jakarta Struts cFramework Teeda(Seasar Project)
- CakePHP 2.x
- Symfony 3.x
- Laravel 5.x - 8.x
- CakePHPでModelがFATになるのが辛かったので、Laravelを使用した開発では無条件にControllerとModelの間にService層を追加していた
感想
色々な問題が解決するので、Mix-inを試してみようと思いました
命名問題
recording.incinerate
Recording::Incineration.new(recording).run
Recording::IncinerationService.execute(recording)it feels more natural, like plain English. It feels more Ruby.
{DomainName}Service.execute
だと、使う側は仕様が分かり辛い、作る側はServiceの単位や名称を考えるのが辛い問題があるので、Mix-inの圧勝
正直、Service層を追加してから毎回悩んでます
Anemic Domain Model問題
Don’t lean too heavily toward modeling a domain concept as a Service. Do so only if the circumstances fit. If we aren’t careful, we might start to treat Services as our modeling “silver bullet.” Using Services overzealously will usually result in the negative consequences of creating an Anemic Domain Model, where all the domain logic resides in Services rather than mostly spread across Entities and Value Objects
Modelの属性しか操作していないServiceのfunctionがあったら、適宜Modelに移動している。Serviceが無ければModelに実装するので、この作業は不要になる
移譲問題
Tons of boilerplate code because many of these application-level elements simply delegate the operation to some domain entity. Remember that the application layer should not contain business rules. It just coordinates and delegates work to domain objects in the layer below.
マスタメンテ系の単純な画面だと、ServiceがModelに移譲するだけになるので、Service層を使わない判断をしている。これも、Serviceが無ければModelに実装するので、この作業は不要になる
LaravelのRoute Model Bindingを使用すると、LaravelがControllerとModelを紐付けするのでServiceを強制すると冗長的なコードになってしまう
- Service無し
<?php use App\Models\User; // Route definition... Route::get('/users/{user}', [UserController::class, 'show']); // Controller method definition... public function show(User $user) { return view('user.profile', ['user' => $user]); }
- Service有り
<?php public function show(int $userId) { $service = app(UserService::class); $user = $service->findBy($userId); return view('user.profile', ['user' => $user]); }
図解 Amazon Web Servicesの仕組みとサービスがたった1日でよくわかる を読んだ
購入した理由
50%OFF and AWS全然知らない人とのコミュニケーションを円滑にする
自身のAWS経験
3年程度。それまではオンブレミス
主なロールはBackendのSWE。オンプレミス時代はインフラ未経験
触ったことのあるサービス
- Elastic Beanstalk / EC2 / ECS / App Runner / Lambda
- CloudFront / RDS / Aurora / ElastiCache / S3 / CloudWatch Logs / Athena / SES
- CodeDeploy / SAM / CDK
良かった点
プロビジョンド方式 という単語。「サーバレスじゃなくて通常の奴って何て言うんだっけ」とよく忘れるので
物理サーバ・仮想サーバ・コンテナの違い。一軒家・マンション・シェアハウスの例は分かりやすい
割引(RIとSP)に触れている。長期間の利用が確実な場合は使った方が良い
高価なRDSは劇的に安くなりますよ!コンテナのメリット コンテナイメージはどの環境でも起動出来る点
EC2を直接使用の場合はAMIを生成するツール(Packerなど)、Elastic Beanstalkの場合は設定ファイル(.ebextensions)にDockerfileと同じような記述をすることになるので2重メンテナンスが面倒だったネットワーク。VPCやサブネットを単体で理解するのは難しいですが、Webアプリケーションの一般的な構成例が図解されてるので理解が比較的容易になっている
データベース RDSとAuroraの違い。Auroraは書き込みと読み込みのエンドポイントが異なる点など
LaravelはWrite/Read毎にエンドポイント指定出来ますねIAM ユーザとロールの違い。ユーザは人、ロールはAWSのサービスが使う
Backendのプログラムで環境変数にIDとシークレットを登録して、AWS SDKを使用しているのをたまに見かけます複数AWSアカウントの利用
後からマルチアカウントにするのは結構大変なので、最初から本番とテストは別のアカウントの方が良いですCloudWatch Logs アプリケーションログを転送することでAWSの管理コンソールでログを確認出来る
オンプレミスだとサーバにSSHして、tail
やgrep
してログ見てますけど、これは不要
追加するとすれば
JSONを説明するなら、jq も一緒に
AWSの利用頻度が少ない人はJSONに不慣れな可能性があるので jq の存在を知らないかも(DynamoDBの利用シーンの記載はあったので)DocumentDBの利用シーン
JSON形式で保存出来るので、Google Formsみたいなフォームビルダーで活躍するのかも(使用した事が無いので想像です・・)
まとめ
主要なサービスの概要が纏まっているので、薄く広く理解出来る良書でした
深堀したい場合は公式を見る。内容が難しいと感じたら、AWSの管理コンソールを操作すれば理解が深まるでしょう
VPC関連が完全な初心者だと結構難しいはず
一旦スキップして、ELB・Route53・CloudFrontだけの理解でも良いかも
SES メールを連続送信後にAthenaで結果を確認すると1件しか表示されない Kinesis 動的パーティショニングキーで解決
最近、SESと戯れています
事象
SESを使用してメールを連続送信後にAthenaでメール送信の結果を確認すると1件しか表示されない
CREATE EXTERNAL TABLE SES_RESULTS ( notificationType string, mail struct <messageId:string, commonHeaders:struct < `from`:array<string>,`to`:array<string>, subject:string >>, delivery struct < timestamp:string > ) ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' WITH SERDEPROPERTIES ('ignore.malformed.json' = 'true') LOCATION 's3://bucket_name/[folder]/'
メールボックスシミュレーターを使用しています docs.aws.amazon.com
WITH dataset AS ( SELECT notificationType, mail.commonHeaders.to as to_addresses, mail.commonHeaders.subject as subject, mail.messageId AS messageId FROM SES_RESULTS ) SELECT notificationType, TIMESTAMP, to_addresses[1] as to_address, subject, messageId FROM dataset WHERE to_addresses[1] = 'success@simulator.amazonses.com' ORDER BY TIMESTAMP DESC LIMIT 10
原因
Q8. Amazon Kinesis Firehose を利用してCloudWatch LogsをS3に転送してそれをAthena で分析したいのですが、Kinesis Firehoseを通すと{json}{json}のように1行に複数のJSONオブジェクトが保存されるようです。このデータを効率的にAthenaで分析するにはどういった方法がありますか?? A8. Amazon Kinesis FirehoseにはData TransformationをAWS Lambdaで行う機能がございますので,こちらを使って所望の形式に変換すると良いです. http://docs.aws.amazon.com/firehose/latest/dev/data-transformation.html
確かに1行に複数のJSONオブジェクトが保存されている。送信したメールの件数も20件で一致している
% cat ses-kinesis-8-2022-10-07-00-19-59-f5cec068-edf9-414b-a03d-xxxxxxx | jq | grep notificationType | head -n 5 "notificationType": "Delivery", "notificationType": "Delivery", "notificationType": "Delivery", "notificationType": "Delivery", "notificationType": "Delivery", % cat ses-kinesis-8-2022-10-07-00-19-59-f5cec068-edf9-414b-a03d-xxxxxxx | jq | grep notificationType | wc -l 20
対応
動的パーティショニングを使用する aws.amazon.com
SES ログ
こんな感じのフォーマットなので
{ "notificationType": "Delivery", "mail": { "timestamp": "2022-10-07T00:19:58.503Z", "messageId": "01010183afd029a7-914ed9cd-641a-4eaf-87de-xxxxxxx-xxxxxxx", } } { "notificationType": "Delivery", "mail": { "timestamp": "2022-10-07T00:19:58.498Z", "messageId": "01010183afd029a2-a2d69f7f-4b3f-4a07-9935-xxxxxxx-xxxxxxx", } }
Kinesis
ses-log-partitioning-!{partitionKeyFromQuery:year}/!{partitionKeyFromQuery:month}/!{partitionKeyFromQuery:day}/!{partitionKeyFromQuery:messageId}/
S3
- 年月日 + MessageIdのフォルダに分かれている
- 送信したメール数とオブジェクト数が一致している
Athena
送信したメールとレコード数が一致する
おまけ
制限とクォータ
Kinesis Data Firehose 動的パーティショニング では、データをアクティブにバッファリングしている間、配信ストリームあたり 500 のアクティブパーティションの制限があります。これは、設定されたバッファリングヒント中に配信ストリームに存在するアクティブなパーティションの数です。この制限は調整可能で、引き上げたい場合は、制限引き上げのためのサポートチケットを送信する必要があります。
一度に500件以上のメールを送信すると発生しました。なかなか面倒ですね
{ "attemptsMade":1, "arrivalTimestamp":1665109794722, "errorCode":"DynamicPartitioning.ActivePartitionsLimitExceeded", "errorMessage":"active-partition-count-exceeded", "attemptEndingTimestamp":1665109885901, "rawData":"=" }