Event-Driven Architecture: event ও versioned contract
Event-Driven Architecture and Contracts
এই পাঠের সূচিপত্র
এই পাঠে যা শিখবেন
- Command ও event-এর ভাষা আলাদা করুন
- Versioned event contract লিখুন
- Hidden coupling এবং eventual behavior দেখুন
সমস্যা দিয়ে শুরু
Order service payment, shipping ও email API সরাসরি ডাকে। একটি নতুন loyalty service যোগ করতে order deploy করতে হচ্ছে। Dependency down হলে checkout আটকে যায়। Event ব্যবহার করতে গিয়ে আবার সবাই database table-এর ভেতরের column name-এর উপর নির্ভর করছে।
সহজ করে বুঝি
Event-driven architecture-এ ঘটেছে এমন fact প্রকাশ করা হয় এবং interested consumer নিজের কাজ করে। Command নির্দিষ্ট কাজের অনুরোধ; event অতীতের তথ্য। Stable event contract implementation details থেকে আলাদা হওয়া দরকার। Asynchronous flow coupling মুছে দেয় না; timing ও schema coupling অন্যভাবে আসে।
পরিচিত একটি উপমা
দোকান “বিক্রি সম্পন্ন” notice দেয়; হিসাবরক্ষক commission গোনেন, warehouse packing শুরু করে। Notice-এ হিসাবরক্ষককে কীভাবে spreadsheet সাজাতে হবে বলা হয় না। তবে বিক্রির অর্থ বা পরিচয় বদলালে সবার interpretation বদলে যেতে পারে।
নকশাটি দেখুন
Diagram লোড হচ্ছে…
Diagram-এর text flow দেখুন
flowchart LR O[Order Aggregate] --> E[OrderPaid v1] E --> W[Warehouse] E --> L[Loyalty] E --> A[Audit] W --> S[Shipment state]
Order aggregate নিজের সত্য থেকে fact তৈরি করে। Consumer নিজেদের state বদলায়, publisher-এর private schema query করার প্রয়োজন কমে। Warehouse failure loyalty থামায় না। Audit consumer একই event identity ধরে রাখে। Shipping complete event আবার নতুন fact হতে পারে।
PHP ও Laravel-এ প্রয়োগ
Laravel event envelope তৈরির fragment। এটি publish transport নয়; outbox-এ atomic insert করে relay করা উত্তম। Amount minor units, currency explicit এবং occurred_at UTC রাখুন। Full Eloquent model serialize করলে অনিচ্ছাকৃত private field leak হতে পারে।
use Illuminate\Support\Str;
$event = [
'id' => (string) Str::uuid(),
'type' => 'commerce.order.paid',
'version' => 1,
'occurred_at' => now()->utc()->toIso8601String(),
'subject' => 'orders/'.$order->id,
'data' => [
'order_id' => $order->id,
'tenant_id' => $order->tenant_id,
'amount_minor' => $order->total_minor,
'currency' => 'BDT',
],
];
$payload = json_encode($event, JSON_THROW_ON_ERROR);এটি PHP 8.3+ ও Laravel 12-এর শিক্ষামূলক fragment; সম্পূর্ণ application নয়। সংশ্লিষ্ট route-এ authentication ও resource authorization প্রয়োগ করতে হবে। নিচের অনুশীলনে failure ইচ্ছা করে তৈরি করুন; শুধু সফল response দেখে correctness ধরে নেবেন না।
সুবিধা, সীমাবদ্ধতা ও trade-off
নতুন consumer publisher বদল ছাড়া যোগ করা যায়। কিন্তু end-to-end flow বুঝতে tracing ও contract registry দরকার। Immediate consistency চাইলে user-facing response শুধু event publish success-এর উপর নির্ভর করতে পারে না। Simple local operation-এ direct call পরিষ্কার হতে পারে।
সাধারণ ভুল
- Event নামে send_email command লুকানো।
- Consumer-কে producer database join করতে বাধ্য করা।
- Breaking field change একই version-এ নীরবে চালু করা।
আরও একটু গভীরে
Schema compatibility syntax-এর চেয়ে বেশি: amount-এর unit বদলালে JSON number আগের মতো থাকলেও অর্থ ভাঙে। Additive optional field সাধারণত সহজ; required field rename migration চায়। Consumer-driven contract test known payload দিয়ে expected behavior যাচাই করতে পারে।
এক consumer পুরোনো version পড়ছে ধরে envelope-এ optional field যোগ করুন। তা যেন ভাঙে না দেখুন। তারপর currency unit বদলে কী silent error হয় লিখুন। Version migration-এ dual publishing করলে duplicate identity এবং double counting-এর policy লাগবে।
মনে রাখুন
- Event fact, command অনুরোধ।
- Public contract private schema নয়।
- Asynchronous coupling দৃশ্যমান করুন।
নিজে চেষ্টা করুন
OrderPaid contract-এ প্রয়োজনীয় ন্যূনতম field লিখুন। কোন personal data বাদ দেবেন এবং consumer missing field পেলে কী করবে জানান।
নিজের বোঝা যাচাই করুন
1. Event কী?
একটি ঘটেছে এমন business fact; consumer-এর কাজ করার নির্দেশের সমার্থক নয়।
2. Model পাঠালে সমস্যা?
Private fields, framework serialization এবং schema coupling বাড়ে; explicit payload বেশি নিয়ন্ত্রিত।
3. Publish হলেই workflow শেষ?
না। Consumer পরে চলে এবং ব্যর্থ হতে পারে; progress আলাদা দেখা দরকার।
4. Version কখন?
Meaning বা compatibility বদলালে migration contract দরকার; শুধু field count দেখে নয়।
5. সবকিছু event?
দরকার নেই। Simple synchronous invariant direct local call-এ পরিষ্কার হতে পারে।
আরও পড়ুন
এই পাঠের কুইজ
এই পাঠ থেকে ২টি প্রশ্ন। উত্তর দিয়ে ব্যাখ্যা দেখুন; অধ্যায়ের কুইজ আলাদাভাবে দিতে হবে।
এই পাঠের ২টি প্রশ্নের উত্তর দিন। পাস করতে দুটিই সঠিক হতে হবে।
জমা দেওয়ার পর সঠিক উত্তর ও ব্যাখ্যা দেখবেন। ফলাফল এই ব্রাউজারে সংরক্ষিত থাকবে। অনুশীলনের ফল অধ্যায় সম্পন্নের হিসাবে যোগ হয় না।
অগ্রগতি এই ব্রাউজারে সংরক্ষিত থাকে। শুধু পাঠ খুললে সম্পন্ন হয় না।