ইভেন্ট সোর্সিং আপনাকে আপনার ডেটা ওভাররাইট করা বন্ধ করতে বলে। একটি প্রথাগত CRUD অ্যাপ্লিকেশনে, একজন ব্যবহারকারীর শিপিং অ্যাড্রেস আপডেট করার অর্থ হলো সেই রো (row) খুঁজে বের করা, মান পরিবর্তন করা এবং পূর্ববর্তী অবস্থাটি মুছে ফেলা। ইভেন্ট সোর্সিং একটি ভিন্ন পথ অনুসরণ করে। এটি প্রতিটি পরিবর্তনকে একটি অপরিবর্তনীয় তথ্য (immutable fact) হিসেবে সংরক্ষণ করে: যেমন একজন ব্যবহারকারী একটি অ্যাকাউন্ট তৈরি করেছেন, তাদের ঠিকানা আপডেট করেছেন, বা তাদের ইমেল ভেরিফাই করেছেন। সিস্টেমের বর্তমান অবস্থা সরাসরি সংরক্ষণ করা হয় না। এটি এই ইভেন্টগুলোকে ক্রমানুসারে পুনরায় প্লে (replay) করার মাধ্যমে গণনা করা হয়।

এই প্যাটার্নটি বাস্তব সমস্যা সমাধান করে। অডিট ট্রেইল (Audit trails) এখানে বিনামূল্যে উপজাত (byproduct) হিসেবে পাওয়া যায়। আপনি যেকোনো অতীত মুহূর্তের একটি অর্ডারের অবস্থা পুনর্গঠন করতে পারেন। ঠিক কী ঘটেছিল তা পুনরায় প্লে করার মাধ্যমে আপনি ডিবাগ করতে পারেন। এর বিনিময়ে আপনাকে জটিলতা মেনে নিতে হবে। এখন আপনাকে সাধারণ রো-এর পরিবর্তে তথ্যের স্ট্রিম (streams of facts), রিড মডেল (read models) এবং ইভেনচুয়াল কনসিস্টেন্সি (eventual consistency) পরিচালনা করতে হবে।

PostgreSQL আপনার ইভেন্ট স্টোর হিসেবে কাজ করতে পারে। বেশিরভাগ টিম ইতিমধ্যে এটি ব্যবহার করছে। এটি ACID ট্রানজ্যাকশন, নমনীয় পেলোডের জন্য JSONB এবং প্রমাণিত ব্যাকআপ টুল প্রদান করে। প্রথম দিনেই আপনার Kafka, Cassandra বা কোনো বিশেষায়িত ইভেন্ট-স্টোর ডেটাবেস প্রবর্তন করার প্রয়োজন নেই। একটি স্ট্যান্ডার্ড Postgres ইনস্ট্যান্স আপনার ইনফ্রাস্ট্রাকচার ফুটপ্রিন্ট না বাড়িয়েই ইভেন্ট সোর্সিংয়ের জন্য প্রয়োজনীয় ট্রানজ্যাকশনাল গ্যারান্টি এবং অডিট ট্রেইল প্রদান করে।

একটি Postgres ইভেন্ট স্টোরের গঠন

স্কিমাটি প্রায় অবিশ্বাস্য রকমের সহজ হতে পারে। নূন্যতমভাবে, আপনার এমন একটি টেবিল প্রয়োজন যা ইভেন্টগুলো অ্যাপেন্ড (append) করবে এবং কখনোই সরাসরি আপডেট করবে না। একটি ব্যবহারিক ডিজাইন দেখতে এমন হতে পারে:

  • id হিসেবে bigserial বা UUID, যা গ্লোবাল অর্ডারিং হিসেবে কাজ করবে।
  • stream_id সম্পর্কিত ইভেন্টগুলোকে গ্রুপ করার জন্য, যেমন একজন ব্যবহারকারী বা অর্ডারের সমস্ত পরিবর্তন।
  • event_type হিসেবে সাধারণ টেক্সট: UserEmailChanged, PaymentReceived, InventoryAdjusted
  • payload হিসেবে JSONB, যা সেই ঘটনার নির্দিষ্ট ডেটা ধারণ করবে।
  • occurred_at টাইমজোন প্রিসিশনসহ।
  • প্রতিটি স্ট্রিমের জন্য version, যা অপ্টিমিস্টিক কনকারেন্সি (optimistic concurrency) নিশ্চিত করবে।

আপনি অ্যাপ্লিকেশন কোড বা ডেটাবেস কনস্ট্রেইন্টের মাধ্যমে অপরিবর্তনীয়তার (immutability) নিয়মটি প্রয়োগ করতে পারেন। (stream_id, version) এর ওপর একটি ইউনিক ইনডেক্স দুটি রাইটারকে একই সিকোয়েন্স নম্বর অ্যাপেন্ড করা থেকে বিরত রাখে। যখন একটি কমান্ড আসে, আপনি সেই স্ট্রিমের বর্তমান ভার্সনটি পড়েন, সেটি বৃদ্ধি করেন এবং একটি ট্রানজ্যাকশনের মধ্যে নতুন ইভেন্টটি ইনসার্ট করেন। যদি অন্য কোনো প্রসেস আপনার আগে এটি করে ফেলে, তবে ইউনিক কনস্ট্রেইন্টটি ব্যর্থ হবে এবং আপনি কমান্ডটি পুনরায় চেষ্টা করবেন বা প্রত্যাখ্যান করবেন।

একটি বাস্তব উদাহরণ বিবেচনা করুন। আপনি একটি ইনভেন্টরি সিস্টেম পরিচালনা করছেন। একটি quantity কলামসহ একক inventory রো-এর পরিবর্তে, আপনি একটি inventory_events টেবিলে ইভেন্টগুলো অ্যাপেন্ড করেন। ItemReceived দশটি ইউনিট যোগ করে। ItemReserved দুটি সরিয়ে নেয়। ItemShipped তিনটি সরিয়ে নেয়। SKU-42 এর বর্তমান স্টক জানতে, আপনি সংশ্লিষ্ট ইভেন্ট পেলোডগুলোর যোগফল বের করবেন। তিন দিন আগে স্টক কত ছিল তা জানতে, আপনি শুধুমাত্র সেই টাইমস্ট্যাম্প পর্যন্ত ইভেন্টগুলোর যোগফল বের করবেন। যদি গত মঙ্গলবার আপনার শিপিং লজিকের কোনো বাগের কারণে ভুল হয়ে থাকে, তবে আপনি সঠিক কোড দিয়ে ইভেন্টগুলো পুনরায় প্লে করে প্রকৃত অবস্থাটি পেতে পারেন। একটি সাধারণ UPDATE স্টেটমেন্ট দিয়ে আপনি এটি করতে পারবেন না।

নীতিমালা যা আপনাকে ঝামেলা থেকে দূরে রাখবে

Postgres-এর ওপর ভিত্তি করে তৈরি করার মানে এই নয় যে শৃঙ্খলার প্রয়োজন নেই। নিচের নীতিমালাগুলো ইভেন্ট-সোর্সড সিস্টেমের ক্ষেত্রে সরাসরি প্রযোজ্য।

সহজ রাখুন। জটিলতা নির্ভরযোগ্যতা নষ্ট করে। একটি কার্যকর ফ্লো শিপ করার আগে একটি জেনেরিক ইভেন্ট ফ্রেমওয়ার্ক তৈরির প্রলোভন থেকে দূরে থাকুন। একটি একক টেবিল, ইভেন্ট অ্যাপেন্ড করার জন্য একটি রিপোজিটরি ফাংশন এবং রিড মডেল তৈরির জন্য একটি প্রজেকশন ওয়ার্কার (projection worker) এর মান প্রমাণ করার জন্য যথেষ্ট। যখন কোনো বাস্তব সমস্যা দেখা দেবে, তখনই কেবল টুল যোগ করুন।

ছোট থেকে শুরু করুন। আপনার পুরো মনোলিথ (monolith) পুনরায় লিখবেন না। এমন একটি বাউন্ডেড কনটেক্সট (bounded context) বেছে নিন যেখানে অডিট ট্রেইল ওভারহেডের তুলনায় বেশি সুবিধা দেয়। একটি বিলিং লেজার, একটি ওয়ার্কফ্লো ইঞ্জিন, বা একটি ইনভেন্টরি রিজার্ভেশন সিস্টেম ভালো প্রার্থী হতে পারে। সেই একটি পাইপলাইন এন্ড-টু-এন্ড তৈরি করুন। এটি প্রোডাকশনে চলতে দিন। তারপর সিদ্ধান্ত নিন যে এটি আরও সম্প্রসারণ করা হবে কি না।

প্রথমে সাফল্য সংজ্ঞায়িত করুন। ইভেন্ট সোর্সিং কোনো ডিফল্ট আর্কিটেকচার নয়; এটি নির্দিষ্ট প্রয়োজনের সমাধান। যদি আপনার প্রয়োজন কেবল সর্বশেষ অবস্থা ট্র্যাক করা হয়, তবে CRUD দ্রুত এবং সাশ্রয়ী। যদি আপনার টেম্পোরাল কুয়েরি (temporal queries), কঠোর অডিটেবিলিটি বা প্রয়োজন অনুযায়ী রিড মডেল পুনর্গঠন করার ক্ষমতা প্রয়োজন হয়, তবে ইভেন্ট সোর্সিং যুক্তিযুক্ত। প্রতিশ্রুতিবদ্ধ হওয়ার আগে জানুন আপনি কোন সমস্যার সমাধান করছেন।

অপ্টিমাইজ করার আগে পরিমাপ করুন। সাধারণ হার্ডওয়্যারে আধুনিক PostgreSQL একটি সাধারণ অ্যাপেন্ড-অনলি (append-only) টেবিলের মাধ্যমে প্রতি সেকেন্ডে হাজার হাজার ইভেন্ট গ্রহণ করতে পারে। আপনার মনিটরিং যতক্ষণ না প্রমাণ করছে যে আপনি সহজ সমাধানগুলো শেষ করে ফেলেছেন, ততক্ষণ আপনার ইভেন্ট স্টোর শার্ড (shard) করবেন না বা জটিল পার্টিশনিং স্কিম প্রবর্তন করবেন না। যে ফিল্ডগুলো আপনি কুয়েরি করেন সেগুলোতে ইনডেক্স করুন। অ্যাপেন্ড-অনলি ওয়ার্কলোডের জন্য autovacuum টিউন করুন। তারপর আবার পরিমাপ করুন।

সবকিছু পরীক্ষা করুন। আপনার ইভেন্ট হ্যান্ডলারগুলোর ইউনিট টেস্ট করুন। অ্যাপেন্ড পাথ (append path) ইন্টিগ্রেশন টেস্ট করুন। সবচেয়ে গুরুত্বপূর্ণ হলো, ফেইলর সিনারিওগুলো (failure scenarios) পরীক্ষা করুন। যখন দুটি নোড একই সাথে একটি স্ট্রিমে অ্যাপেন্ড করে তখন কী ঘটে? যখন একটি প্রজেকশন ওয়ার্কার ব্যাচ চলাকালীন ক্র্যাশ করে তখন কী ঘটে? আপনার অপটিমিস্টিক কনকারেন্সি (optimistic concurrency) এবং অ্যাট-লিস্ট-ওয়ান্স ডেলিভারি (at-least-once delivery) গ্যারান্টিগুলো যাচাই করার জন্য টেস্ট লিখুন।

প্রোডাকশনে মনিটর করুন। ইভেন্ট টেবিলটি বড় হতে থাকবে। একটি নরমালাইজড স্কিমার বিপরীতে যেখানে আপডেটগুলো রো (row) সংখ্যা স্থির রাখে, ইভেন্ট সোর্সিং ইচ্ছাকৃতভাবেই অ্যাডিশনাল বা যোগধর্মী। টেবিলের আকার, ডিস্ক I/O এবং আপনার রাইট মডেল ও রিড মডেল প্রজেকশনের মধ্যকার ল্যাগ (lag) ট্র্যাক করুন। ব্যবহারকারীরা পুরনো ডেটা দেখার আগেই প্রজেকশন ল্যাগের জন্য অ্যালার্ট সেট করুন।

ম্যানুয়াল কাজগুলো অটোমেট করুন। ম্যানুয়াল স্কিমা পরিবর্তন, ম্যানুয়াল প্রজেকশন রিবিল্ড এবং ম্যানুয়াল ইভেন্ট রিপ্লে হলো ঘড়ির কাঁটার মতো টিকটিক করা টাইম বোমা। আপনার মাইগ্রেশন স্ট্র্যাটেজি স্ক্রিপ্ট করুন। আপনি যদি একটি ইভেন্ট স্কিমা পরিবর্তন করেন, তবে আপকাস্টিং (upcasting) বা ট্রান্সফরমেশন অটোমেট করুন যাতে মাঝরাতে মানুষের হস্তক্ষেপ ছাড়াই পুরনো ইভেন্টগুলো নতুন লজিকের মাধ্যমে রিপ্লে করা যায়।

আপনার সিদ্ধান্তগুলো ডকুমেন্ট করুন। কেন নির্দিষ্ট স্ট্রীমগুলো বিদ্যমান, প্রতিটি ইভেন্ট টাইপের অর্থ কী এবং কখন টিমের ইভেন্টের পরিবর্তে CRUD বেছে নেওয়া উচিত তা লিখে রাখুন। ইভেন্ট সোর্সিং কগনিটিভ লোড (cognitive load) বা মানসিক চাপ তৈরি করে। ভালো ডকুমেন্টেশন একজন নতুন ইঞ্জিনিয়ারকে ভুল অনুমান করা এবং একটি গুরুত্বপূর্ণ স্ট্রিমে ভুল ফরম্যাটের ইভেন্ট অ্যাপেন্ড করা থেকে বিরত রাখে।

মাসব্যাপী সময় নষ্ট করতে পারে এমন ফাঁদসমূহ

ইভেন্ট সোর্সিং ডায়াগ্রামে দেখতে খুব মার্জিত মনে হলেও প্রোডাকশনে এটি যন্ত্রণাদায়ক হতে পারে। এই ফাঁদগুলোর দিকে নজর রাখুন।

জটিলতাকে অবমূল্যায়ন করা। স্টেট রিবিল্ড করার জন্য ইভেন্ট রিপ্লে করা ধারণাগতভাবে সহজ। কিন্তু আইডেমপোটেন্সি (idempotency) ম্যানেজ করা, পারফরম্যান্সের জন্য স্ন্যাপশট নেওয়া এবং অ্যাগ্রিগেটের মধ্যে কম্পেনসেটিং ট্রানজ্যাকশন পরিচালনা করা সহজ নয়। আপনার সিস্টেমকে ছোট ছোট অংশে ভাগ করুন। একবারে একটি স্ট্রীম সমাধান করুন।

ওভার-ইঞ্জিনিয়ারিং। শুধুমাত্র কল্পনা করে যে একদিন আপনার ইভেন্ট ভলিউম অনেক বেড়ে যাবে, তাই একটি মাল্টি-নোড Kafka ক্লাস্টার তৈরি করবেন না। Postgres আপনাকে আশ্চর্যজনকভাবে অনেক দূর পর্যন্ত নিয়ে যেতে পারে। নতুন ইনফ্রাস্ট্রাকচার তখনই যুক্ত করুন যখন আপনার কাছে একটি পরিমাপযোগ্য বটলনেক (bottleneck) থাকবে যা আপনার বর্তমান সেটআপ দিয়ে সমাধান করা সম্ভব নয়।

টেকনিক্যাল ডেট (technical debt) উপেক্ষা করা। পুরনো ইভেন্ট স্কিমা চিরকাল থেকে যায়। আপনি যদি আপনার OrderCreated পেলোড পরিবর্তন করেন, তবুও আপনার কাছে পুরনো ফরম্যাটে দশ মিলিয়ন ঐতিহাসিক ইভেন্ট থেকে যাবে। এই ডেট ট্র্যাক করুন। ব্যাকওয়ার্ড-কম্প্যাটিবল রিডার বা মাইগ্রেশন স্ক্রিপ্ট পরিকল্পনা করুন। লেগাসি ইভেন্টের বোঝা যেন প্রতিটি নতুন ফিচারকে ধীর করে না দেয় সেদিকে খেয়াল রাখুন।

এমন টুলস বেছে নেওয়া যা টিম চালাতে পারে না। সেরা আর্কিটেকচারও ব্যর্থ হয় যদি এটি কেবল একজন মানুষ বোঝেন। আপনার টিম যদি Postgres এবং SQL জানে, তবে সেখান থেকেই শুরু করুন। আপনি যদি একটি বিশেষায়িত ইভেন্ট স্টোর (event store) চালু করেন, তবে নিশ্চিত করুন যে রাত দুটোর সময় সেটি ডিবাগ করার মতো অপারেশনাল দক্ষতা আপনার কাছে আছে।

একটি ব্যবহারিক শুরুর বিন্দু

যদি এই পদ্ধতিটি আপনার সমস্যার জন্য উপযুক্ত হয়, তবে পাঁচ কোয়ার্টার পর রিরাইট করার জন্য অপেক্ষা করবেন না। এই সপ্তাহ থেকেই শুরু করুন।

আপনার বর্তমান সিস্টেমগুলো অডিট করুন। এমন একটি জায়গা খুঁজুন যেখানে একটি অডিট ট্রেইল (audit trail) প্রকৃত সমস্যা সমাধান করতে পারে। হতে পারে এটি একটি অর্ডার স্টেট মেশিন যা বর্তমানে একটি মাত্র status কলাম বজায় রাখে। অথবা হতে পারে এটি একটি আর্থিক লেজার যেখানে ব্যালেন্স সংশোধনের জন্য ম্যানুয়াল ডেটাবেস প্যাচ প্রয়োজন হয়। এমন একটি গ্যাপ বেছে নিন যেখানে স্টেট ওভাররাইট করা আপনাকে ক্ষতিগ্রস্ত করেছে।

তারপর আজ আপনি যে ছোট উন্নতিটি করতে পারেন তা বেছে নিন। একটি ইভেন্ট টেবিল তৈরি করুন। একটি স্ট্রীম মডেল করুন। একটি প্রজেকশন লিখুন যা সেই ইভেন্টগুলো থেকে একটি রিড মডেল তৈরি করে। এটি একটি ফিচার ফ্ল্যাগ (feature flag)-এর পেছনে ডেপ্লয় করুন। এটি কীভাবে রিয়েল ট্রাফিক হ্যান্ডেল করে তা দেখুন।

PostgreSQL দিয়ে ইভেন্ট সোর্সিং কোনো জাদু নয়। এটি সেই সব টিমের জন্য একটি ব্যবহারিক টুল যাদের শুধু জিনিসগুলো কোথায় আছে তা নয়, বরং সেগুলো সেখানে কীভাবে পৌঁছাল তাও জানা প্রয়োজন। ধীরে ধীরে তৈরি করুন, সততার সাথে পরিমাপ করুন এবং আপনার প্রকৃত প্রয়োজনীয়তাগুলোকে আর্কিটেকচার গাইড করতে দিন।

Source: https://dev.to/therizwansaleem/event-sourcing-with-postgresql-using-the-database-as-an-event-store-2kd4