CQRS Pattern

CQRS Pattern

Concept & Implementation

Concept

CQRS - Command & Query Responsibility Segregation

Database မှာ INSERT, UPDATE, DELETE စတဲ့ operation တွေကို Command တွေလို့ ခေါ်ပြီး၊ SELECT operation ကိုတော့ Query operation လို့ခေါ်ပါတယ်။ တစ်နည်းအားဖြင့် Database ထဲ data တွေ သိမ်း၊ပြင်၊ဖျက် တာကို write operation လို့သတ်မှတ်နိုင်ပြီး၊ Database ထဲက data တွေ ထုတ်တာကို read operation လို့ သတ်မှတ်နိုင်ပါတယ်။

Application တွေတည်ဆောက်ရာမှာ အချက်အလက်တွေ သိမ်း၊ပြင်၊ဖျက် (write) ဖို့နဲ့ ပြန်လည်ထုတ်ယူ ဖော်ပြ (read) ဖို့ Database တွေက မပါမဖြစ်သုံးရပါတယ်။

CQRS Pattern ဟာ Application နဲ့ Database ကြားက Command နဲ့ Query လုပ်ဆောင်ချက်တွေကို ခွဲခြားပြီး လုပ်ဖို့ လမ်းညွှန်ထားတဲ့ ပုံစံဖြစ်ပါတယ်။ အဲ့လိုခွဲလုပ်ဖို့ Database Server ကို Write အတွက် တစ်လုံး၊ Read အတွက် တစ်လုံး ထားပြီး၊ Database ၂ လုံးကြားက data တွေ တူညီနေအောင် ညှိရပါတယ်။

MariaDB, MySQL လို Database Server တွေမှာ ဒီလို Database တွေခွဲပြီး Sync တဲ့ လုပ်ဆောင်ချက်တွေကို Configure လုပ်လို့ရပါတယ်။ AWS ရဲ့ RDS လို Cloud Database Managed Service တွေမှာဆို Configure လုပ်ဖို့ ပိုလွယ်ပါတယ်။

အသုံးပြုသူ အမြောက်အများရှိလာတဲ့ အခါ Application တွေ မနှေးကွေးသွားဖို့နဲ့ အချက်အလက်တွေ လိုသလို သိမ်းဆည်း ထုတ်ယူနိုင်ဖို့ CQRS pattern ကို အသုံးပြုနိုင်ပါတယ်။

CQRS Pattern Deployment

Application ဘက်ကနေ Database ကို ဆက်သွယ်လုပ်ဆောင်ရာမှာ read နဲ့ write အတွက် connection တွေ ခွဲပြီး ဝန်မျှအောင် လုပ်ဆောင်ထားတဲ့ ပုံစံဖြစ်ပါတယ်။

Application တွေမှာ အများအားဖြင့် read operation တွေဟာ write operation ထက် ပိုမိုလုပ်ဆောင်ရပါတယ်။ ကိုယ့် Application မှာ CQRS Pattern ကို သုံးထားမယ်ဆိုရင် ဒီလိုအခြေအနေမျိုးမှာ Write Database ကို ပုံမှန်ထားပြီး၊ Read Database ကို Resource ပိုတိုး Size ပိုကြီးထားလို့ရပါတယ်။


Implementation

CQRS Pattern ဟာ Application Development ဘက် Coding ပိုင်း နဲ့ Server ပိုင်း Deployment ပေါင်းစပ်ပြီး အကောင်အထည်ဖော်ရတဲ့ ပုံစံ တစ်ခုဖြစ်ပါတယ်။

ဒီ Post မှာတော့ Coding ဘက်က Implement လုပ်တဲ့ အပိုင်းကို အဓိကထား မှာဖြစ်ပါတယ်။ ဥပမာ အနေနဲ့ PHP Laravel Framework နဲ့ MySQL/MariaDB Database သုံးပြီး အကောင်အထည်ဖော် လို့ရလဲဆိုတာ မျှဝေပေးသွားမှာပါ။

Step 1 - Database ကို Read နဲ့ Write connection ခွဲဖို့ .env config မှာ အောက်ပါအတိုင်း ပြောင်းပါမယ်။

WRITE_DB_HOST=172.16.1.1
WRITE_DB_PORT=3306
WRITE_DB_DATABASE=dbname
WRITE_DB_USERNAME=username
WRITE_DB_PASSWORD=password

READ_DB_HOST=172.16.1.2
READ_DB_PORT=3306
READ_DB_DATABASE=dbname
READ_DB_USERNAME=username
READ_DB_PASSWORD=password

Step 2 - config/database.php file ရဲ့ 'mysql' မှာ အောက်ပါအတိုင်း ပြောင်းပါမယ်။

<?php
use Illuminate\Support\Str;

return [
    'default' => env('DB_CONNECTION', 'mysql'),
    ...
    'connections' => [
        ...
        'mysql' => [
            'driver' => 'mysql',
            'read' => [
                'host' => [env('READ_DB_HOST', env('WRITE_DB_HOST'))],
                'database' => env('READ_DB_DATABASE', env('WRITE_DB_DATABASE')),
                'port' => env('READ_DB_PORT', env('WRITE_DB_PORT', 3306)),
                'username' => env('READ_DB_USERNAME', env('WRITE_DB_USERNAME')),
                'password' => env('READ_DB_PASSWORD', env('WRITE_DB_PASSWORD')),
            ],
            'write' => [
                'host' => [env('WRITE_DB_HOST')],
                'database' => env('WRITE_DB_DATABASE'),
                'port' => env('WRITE_DB_PORT', '3306'),
                'username' => env('WRITE_DB_USERNAME'),
                'password' => env('WRITE_DB_PASSWORD'),
            ],
            'sticky' => true,
            ...
        ],
...

ဒီအတိုင်း configure လုပ်လိုက်မယ်ဆိုရင် Laravel က SELECT query တွေဆို read မှာ ထည့်ထားတဲ့ Database connection ကို အလိုအလျောက်သုံးသွားပြီး INSERT, UPDATE, DELETE command တွေဆို write မှာထည့်ထားတဲ့ Database connection ကို အလိုအလျောက်သုံး သွားမှာဖြစ်ပါတယ်။

တစ်ခါတစ်လေ Write DB မှာ data တွေထည့်ပြီးပေမယ့် Read ဘက်ကို sync တာမပြီးသေးရင် (Database Sever Master နဲ့ Slave sync တာ Network ပေါ်က သွားတာ ဖြစ်တဲ့အတွက် latency ရှိပါတယ်) Read connection သုံးပြီး SELECT query လုပ်တာမရဖြစ်တတ်ပါတယ်။

အဲ့ဒီ ပြဿနာအတွက် 'sticky' => true ဆိုတဲ့ option ကို Laravel က provide လုပ်ပေးထားပါတယ်။ sticky မှာ true ထားမယ်ဆိုလျှင် Write operation ပြီးလို့ Read Operation လုပ်မယ်ဆိုရင်လည်း Laravel က Write connection ကိုပဲ ဆက်သုံးပေးပါတယ်။


Manual Handling in Code

တစ်ခါတစ်လေ development မှာ အခြေအနေပေါ် မူတည်ပြီး Data Model / Table တစ်ခု ချင်းစီအလိုက် မတူညီတဲ့ connection ကို ခေါ်သုံးပြီး Query/Command လုပ်ဖို့လုပ်အပ်လာတာမျိုးတွေ ရှိပါတယ်။ လိုအပ်လာတဲ့အခါ အောက်ပါအတိုင်း အသုံးပြုနိုင်ပါတယ်။

Eloquent ORM

/* Read */
$category = Category::on('mysql::read')->get();
$category = Category::on('mysql::write')->get();

/* Write */
$category = new Category::on('mysql::write’);
$category->name = ‘Sports’;
$category->save();

DB Facade

/* Read */
$category = DB::connection('mysql::read')->table('categories')->get();
$category = DB::connection('mysql::write')->table('categories')->get();

/* Write */
DB::connection(‘mysql::write’)->table(‘categories’)->insertGetId([
    ‘name’ => ‘Sports’
]);

ဒီလောက် ဆို CQRS pattern ရဲ့ Concept ပိုင်းကို ရိပ်မိပြီး နဲ့ Implementation စမ်းလုပ်ကြည့်လို့ရပါပြီ။

Aung Kyaw Minn | Head of Technology
Onenex