イベント駆動型の考え方

in Vlog

(CJP) Spatie Web サイトの新機能に取り組んでいます。±10,000 人のユーザー向けに軽量形式のゲーミフィケーションを追加しています。

仕組みは次のとおりです。ユーザーがコース ビデオの 1 つを視聴するたびに、ビデオ シリーズを完了するか、プル リクエストが GitHub でマージされるたびに、経験値を受け取ります。 それに加えて、ユーザーの努力に報いるいくつかの成果もあります。 たとえば、「10 プル リクエスト」の実績や、「100 XP」の実績などがあります。

アチーブメントを取得すると、ユーザーにデジタル バッジが与えられ、場合によっては何らかの証明書も取得されます。

ご覧のとおり、これは境界が明確に定義された小さな封じ込めシステムです。 今日はその一面についてお話ししたいと思います。

考えられるフローの 1 つ、プル リクエストの報酬システムに注目してみましょう。 それにはいくつかのステップがあります:

ユーザーのプル リクエストを登録する
それに対していくつかのXPを与えます
このプル リクエストが実績の対象かどうかを確認する
その場合は、バッジのロックを解除してユーザーに通知します

このフローを箇条書きで書くと、プル リクエストの報酬システムが直線的なプロセスのように感じられます。 実際、次のように書くことができます。

class RegisterPullRequest

public function __invoke(PullRequestData $data): void

$pullRequest = PullRequest::create(…$data);

$user = $pullRequest->user;

$user->award(10);

$pullRequestCount = count($user->pullRequests);

if (in_array($pullRequestCount, [10, 50, 100]))
$achievement = new PullRequestAchievement($pullRequestCount);

$user->unlock($achievement);

$user->notify(new AchievementNotification($achievement));

このコードをリファクタリングしてメソッドを分離したり、注入されたアクションやサービス クラスを使用したりすることもできます (そのスタイルのプログラミングを好む場合)。 しかし、このアプローチの欠点を簡単に明確にするために、この例を簡潔にしたかったのです。

このコードは、最初にリストした順序付けられたステップを明確に表していますが、いくつかの隠れたコストが伴います。 それを書いている時点では明らかでないかもしれないコスト。 この実装には 2 つの問題が隠されていることがわかります。

プログラムの流れを固定された順序にハードコーディングしました
多数の責任を 1 つの巨大なクラスに混ぜ合わせました

「XP の授与」と「実績のロック解除」について少し考えてみましょう。 これらは、私たちのシステムの 2 つの等しく重要な部分です。 実際、XP が授与された実績もあります。これは、現在の実装が不足しているか、いくつかの機能が追加されていることを意味します。 $user->award(10); 私たちが知らないこと。 とりあえず後者としましょう。

これら 2 つの部分は等しく重要であり、互いに直接依存するわけではありませんが、これらは一緒に属しているように見えるため、1 つのプロセスにまとめました。 ただし、そうすることの残念な副作用は、 RegisterPullRequest クラスはますます大きく、より複雑になっています。 プル リクエスト アチーブメントの処理方法を変更すると、必然的に XP 報酬が処理されるのと同じ場所に移動します。

この孤立した (単純化された) 例について推論するのはまだ比較的簡単だと思うかもしれませんが、私たちのほとんどは、はい、実際には、いくつかのプロセスを 1 つに混ぜ合わせていることに同意できると思います。複雑なプロセスを管理および監督する「クラス」。 単一障害点を作成しました。 そして、ビジネスが複雑になればなるほど、このコードはより大きく、より複雑になり、推論が難しくなる可能性があります。

私自身、この種のクラスを認めたくないほど書いてきましたし、それが他の多くのコード ベースにも適用されているのを見てきました。 経験上、現在取り組んでいる例よりもはるかに大きくなることがわかります。

ここまでたどり着いた理由は理解できます。 エントリーポイント 私たちのコードでは、いいえ? 複雑なプロセスは何らかの方法で結び付ける必要があります。 それを避けることはできませんよね?

tpyoに気づきましたか? PR を送信して修正することができます。 このブログの最新情報を知りたい場合は、私をフォローしてください。 ツイッター または私のニュースレターを購読してください:

イベント駆動型システムについて初めて知ったとき、私は躊躇し、懐疑的でさえありました。 イベントは、「プログラムの流れ」をたどるのをより難しくするコードに避けられない間接的なレイヤーを導入します。 ただし、すべてを緊密に結合したままにします また 別の方法で、プログラムの流れを理解するのが難しくなります。

イベント駆動型システムの間接性は、実際に私たちの問題の解決策です。 イベント ドリブン アーキテクチャは一見複雑すぎるように感じるかもしれませんが、プロセスをクリーンな方法でモデル化するために必要な柔軟性を提供します。私たちの現在のソリューション。

イベント駆動型システムでは、「XP 報酬」と「実績のアンロック」の両方が 2 つの独立したシステムとして扱われます。 彼らはお互いを知る必要はありません。 彼らが知る必要がある唯一のことは、プル リクエストがマージされたとき、つまりイベントが発生したときです。

両方のシステムがイベント リスナーになりました。 PullRequestMerged イベントがディスパッチされます:

class AchievementManager

public function __invoke(PullRequestMerged $event): void

$pullRequestCount = User::find($event->userId)
->pullRequests
->count();

if (! in_array($pullRequestCount, [10, 50, 100]))
return;

$achievement = new PullRequestAchievement($pullRequestCount);

$user->unlock($achievement);

$user->notify(new AchievementNotification($achievement));

class ExperienceManager

public function __invoke(PullRequestMerged $event): void

$user = User::find($event->userId);

$user->award(10);

これら 2 つのシステムが分離されたので、それらは分離されているため、それらについて推論するのははるかに簡単です.

ところで、それだけではありません。 この投稿の冒頭で述べた「一定量の XP の達成」についてはどうでしょうか。 ExperienceEarned 私たちのイベントそのものかもしれません AchievementManager 同様にリッスンします:

class AchievementManager

public function onPullRequestMerged(PullRequestMerged $event): void

public function onExperienceEarned(ExperienceEarned $event): void

$user = User::find($event->userId);

$currentCount = $user->experience;

$previousCount = $currentCount – $event->amount;

if ($previousCount >= 100)
return;

if ($currentCount unlock($achievement);

$user->notify(new AchievementNotification($achievement));

実績が解除された後にメールを送信するのはどうでしょうか? それはまた、イベントによって駆動される可能性があるため、 AchievementManager メールを処理するリスナーを追加できます。 プル リクエストをデータベースに永続化するのはどうでしょうか。 これもイベント駆動型である可能性があります。 経験を積む実績? リストは続きます。

これがイベント駆動型システムの優れた点です。密結合されたコンポーネントを削除することで、個々のコンポーネントを小さく持続可能に保ちながら、柔軟性を大幅に高めることができます。 それに加えて、イベントは、マイクロサービスのメッセージングや水平方向のスケーリングなどを処理する優れた方法ですが、これらすべての利点について 1 つのブログ記事で説明するには多すぎます。

もちろん、いくつかの重要な詳細についても触れています。結果整合性についてはどうでしょうか。 または、イベント自体の永続化についてはどうでしょうか。 イベント駆動型システムには、今日お見せしたものよりもはるかに多くの機能がありますが、 やりました イベントで考える力を示します。 このアイデアだけでも、私のコードの見方に革命をもたらしました。

tpyoに気づきましたか? PR を送信して修正することができます。 このブログの最新情報を知りたい場合は、私をフォローしてください。 ツイッター または私のニュースレターを購読してください:

このトピックに本当に興味がある場合は、Laravel でのイベント ソーシングに関する私のコースを共有したいと思います。 これは、イベント駆動型システムに関連する多くのトピックをカバーする詳細なコースであり、Laravel や PHP の事前知識がなくても、たくさんのことを学ぶことができます。 詳細を知りたい場合は、サンプルの章を 1 つか 2 つ読むことができます。

以上のことを踏まえて、あなたの考えを教えてください ツイッター この投稿に感謝したら、いいねを残してください。

関連記事

前の投稿
デラウェア州の鹿シーズン: 準備するために知っておくべきことすべて
次の投稿
七面鳥は冬にどこに行きますか?