DEV Community

Ruhul Amin Sujon
Ruhul Amin Sujon

Posted on

ব্যাকএন্ড ইঞ্জিনিয়ারের জন্য সিস্টেম ডিজাইন শেখা

Q:1- System Design (scalability, microservices)
Answer:
🏗️ System Design কী?
System Design হলো এমন একটি প্রক্রিয়া যেখানে আপনি একটি বড় সফটওয়্যার সিস্টেমের আর্কিটেকচার, কম্পোনেন্ট, এবং ডেটা ফ্লো পরিকল্পনা করেন — যাতে সিস্টেমটি লক্ষ লক্ষ ইউজার হ্যান্ডেল করতে পারে, ক্র্যাশ না করে, এবং দ্রুত রেসপন্স দিতে পারে।
সহজ কথায়: আপনার অ্যাপ যখন ১০০ ইউজার থেকে ১০ মিলিয়ন ইউজারে যায় — তখন কী করবেন, সেটাই System Design।


📐 ১. Scalability (স্কেলেবিলিটি)
Scalability মানে হলো আপনার সিস্টেম বেশি লোড আসলে সেটা সামলাতে পারে কিনা।
দুই ধরনের Scaling আছে:
Vertical Scaling (Scale Up)
মানে আপনার সার্ভারের শক্তি বাড়ানো — বেশি RAM, বেশি CPU। কিন্তু এর একটা সীমা আছে এবং এটা ব্যয়বহুল।
Horizontal Scaling (Scale Out)
মানে একটা সার্ভারের বদলে অনেকগুলো সার্ভার ব্যবহার করা। Laravel-এ এটাই বেশি প্র্যাক্টিক্যাল।

[User] → [Load Balancer] → [Server 1]
                        → [Server 2]  
                        → [Server 3]
Enter fullscreen mode Exit fullscreen mode

Laravel-এ Horizontal Scaling এর জন্য যা করতে হবে:
১. Session Shared করুন — একাধিক সার্ভারে session share করতে Redis ব্যবহার করুন:

php
// .env
SESSION_DRIVER=redis
CACHE_DRIVER=redis

// config/session.php
'driver' => env('SESSION_DRIVER', 'redis'),
Enter fullscreen mode Exit fullscreen mode

২. File Storage Centralize করুন — সব সার্ভার যেন একই জায়গা থেকে ফাইল পায়:

php
// config/filesystems.php
'default' => env('FILESYSTEM_DISK', 's3'),

'disks' => [
    's3' => [
        'driver' => 's3',
        'key'    => env('AWS_ACCESS_KEY_ID'),
        'secret' => env('AWS_SECRET_ACCESS_KEY'),
        'region' => env('AWS_DEFAULT_REGION'),
        'bucket' => env('AWS_BUCKET'),
    ],
],
Enter fullscreen mode Exit fullscreen mode

৩. Queue Centralize করুন — Jobs যেন যেকোনো সার্ভার প্রসেস করতে পারে:

php
// .env
QUEUE_CONNECTION=redis

// Job dispatch করুন
ProcessOrder::dispatch($order)->onQueue('orders');

// যেকোনো সার্ভারে worker চালান
php artisan queue:work redis --queue=orders
Enter fullscreen mode Exit fullscreen mode

🔄 ২. Scalability Patterns
ক) Database Read/Write Separation
বেশিরভাগ অ্যাপে ৮০% Read, ২০% Write অপারেশন হয়। তাই Read আলাদা সার্ভারে পাঠানো স্মার্ট।

php
// config/database.php
'mysql' => [
    'read' => [
        'host' => [
            env('DB_READ_HOST_1', '192.168.1.2'),
            env('DB_READ_HOST_2', '192.168.1.3'),
        ],
    ],
    'write' => [
        'host' => [env('DB_WRITE_HOST', '192.168.1.1')],
    ],
    'driver'   => 'mysql',
    'database' => env('DB_DATABASE', 'myapp'),
    'username' => env('DB_USERNAME', 'root'),
    'password' => env('DB_PASSWORD', ''),
],
Enter fullscreen mode Exit fullscreen mode

এখন Laravel নিজেই SELECT কে Read সার্ভারে এবং INSERT/UPDATE/DELETE কে Write সার্ভারে পাঠাবে।
খ) Caching Layer

php
// ডাটাবেজে বারবার না গিয়ে Cache থেকে নিন
public function getPopularProducts()
{
    return Cache::remember('popular_products', 3600, function () {
        return Product::where('is_popular', true)
                      ->with('category')
                      ->take(20)
                      ->get();
    });
}
Enter fullscreen mode Exit fullscreen mode

গ) Queue দিয়ে Heavy Tasks আলাদা করুন

php
// ❌ ভুল — Request এর মধ্যেই Heavy কাজ করা
public function register(Request $request)
{
    $user = User::create($request->all());
    Mail::send(new WelcomeMail($user)); // এটা slow!
    Notification::send($user, new SmsNotification()); // এটাও slow!
    return response()->json(['success' => true]);
}

// ✅ সঠিক — Queue এ পাঠিয়ে দিন
public function register(Request $request)
{
    $user = User::create($request->all());
    SendWelcomeEmail::dispatch($user);   // Background এ যাবে
    SendWelcomeSms::dispatch($user);     // Background এ যাবে
    return response()->json(['success' => true]); // তৎক্ষণাৎ রেসপন্স
}
Enter fullscreen mode Exit fullscreen mode

🧩 ৩. Microservices Architecture

Microservices মানে একটা বড় অ্যাপকে ছোট ছোট স্বাধীন সার্ভিসে ভাগ করা — যেগুলো আলাদাভাবে deploy ও scale করা যায়।

Monolith vs Microservices:

Monolith (একটাই বড় অ্যাপ):

[User, Product, Order, Payment, Notification] → একটা Laravel App
Enter fullscreen mode Exit fullscreen mode

Microservices (আলাদা আলাদা সার্ভিস):

[User Service]        → api.myapp.com/users
[Product Service]     → api.myapp.com/products  
[Order Service]       → api.myapp.com/orders
[Payment Service]     → api.myapp.com/payments
[Notification Service]→ api.myapp.com/notifications
Enter fullscreen mode Exit fullscreen mode

Microservices কখন দরকার?
আপনার অ্যাপ যদি এরকম হয়:

  • টিম বড় এবং আলাদা আলাদা টিম আলাদা ফিচারে কাজ করে
  • কিছু সার্ভিস বেশি scale দরকার (যেমন Payment আলাদা scale)
  • Different technology দরকার বিভিন্ন অংশে

Laravel-এ Microservices — Service Communication:
HTTP দিয়ে সার্ভিস কল করুন (Guzzle/Http Facade):

php
// Order Service থেকে Product Service কে কল করা
class OrderService
{
    public function createOrder(array $data)
    {
        // Product Service এ চেক করুন stock আছে কিনা
        $response = Http::withToken(config('services.product.token'))
                        ->get(config('services.product.url') . '/api/products/' . $data['product_id']);

        if (!$response->successful()) {
            throw new ServiceUnavailableException('Product service unreachable');
        }

        $product = $response->json();

        if ($product['stock'] < $data['quantity']) {
            throw new InsufficientStockException('Not enough stock');
        }

        // Order তৈরি করুন
        return Order::create([
            'user_id'    => $data['user_id'],
            'product_id' => $data['product_id'],
            'quantity'   => $data['quantity'],
            'total'      => $product['price'] * $data['quantity'],
        ]);
    }
}
Enter fullscreen mode Exit fullscreen mode

Event-Driven Communication (RabbitMQ/Redis):

php
// Order Service — Event Publish করছে
class OrderController extends Controller
{
    public function store(Request $request)
    {
        $order = Order::create($request->validated());

        // Notification Service কে জানাও — সরাসরি কল না করে Event দিয়ে
        event(new OrderCreated($order));

        // অথবা Redis Pub/Sub দিয়ে
        Redis::publish('order.created', json_encode([
            'order_id' => $order->id,
            'user_id'  => $order->user_id,
            'total'    => $order->total,
        ]));

        return response()->json($order, 201);
    }
}

// Notification Service — Event Subscribe করছে
class OrderCreatedListener
{
    public function handle(OrderCreated $event)
    {
        // Email পাঠাও
        Mail::to($event->order->user->email)
            ->send(new OrderConfirmationMail($event->order));
    }
}
Enter fullscreen mode Exit fullscreen mode

🚦 ৪. API Gateway Pattern

Microservices এ সব সার্ভিস আলাদা থাকলে Client কে একটাই Entry Point দেওয়া হয় — এটাই API Gateway

[Mobile App]  ──┐
[Web App]     ──┤──→ [API Gateway] ──→ [User Service]
[Third Party] ──┘         │        ──→ [Product Service]
                          │        ──→ [Order Service]
                    (Auth, Rate Limit,
                     Logging, Routing)
Enter fullscreen mode Exit fullscreen mode

Laravel-এ Simple API Gateway বানানো:

php
// routes/api.php — Gateway হিসেবে কাজ করবে
Route::middleware(['auth:sanctum', 'throttle:100,1'])->group(function () {

    // User Service এ forward করুন
    Route::any('/users/{path?}', function (Request $request, $path = '') {
        return Http::withToken($request->bearerToken())
                   ->send($request->method(),
                          config('services.user.url') . '/' . $path,
                          ['json' => $request->all()]);
    })->where('path', '.*');

    // Product Service এ forward করুন  
    Route::any('/products/{path?}', function (Request $request, $path = '') {
        return Http::send($request->method(),
                          config('services.product.url') . '/' . $path,
                          ['json' => $request->all()]);
    })->where('path', '.*');
});
Enter fullscreen mode Exit fullscreen mode

📊 ৫. Real-world System Design Example

ধরুন আপনাকে একটা E-commerce সিস্টেম ডিজাইন করতে বলা হলো যেখানে ১০ লক্ষ ইউজার থাকবে:

                    ┌─────────────────────────────┐
                    │         CDN (CloudFront)     │  ← Static files, Images
                    └──────────────┬──────────────┘
                                   │
                    ┌──────────────▼──────────────┐
                    │      Load Balancer (Nginx)   │  ← Traffic distribute
                    └──────┬───────────┬───────────┘
                           │           │
              ┌────────────▼─┐    ┌────▼────────────┐
              │  Laravel App │    │  Laravel App     │  ← Multiple instances
              │  (Server 1)  │    │  (Server 2)      │
              └──────┬───────┘    └────┬─────────────┘
                     │                 │
        ┌────────────▼─────────────────▼────────────┐
        │                                            │
   ┌────▼────┐    ┌──────────┐    ┌───────────────┐  │
   │  Redis  │    │  MySQL   │    │  Elasticsearch│  │
   │ (Cache/ │    │ (Master) │    │  (Search)     │  │
   │  Queue/ │    │    │     │    └───────────────┘  │
   │ Session)│    │ Replica  │                        │
   └─────────┘    │ (Read)   │    ┌───────────────┐  │
                  └──────────┘    │  S3 (Storage) │  │
                                  └───────────────┘  │
        └────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

এই ডিজাইনের প্রতিটা অংশের কাজ:
CDN — Images, CSS, JS ক্যাশ করে রাখে, সার্ভারে চাপ কমায়।
Load Balancer — Request গুলো Server 1 ও Server 2 এ ভাগ করে দেয়।
Redis — Session, Cache এবং Queue — তিনটাই হ্যান্ডেল করে।
MySQL Master-Replica — Master Write করে, Replica Read করে।
Elasticsearch — Product search দ্রুত করে।
S3 — User uploaded files রাখে।


🎯 ৬. Scalability Best Practices — Laravel এ

php
// ১. N+1 Query সমস্যা দূর করুন
// ❌ ভুল — প্রতিটা order এর জন্য আলাদা query
$orders = Order::all();
foreach ($orders as $order) {
    echo $order->user->name; // প্রতিবার DB hit!
}

// ✅ সঠিক — Eager Loading ব্যবহার করুন
$orders = Order::with(['user', 'products', 'payment'])->get();

// ২. Pagination ব্যবহার করুন — সব data একসাথে না নিয়ে
// ❌ ভুল
$products = Product::all(); // ১০ লক্ষ row!

// ✅ সঠিক
$products = Product::paginate(20);

// ৩. Chunk দিয়ে বড় ডেটা প্রসেস করুন
Product::chunk(1000, function ($products) {
    foreach ($products as $product) {
        // প্রতি ১০০০টা করে process হবে, memory overflow হবে না
        ProcessProduct::dispatch($product);
    }
});

// ৪. Database Index ব্যবহার করুন
Schema::table('orders', function (Blueprint $table) {
    $table->index(['user_id', 'status']);      // Composite Index
    $table->index('created_at');               // Date filter এর জন্য
});

// ৫. Rate Limiting — অতিরিক্ত Request ঠেকান
Route::middleware('throttle:60,1')->group(function () {
    Route::get('/products', [ProductController::class, 'index']);
});
Enter fullscreen mode Exit fullscreen mode

🛡️ ৭. Fault Tolerance — সিস্টেম যেন ক্র্যাশ না করে

php
// Circuit Breaker Pattern — একটা সার্ভিস বন্ধ হলে পুরো সিস্টেম যেন না পড়ে
class PaymentService
{
    public function processPayment(Order $order)
    {
        try {
            $response = Http::timeout(5) // ৫ সেকেন্ডের বেশি অপেক্ষা না করা
                            ->retry(3, 1000) // ৩ বার try, ১ সেকেন্ড পরপর
                            ->post('https://payment-gateway.com/api/charge', [
                                'amount'   => $order->total,
                                'currency' => 'BDT',
                            ]);

            if ($response->failed()) {
                // Fallback — alternative payment method try করুন
                return $this->processWithAlternativeGateway($order);
            }

            return $response->json();

        } catch (ConnectionException $e) {
            // Gateway down — Queue তে রাখুন পরে retry করার জন্য
            RetryPayment::dispatch($order)->delay(now()->addMinutes(5));
            Log::error('Payment gateway down', ['order_id' => $order->id]);

            throw new PaymentGatewayException('Payment will be processed shortly');
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

📝 সংক্ষেপে মনে রাখুন
System Design এর মূল বিষয়গুলো হলো — Scalability মানে বেশি লোড সামলানোর ক্ষমতা (Horizontal > Vertical), Microservices মানে বড় সিস্টেমকে ছোট ছোট স্বাধীন সার্ভিসে ভাগ করা, Caching মানে ডাটাবেজে বারবার না গিয়ে দ্রুত ডেটা দেওয়া, Queue মানে ভারী কাজ Background এ করা, এবং Fault Tolerance মানে এক অংশ ভাঙলেও বাকি সিস্টেম চালু রাখা।

Q:2 - APIs (REST, GraphQL, gRPC)
Answer:

🌐 API কী?
API (Application Programming Interface) হলো দুটো সফটওয়্যারের মধ্যে কথা বলার নিয়ম। যেমন — আপনার Laravel অ্যাপ যখন Mobile App কে ডেটা দেয়, সেটা API এর মাধ্যমে হয়।

[Mobile App] ←──── API ────→ [Laravel Backend] ←──→ [Database]
[Web App]    ←──── API ────→ [Laravel Backend]
[Third Party]←──── API ────→ [Laravel Backend]
Enter fullscreen mode Exit fullscreen mode

তিনটা প্রধান API টাইপ আছে — REST, GraphQL, এবং gRPC। প্রতিটার আলাদা সুবিধা ও ব্যবহার আছে।


🔵 PART 1 — REST API
REST API কী?
REST (Representational State Transfer) হলো সবচেয়ে জনপ্রিয় API স্টাইল। এটা HTTP Methods ব্যবহার করে ডেটা আদান-প্রদান করে।

GET    /api/products        → সব product দেখাও
GET    /api/products/5      → ৫ নম্বর product দেখাও
POST   /api/products        → নতুন product তৈরি করো
PUT    /api/products/5      → ৫ নম্বর product সম্পূর্ণ আপডেট করো
PATCH  /api/products/5      → ৫ নম্বর product আংশিক আপডেট করো
DELETE /api/products/5      → ৫ নম্বর product মুছে ফেলো
Enter fullscreen mode Exit fullscreen mode

Laravel-এ Professional REST API বানানো
Step 1 — Route তৈরি করুন:

php
// routes/api.php
use App\Http\Controllers\Api\V1\ProductController;

// API Versioning — v1, v2 আলাদা রাখুন
Route::prefix('v1')->group(function () {

    // Public routes — Auth লাগবে না
    Route::get('/products', [ProductController::class, 'index']);
    Route::get('/products/{id}', [ProductController::class, 'show']);

    // Protected routes — Auth লাগবে
    Route::middleware('auth:sanctum')->group(function () {
        Route::post('/products', [ProductController::class, 'store']);
        Route::put('/products/{id}', [ProductController::class, 'update']);
        Route::delete('/products/{id}', [ProductController::class, 'destroy']);
    });
});
Enter fullscreen mode Exit fullscreen mode

Step 2 — Form Request দিয়ে Validation:

php
// app/Http/Requests/Api/StoreProductRequest.php
class StoreProductRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name'        => 'required|string|max:255',
            'price'       => 'required|numeric|min:0',
            'stock'       => 'required|integer|min:0',
            'category_id' => 'required|exists:categories,id',
            'description' => 'nullable|string|max:1000',
        ];
    }

    public function messages(): array
    {
        return [
            'name.required'        => 'পণ্যের নাম দিতে হবে',
            'price.required'       => 'দাম দিতে হবে',
            'category_id.exists'   => 'ক্যাটাগরি সঠিক নয়',
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3 — API Resource দিয়ে Response Format করুন:

php
// app/Http/Resources/ProductResource.php
class ProductResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id'          => $this->id,
            'name'        => $this->name,
            'price'       => number_format($this->price, 2),
            'price_bdt'   => '৳ ' . number_format($this->price, 2),
            'stock'       => $this->stock,
            'in_stock'    => $this->stock > 0,
            'category'    => new CategoryResource($this->whenLoaded('category')),
            'images'      => ImageResource::collection($this->whenLoaded('images')),
            'created_at'  => $this->created_at->format('d M Y'),

            // Condition — শুধু Admin দেখবে
            'cost_price'  => $this->when(
                                auth()->user()?->isAdmin(),
                                $this->cost_price
                             ),
        ];
    }
}

// Collection Resource
class ProductCollection extends ResourceCollection
{
    public function toArray(Request $request): array
    {
        return [
            'data' => $this->collection,
            'meta' => [
                'total_products' => $this->total(),
                'current_page'   => $this->currentPage(),
            ],
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4 — Controller লিখুন:

php
// app/Http/Controllers/Api/V1/ProductController.php
class ProductController extends Controller
{
    public function index(Request $request)
    {
        $products = Product::query()
            ->with(['category', 'images'])
            ->when($request->search, fn($q) =>
                $q->where('name', 'like', "%{$request->search}%")
            )
            ->when($request->category_id, fn($q) =>
                $q->where('category_id', $request->category_id)
            )
            ->when($request->min_price, fn($q) =>
                $q->where('price', '>=', $request->min_price)
            )
            ->latest()
            ->paginate($request->per_page ?? 15);

        return ProductResource::collection($products);
    }

    public function store(StoreProductRequest $request)
    {
        $product = Product::create($request->validated());

        return (new ProductResource($product))
                    ->response()
                    ->setStatusCode(201); // 201 Created
    }

    public function show(int $id)
    {
        $product = Product::with(['category', 'images', 'reviews'])
                          ->findOrFail($id);

        return new ProductResource($product);
    }

    public function update(UpdateProductRequest $request, int $id)
    {
        $product = Product::findOrFail($id);
        $product->update($request->validated());

        return new ProductResource($product);
    }

    public function destroy(int $id)
    {
        $product = Product::findOrFail($id);
        $product->delete();

        return response()->json([
            'message' => 'Product deleted successfully'
        ], 200);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 5 — Global Exception Handler (সুন্দর Error Response):

php
// app/Exceptions/Handler.php
public function register(): void
{
    $this->renderable(function (Throwable $e, Request $request) {

        if ($request->expectsJson()) {

            // Validation Error
            if ($e instanceof ValidationException) {
                return response()->json([
                    'success' => false,
                    'message' => 'Validation failed',
                    'errors'  => $e->errors(),
                ], 422);
            }

            // Not Found
            if ($e instanceof ModelNotFoundException) {
                return response()->json([
                    'success' => false,
                    'message' => 'Resource not found',
                ], 404);
            }

            // Unauthorized
            if ($e instanceof AuthenticationException) {
                return response()->json([
                    'success' => false,
                    'message' => 'Unauthenticated. Please login first.',
                ], 401);
            }

            // Server Error
            return response()->json([
                'success' => false,
                'message' => 'Something went wrong',
                'debug'   => config('app.debug') ? $e->getMessage() : null,
            ], 500);
        }
    });
}
Enter fullscreen mode Exit fullscreen mode

REST API Response Structure — Best Practice:

json
//  Success Response
{
  "success": true,
  "message": "Products fetched successfully",
  "data": {
    "id": 1,
    "name": "iPhone 15",
    "price": "1,200.00"
  },
  "meta": {
    "total": 150,
    "per_page": 15,
    "current_page": 1
  }
}

//  Error Response
{
  "success": false,
  "message": "Validation failed",
  "errors": {
    "name": ["পণ্যের নাম দিতে হবে"],
    "price": ["দাম দিতে হবে"]
  }
}
Enter fullscreen mode Exit fullscreen mode

🟠 PART 2 — GraphQL API

GraphQL কী এবং কেন REST থেকে আলাদা?

REST এ সমস্যা হলোOver-fetching (বেশি ডেটা আসে) এবং Under-fetching (কম ডেটা আসে, আবার call করতে হয়)।

❌ REST এর সমস্যা:
GET /api/users/1  → পুরো user object আসে (name, email, phone, address, ...)
                    কিন্তু আমার শুধু name দরকার ছিল!

GET /api/users/1  → user আসে, কিন্তু তার orders নেই
GET /api/users/1/orders → আবার আলাদা call করতে হলো!
Enter fullscreen mode Exit fullscreen mode

GraphQL এর সমাধান:
যা চাইবেন, ঠিক তাই পাবেন — এক request এ!
Laravel-এ GraphQL Setup (Lighthouse):

bash
composer require nuwave/lighthouse
php artisan vendor:publish --tag=lighthouse-schema
Enter fullscreen mode Exit fullscreen mode

Schema Define করুন:

graphql
# graphql/schema.graphql

type Query {
    # Single product
    product(id: ID! @eq): Product @find

    # Product list with filtering & pagination
    products(
        search: String @where(operator: "like", key: "name")
        category_id: ID @eq
        min_price: Float @where(operator: ">=", key: "price")
    ): [Product!]! @paginate(defaultCount: 15)

    # Current logged in user
    me: User @auth
}

type Mutation {
    createProduct(input: CreateProductInput! @spread): Product
        @create
        @middleware(checks: ["auth:sanctum"])

    updateProduct(id: ID!, input: UpdateProductInput! @spread): Product
        @update
        @middleware(checks: ["auth:sanctum"])

    deleteProduct(id: ID!): Product
        @delete
        @middleware(checks: ["auth:sanctum"])
}

# Types
type Product {
    id: ID!
    name: String!
    price: Float!
    stock: Int!
    in_stock: Boolean! @method(name: "isInStock")
    category: Category @belongsTo
    images: [Image!]! @hasMany
    reviews: [Review!]! @hasMany
    created_at: DateTime!
}

type User {
    id: ID!
    name: String!
    email: String!
    orders: [Order!]! @hasMany
}

type Category {
    id: ID!
    name: String!
    products: [Product!]! @hasMany
}

# Input Types
input CreateProductInput {
    name: String!
    price: Float!
    stock: Int!
    category_id: ID!
    description: String
}

input UpdateProductInput {
    name: String
    price: Float
    stock: Int
}
Enter fullscreen mode Exit fullscreen mode

GraphQL Query — Client থেকে এভাবে request করবেন:
graphql
শুধু দরকারি field নিন

query GetProduct {
    product(id: 1) {
        id
        name
        price
        category {
            name
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

ফলাফল — শুধু যা চেয়েছেন তাই আসবে:

{
  "data": {
    "product": {
      "id": "1",
      "name": "iPhone 15",
      "price": 1200.00,
      "category": {
        "name": "Electronics"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

একসাথে অনেক কিছু নিন — একটাই Request!

query GetDashboard {
    me {
        name
        email
        orders {
            id
            total
            status
        }
    }
    products(category_id: 1) {
        data {
            id
            name
            price
        }
        paginatorInfo {
            total
            currentPage
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Mutation — Data তৈরি করুন

mutation CreateProduct {
    createProduct(input: {
        name: "Samsung Galaxy S24"
        price: 999.99
        stock: 50
        category_id: 1
    }) {
        id
        name
        price
    }
}
Enter fullscreen mode Exit fullscreen mode

Custom Resolver — Complex Logic এর জন্য:

php
// app/GraphQL/Queries/ProductSearch.php
class ProductSearch
{
    public function __invoke($_, array $args)
    {
        return Product::query()
            ->when(isset($args['search']), fn($q) =>
                $q->where('name', 'like', "%{$args['search']}%")
                  ->orWhere('description', 'like', "%{$args['search']}%")
            )
            ->when(isset($args['min_price']), fn($q) =>
                $q->where('price', '>=', $args['min_price'])
            )
            ->with(['category', 'images'])
            ->get();
    }
}

// app/GraphQL/Mutations/CreateOrder.php
class CreateOrder
{
    public function __invoke($_, array $args)
    {
        // Stock চেক করুন
        $product = Product::lockForUpdate()->findOrFail($args['product_id']);

        if ($product->stock < $args['quantity']) {
            throw new \Exception('Insufficient stock available');
        }

        // Transaction এ Order তৈরি করুন
        return DB::transaction(function () use ($product, $args) {
            $order = Order::create([
                'user_id'    => auth()->id(),
                'product_id' => $product->id,
                'quantity'   => $args['quantity'],
                'total'      => $product->price * $args['quantity'],
            ]);

            $product->decrement('stock', $args['quantity']);

            return $order;
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

🟣 PART 3 — gRPC API

gRPC কী?

gRPC হলো Google এর তৈরি অত্যন্ত দ্রুত API প্রোটোকল। এটা REST এর চেয়ে ৫-১০ গুণ দ্রুত কাজ করে। Microservices এর মধ্যে communication এর জন্য এটা সেরা।

REST  → JSON (Text) → HTTP/1.1   → ধীর, বড় size
gRPC  → Protobuf (Binary) → HTTP/2 → দ্রুত, ছোট size
Enter fullscreen mode Exit fullscreen mode

কখন gRPC ব্যবহার করবেন?
যখন দুটো internal service এর মধ্যে খুব দ্রুত এবং অনেক বেশি communication দরকার — যেমন Order Service থেকে Inventory Service কে প্রতি সেকেন্ডে হাজারবার call করতে হচ্ছে।
Laravel-এ gRPC Setup:

bash
# Protocol Buffer compiler install করুন
pecl install grpc
pecl install protobuf
composer require grpc/grpc
Enter fullscreen mode Exit fullscreen mode

Proto File — API Contract Define করুন:

protobuf
// proto/product.proto
syntax = "proto3";

package product;

// Service Define
service ProductService {
    rpc GetProduct (ProductRequest) returns (ProductResponse);
    rpc GetProducts (ProductsRequest) returns (ProductsResponse);
    rpc CreateProduct (CreateProductRequest) returns (ProductResponse);
    rpc CheckStock (StockRequest) returns (StockResponse);
}

// Messages (REST এ JSON এর মতো)
message ProductRequest {
    int32 id = 1;
}

message ProductResponse {
    int32 id = 1;
    string name = 2;
    double price = 3;
    int32 stock = 4;
    bool in_stock = 5;
}

message StockRequest {
    int32 product_id = 1;
    int32 quantity = 2;
}

message StockResponse {
    bool available = 1;
    int32 current_stock = 2;
    string message = 3;
}

message CreateProductRequest {
    string name = 1;
    double price = 2;
    int32 stock = 3;
    int32 category_id = 4;
}
Enter fullscreen mode Exit fullscreen mode

gRPC Server (Product Service):

php
// app/Grpc/ProductServiceServer.php
class ProductServiceServer extends ProductServiceInterface
{
    // Product খুঁজে দিন
    public function GetProduct(ProductRequest $request, array $metadata = []): array
    {
        $product = Product::find($request->getId());

        if (!$product) {
            return [
                new ProductResponse(),
                Status::status(Code::NOT_FOUND, 'Product not found')
            ];
        }

        $response = new ProductResponse();
        $response->setId($product->id);
        $response->setName($product->name);
        $response->setPrice($product->price);
        $response->setStock($product->stock);
        $response->setInStock($product->stock > 0);

        return [$response, Status::ok()];
    }

    // Stock চেক করুন
    public function CheckStock(StockRequest $request, array $metadata = []): array
    {
        $product = Product::find($request->getProductId());
        $response = new StockResponse();

        if (!$product) {
            $response->setAvailable(false);
            $response->setCurrentStock(0);
            $response->setMessage('Product not found');
        } elseif ($product->stock >= $request->getQuantity()) {
            $response->setAvailable(true);
            $response->setCurrentStock($product->stock);
            $response->setMessage('Stock available');
        } else {
            $response->setAvailable(false);
            $response->setCurrentStock($product->stock);
            $response->setMessage("Only {$product->stock} items available");
        }

        return [$response, Status::ok()];
    }
}
Enter fullscreen mode Exit fullscreen mode

gRPC Client (Order Service থেকে Product Service কে call করা):

php
// app/Services/ProductGrpcClient.php
class ProductGrpcClient
{
    private ProductServiceClient $client;

    public function __construct()
    {
        // Product Service এর address
        $this->client = new ProductServiceClient(
            'product-service:50051', // Internal DNS
            ['credentials' => ChannelCredentials::createInsecure()]
        );
    }

    public function checkStock(int $productId, int $quantity): array
    {
        $request = new StockRequest();
        $request->setProductId($productId);
        $request->setQuantity($quantity);

        [$response, $status] = $this->client->CheckStock($request)->wait();

        if ($status->code !== Code::OK) {
            throw new \Exception('gRPC Error: ' . $status->details);
        }

        return [
            'available'     => $response->getAvailable(),
            'current_stock' => $response->getCurrentStock(),
            'message'       => $response->getMessage(),
        ];
    }
}

// Order Service এ ব্যবহার করুন
class OrderController extends Controller
{
    public function store(Request $request, ProductGrpcClient $productClient)
    {
        // gRPC দিয়ে Product Service কে call — অত্যন্ত দ্রুত!
        $stockInfo = $productClient->checkStock(
            $request->product_id,
            $request->quantity
        );

        if (!$stockInfo['available']) {
            return response()->json([
                'message' => $stockInfo['message']
            ], 422);
        }

        $order = Order::create($request->validated());

        return response()->json($order, 201);
    }
}
Enter fullscreen mode Exit fullscreen mode

⚖️ REST vs GraphQL vs gRPC — কোনটা কখন?

┌─────────────┬──────────────┬──────────────┬──────────────┐
│             │     REST     │   GraphQL    │     gRPC     │
├─────────────┼──────────────┼──────────────┼──────────────┤
│ সেরা ব্যবহার│ Public API   │ Complex UI   │ Microservice │
│             │              │ Mobile App   │ Internal API │
├─────────────┼──────────────┼──────────────┼──────────────┤
│ Speed       │ ⭐⭐⭐        │ ⭐⭐⭐        │ ⭐⭐⭐⭐⭐    │
├─────────────┼──────────────┼──────────────┼──────────────┤
│ শেখা সহজ   │ ✅ সহজ       │ মাঝামাঝি    │ ❌ কঠিন     │
├─────────────┼──────────────┼──────────────┼──────────────┤
│ Flexibility │ কম           │ ✅ অনেক বেশি│ কম          │
├─────────────┼──────────────┼──────────────┼──────────────┤
│ Browser     │ ✅ সরাসরি   │ ✅ সরাসরি   │ ❌ কঠিন     │
│ Support     │              │              │              │
└─────────────┴──────────────┴──────────────┴──────────────┘
Enter fullscreen mode Exit fullscreen mode

Real Project এ কীভাবে ব্যবহার করবেন:

আপনার E-commerce App:

REST API      → Third-party integration, Payment Gateway, Public API
GraphQL       → Mobile App, Web Dashboard (যেখানে flexible data দরকার)
gRPC          → Order Service ↔ Inventory Service (Internal fast communication)
Enter fullscreen mode Exit fullscreen mode

🔐 API Security — সব API তে দরকার

php
// ১. Rate Limiting — প্রতি মিনিটে সর্বোচ্চ কতবার call করা যাবে
RateLimiter::for('api', function (Request $request) {
    return Limit::perMinute(60)->by(
        $request->user()?->id ?: $request->ip()
    );
});

// ২. API Versioning — পুরনো clients ভাঙবে না
Route::prefix('v1')->group(...); // পুরনো version চলবে
Route::prefix('v2')->group(...); // নতুন version আলাদা

// ৩. Request Logging — কে কখন কী করলো
class ApiLoggingMiddleware
{
    public function handle(Request $request, Closure $next)
    {
        $response = $next($request);

        Log::channel('api')->info('API Request', [
            'method'      => $request->method(),
            'url'         => $request->fullUrl(),
            'user_id'     => auth()->id(),
            'ip'          => $request->ip(),
            'status'      => $response->getStatusCode(),
            'duration_ms' => round((microtime(true) - LARAVEL_START) * 1000),
        ]);

        return $response;
    }
}

// ৪. Response Cache — একই request বারবার DB তে না গিয়ে
Route::middleware('cache.headers:public;max_age=3600;etag')->group(function () {
    Route::get('/products', [ProductController::class, 'index']);
});
Enter fullscreen mode Exit fullscreen mode

📝 সংক্ষেপে মনে রাখুন
REST হলো সবচেয়ে সহজ ও জনপ্রিয় — Public API এর জন্য সেরা। GraphQL হলো Flexible — Mobile/Web App এ যখন কতটুকু ডেটা দরকার সেটা Client ঠিক করে। gRPC হলো সবচেয়ে দ্রুত — Microservices এর internal communication এ সেরা।
Laravel Backend Engineer হিসেবে REST অবশ্যই আয়ত্ত করুন, তারপর GraphQL শিখুন, এবং যখন Microservices নিয়ে কাজ করবেন তখন gRPC শিখুন।

Q:3 - Database Systems (SQL, NoSQL) ?
ANSWER:

🗄️ Database কী?
Database হলো ডেটা সংরক্ষণের সিস্টেম। আপনার অ্যাপের সব ডেটা — User, Product, Order — সব কিছু Database এ থাকে।
দুই ধরনের Database আছে:

SQL (Relational)          NoSQL (Non-Relational)
─────────────────         ──────────────────────
MySQL, PostgreSQL    vs   MongoDB, Redis
টেবিল আকারে ডেটা         Document/Key-Value আকারে
Fixed Schema              Flexible Schema
Relations আছে            Relations নেই (বেশিরভাগ)
Enter fullscreen mode Exit fullscreen mode

🔵 PART 1 — SQL Database
SQL কী?
SQL Database এ ডেটা টেবিল আকারে থাকে — অনেকটা Excel Spreadsheet এর মতো। প্রতিটা Row হলো একটা Record, প্রতিটা Column হলো একটা Field।

products টেবিল:
┌────┬─────────────┬────────┬───────┬─────────────┐
│ id │ name        │ price  │ stock │ category_id │
├────┼─────────────┼────────┼───────┼─────────────┤
│  1 │ iPhone 15   │ 1200   │  50   │      1      │
│  2 │ Samsung S24 │  999   │  30   │      1      │
│  3 │ MacBook Pro │ 2500   │  20   │      2      │
└────┴─────────────┴────────┴───────┴─────────────┘

Enter fullscreen mode Exit fullscreen mode

Laravel-এ Database Migration — Professional ভাবে:

php
// database/migrations/create_products_table.php
Schema::create('products', function (Blueprint $table) {

    // Primary Key
    $table->id(); // Auto increment bigint

    // Basic Fields
    $table->string('name', 255);
    $table->string('slug')->unique(); // URL friendly name
    $table->text('description')->nullable();
    $table->decimal('price', 10, 2); // 99999999.99 পর্যন্ত
    $table->decimal('cost_price', 10, 2)->default(0);
    $table->unsignedInteger('stock')->default(0);
    $table->unsignedInteger('min_stock')->default(5); // Low stock alert

    // Status
    $table->enum('status', ['active', 'inactive', 'draft'])->default('active');
    $table->boolean('is_featured')->default(false);

    // Foreign Key — Category এর সাথে সম্পর্ক
    $table->foreignId('category_id')
          ->constrained('categories')
          ->onDelete('restrict'); // Category মুছলে Error দেবে

    $table->foreignId('brand_id')
          ->nullable()
          ->constrained('brands')
          ->onDelete('set null'); // Brand মুছলে NULL হবে

    // Performance এর জন্য Index
    $table->index('status');
    $table->index('category_id');
    $table->index(['price', 'status']); // Composite Index
    $table->fullText(['name', 'description']); // Full-text search

    // Timestamps
    $table->timestamps(); // created_at, updated_at
    $table->softDeletes(); // deleted_at — পুরোপুরি মুছবে না
});
Enter fullscreen mode Exit fullscreen mode

Eloquent Relationships — টেবিলের মধ্যে সম্পর্ক:

php
// ১. One to Many — একটা Category তে অনেক Product
// Category Model
class Category extends Model
{
    public function products(): HasMany
    {
        return $this->hasMany(Product::class);
    }

    // শুধু Active Product
    public function activeProducts(): HasMany
    {
        return $this->hasMany(Product::class)
                    ->where('status', 'active');
    }
}

// ২. Many to Many — একটা Product এ অনেক Tag,
//    একটা Tag এ অনেক Product
class Product extends Model
{
    public function tags(): BelongsToMany
    {
        return $this->belongsToMany(Tag::class, 'product_tags')
                    ->withPivot('added_by') // Pivot table এর extra field
                    ->withTimestamps();
    }
}

// ৩. Has Many Through — User এর Orders এর Products
class User extends Model
{
    public function purchasedProducts(): HasManyThrough
    {
        return $this->hasManyThrough(
            Product::class,
            Order::class,
            'user_id',    // orders.user_id
            'id',         // products.id
            'id',         // users.id
            'product_id'  // orders.product_id
        );
    }
}

// ৪. Polymorphic — Comment একসাথে Product ও Blog এ থাকতে পারে
class Comment extends Model
{
    public function commentable(): MorphTo
    {
        return $this->morphTo();
    }
}

class Product extends Model
{
    public function comments(): MorphMany
    {
        return $this->morphMany(Comment::class, 'commentable');
    }
}
Enter fullscreen mode Exit fullscreen mode

Advanced Eloquent Queries:

php
// ১. Complex Query Builder
$products = Product::query()
    ->select([
        'products.*',
        DB::raw('(SELECT AVG(rating) FROM reviews
                  WHERE reviews.product_id = products.id) as avg_rating'),
        DB::raw('(SELECT COUNT(*) FROM order_items
                  WHERE order_items.product_id = products.id) as total_sold'),
    ])
    ->with(['category:id,name', 'brand:id,name', 'images'])
    ->withCount('reviews')
    ->where('status', 'active')
    ->where('stock', '>', 0)
    ->whereBetween('price', [500, 2000])
    ->whereHas('category', fn($q) => $q->where('is_active', true))
    ->when(request('search'), fn($q, $search) =>
        $q->where(function($query) use ($search) {
            $query->where('name', 'like', "%{$search}%")
                  ->orWhere('description', 'like', "%{$search}%");
        })
    )
    ->orderByDesc('total_sold')
    ->orderByDesc('avg_rating')
    ->paginate(20);

// ২. Subquery — সবচেয়ে বেশি বিক্রি হওয়া Product
$topProducts = Product::addSelect([
    'last_order_date' => Order::select('created_at')
        ->whereColumn('product_id', 'products.id')
        ->latest()
        ->limit(1),
])->get();

// ৩. Aggregation — Sales Report
$salesReport = Order::query()
    ->select([
        DB::raw('DATE(created_at) as date'),
        DB::raw('COUNT(*) as total_orders'),
        DB::raw('SUM(total) as revenue'),
        DB::raw('AVG(total) as avg_order_value'),
        DB::raw('MAX(total) as highest_order'),
    ])
    ->where('status', 'completed')
    ->whereBetween('created_at', [
        now()->startOfMonth(),
        now()->endOfMonth()
    ])
    ->groupBy('date')
    ->orderBy('date')
    ->get();

// ৪. Transaction — একসাথে অনেক কাজ, একটা ব্যর্থ হলে সব Rollback
DB::transaction(function () use ($request) {
    // Order তৈরি করুন
    $order = Order::create([
        'user_id' => auth()->id(),
        'total'   => $request->total,
        'status'  => 'pending',
    ]);

    // প্রতিটা Product এর জন্য OrderItem তৈরি করুন
    foreach ($request->items as $item) {
        // Stock check with lock — Race condition এড়াতে
        $product = Product::lockForUpdate()->findOrFail($item['product_id']);

        if ($product->stock < $item['quantity']) {
            throw new \Exception("{$product->name} এর stock নেই");
        }

        OrderItem::create([
            'order_id'   => $order->id,
            'product_id' => $product->id,
            'quantity'   => $item['quantity'],
            'price'      => $product->price,
        ]);

        // Stock কমিয়ে দিন
        $product->decrement('stock', $item['quantity']);
    }

    // Payment record তৈরি করুন
    Payment::create([
        'order_id' => $order->id,
        'amount'   => $order->total,
        'method'   => $request->payment_method,
    ]);

    return $order;
});
Enter fullscreen mode Exit fullscreen mode

Database Performance — SQL Optimization:

php
// ১. N+1 Query সমস্যা — সবচেয়ে Common ভুল
// ❌ ভুল — ১০০ Product এর জন্য ১০১ টা Query!
$products = Product::all();
foreach ($products as $product) {
    echo $product->category->name; // প্রতিবার DB hit
    echo $product->brand->name;    // আবার DB hit
}

// ✅ সঠিক — মাত্র ৩টা Query
$products = Product::with(['category', 'brand'])->get();

// ২. Lazy Loading vs Eager Loading
// Lazy — শুধু দরকার হলে load হবে (সাবধানে ব্যবহার করুন)
$product = Product::find(1);
if ($showCategory) {
    $product->load('category'); // এখন load হবে
}

// ৩. Chunking — লক্ষ লক্ষ Record Process করুন Memory Overflow ছাড়া
Product::with('category')
       ->where('status', 'active')
       ->chunkById(500, function ($products) {
           foreach ($products as $product) {
               // প্রতি ৫০০টা করে process হবে
               GenerateProductReport::dispatch($product);
           }
       });

// ৪. Cursor — আরো কম Memory তে
foreach (Product::cursor() as $product) {
    // একটা একটা করে process — memory খুব কম লাগে
    $product->update(['slug' => Str::slug($product->name)]);
}

// ৫. Index ব্যবহার করুন — Query দ্রুত হবে
// Migration এ:
$table->index('status');                    // Single index
$table->index(['category_id', 'status']);   // Composite index
$table->index('created_at');               // Date filter এর জন্য

// ৬. Query Cache
$popularProducts = Cache::remember('popular_products', 3600, function () {
    return Product::with('category')
                  ->where('is_featured', true)
                  ->orderByDesc('total_sold')
                  ->take(10)
                  ->get();
});

// Cache clear করুন Product update হলে
Product::observe(ProductObserver::class);

class ProductObserver
{
    public function saved(Product $product): void
    {
        Cache::forget('popular_products');
        Cache::forget("product_{$product->id}");
    }
}
Enter fullscreen mode Exit fullscreen mode

PostgreSQL — MySQL এর চেয়ে শক্তিশালী কোথায়?

php
// ১. JSON Column — Flexible Data Store করুন
Schema::table('products', function (Blueprint $table) {
    $table->jsonb('attributes')->nullable(); // jsonb — indexed & faster
    $table->jsonb('metadata')->nullable();
});

// JSON এ data store করুন
$product->update([
    'attributes' => [
        'color'   => 'Space Black',
        'storage' => '256GB',
        'ram'     => '8GB',
    ]
]);

// JSON field দিয়ে query করুন
$blackPhones = Product::where('attributes->color', 'Space Black')
                       ->where('attributes->storage', '256GB')
                       ->get();

// ২. Full-Text Search — PostgreSQL অনেক ভালো
$products = Product::whereRaw(
    "to_tsvector('english', name || ' ' || description)
     @@ plainto_tsquery('english', ?)",
    [$request->search]
)->get();

// ৩. Window Functions — Advanced Analytics
$rankedProducts = DB::select("
    SELECT
        id, name, price, category_id,
        RANK() OVER (
            PARTITION BY category_id
            ORDER BY price DESC
        ) as price_rank,
        SUM(price) OVER (
            PARTITION BY category_id
        ) as category_total
    FROM products
    WHERE status = 'active'
");
Enter fullscreen mode Exit fullscreen mode

🟠 PART 2 — NoSQL Database

NoSQL কী এবং কেন দরকার?

NoSQL এ ডেটা Document আকারে থাকে — অনেকটা JSON এর মতো। Fixed Schema নেই, যেকোনো Structure এ ডেটা রাখা যায়।

SQL (MySQL):                    NoSQL (MongoDB):
──────────────────              ────────────────────────────
টেবিল → Row → Column           Collection → Document → Field

products টেবিল:                products collection:
id | name | price              {
1  | iPhone | 1200               "_id": "abc123",
                                 "name": "iPhone 15",
                                 "price": 1200,
                                 "variants": [        ← Nested!
                                   {"color": "Black", "stock": 20},
                                   {"color": "White", "stock": 15}
                                 ],
                                 "specs": {           ← Flexible!
                                   "ram": "8GB",
                                   "storage": "256GB"
                                 }
                               }
Enter fullscreen mode Exit fullscreen mode

MongoDB — Laravel Setup:

bashcomposer require mongodb/laravel-mongodb
Enter fullscreen mode Exit fullscreen mode
php
// config/database.php
'mongodb' => [
    'driver'   => 'mongodb',
    'host'     => env('MONGODB_HOST', '127.0.0.1'),
    'port'     => env('MONGODB_PORT', 27017),
    'database' => env('MONGODB_DATABASE', 'myapp'),
    'username' => env('MONGODB_USERNAME', ''),
    'password' => env('MONGODB_PASSWORD', ''),
],

// Model
class Product extends Model
{
    protected $connection = 'mongodb';
    protected $collection = 'products';

    protected $fillable = [
        'name', 'price', 'stock', 'attributes',
        'variants', 'images', 'category'
    ];
}
Enter fullscreen mode Exit fullscreen mode

MongoDB CRUD Operations:

php
// ১. Create — Flexible Structure
$product = Product::create([
    'name'  => 'iPhone 15 Pro',
    'price' => 1299,
    'stock' => 100,

    // Nested Object — SQL এ আলাদা টেবিল লাগতো
    'category' => [
        'id'   => 1,
        'name' => 'Smartphones',
        'slug' => 'smartphones',
    ],

    // Array of Objects — SQL এ Junction Table লাগতো
    'variants' => [
        ['color' => 'Natural Titanium', 'storage' => '128GB', 'stock' => 30, 'price' => 1299],
        ['color' => 'Black Titanium',   'storage' => '256GB', 'stock' => 25, 'price' => 1499],
        ['color' => 'White Titanium',   'storage' => '512GB', 'stock' => 15, 'price' => 1699],
    ],

    // Completely Different Attributes per Product
    'specs' => [
        'chip'    => 'A17 Pro',
        'display' => '6.1 inch Super Retina XDR',
        'camera'  => '48MP Main + 12MP Ultra Wide',
        'battery' => '3274 mAh',
    ],

    'tags'   => ['flagship', 'apple', '5g', 'pro'],
    'images' => [
        ['url' => 'front.jpg', 'is_primary' => true],
        ['url' => 'back.jpg',  'is_primary' => false],
    ],
]);

// ২. Read — Nested Field Query
// Nested field এ search
$blackPhones = Product::where('variants.color', 'Black Titanium')->get();

// Array এ value খুঁজুন
$taggedProducts = Product::where('tags', 'flagship')->get();

// Nested object এ range query
$affordableVariants = Product::where('variants.price', '<=', 1500)->get();

// ৩. Update — Nested Array Update
// Array তে নতুন item যোগ করুন
$product->push('tags', 'bestseller');

// Nested object update করুন
$product->update(['specs.battery' => '3500 mAh']);

// Array থেকে item সরান
$product->pull('tags', 'old-tag');

// ৪. Aggregation Pipeline — Complex Analytics
$categoryStats = Product::raw(function($collection) {
    return $collection->aggregate([
        // Stage 1: Filter
        ['$match' => ['status' => 'active']],

        // Stage 2: Unwind array
        ['$unwind' => '$variants'],

        // Stage 3: Group করুন
        ['$group' => [
            '_id'         => '$category.name',
            'total_products' => ['$sum' => 1],
            'avg_price'   => ['$avg' => '$variants.price'],
            'total_stock' => ['$sum' => '$variants.stock'],
            'min_price'   => ['$min' => '$variants.price'],
            'max_price'   => ['$max' => '$variants.price'],
        ]],

        // Stage 4: Sort
        ['$sort' => ['total_products' => -1]],

        // Stage 5: Limit
        ['$limit' => 10],
    ]);
});
Enter fullscreen mode Exit fullscreen mode

Redis — সবচেয়ে দ্রুত NoSQL:
Redis হলো In-Memory Database — সব ডেটা RAM এ থাকে, তাই অত্যন্ত দ্রুত। এটা মূলত Cache, Session, Queue, এবং Real-time কাজে ব্যবহার হয়।

php
// ১. Simple Cache
// Set — ১ ঘন্টার জন্য cache করুন
Redis::setex('product:1', 3600, json_encode($product));

// Get
$product = json_decode(Redis::get('product:1'));

// Delete
Redis::del('product:1');

// ২. Hash — Object Store করুন
Redis::hmset("user:{$userId}", [
    'name'       => 'রাকিব হাসান',
    'email'      => 'rakib@example.com',
    'last_login' => now()->toDateTimeString(),
    'total_orders' => 25,
]);

// একটা field পড়ুন
$name = Redis::hget("user:{$userId}", 'name');

// সব field পড়ুন
$user = Redis::hgetall("user:{$userId}");

// ৩. Counter — View Count, Like Count
Redis::incr("product:{$productId}:views");
Redis::incrby("product:{$productId}:likes", 1);

$views = Redis::get("product:{$productId}:views");

// ৪. Sorted Set — Leaderboard/Ranking
// Score যোগ করুন
Redis::zadd('product:sales', ['iPhone 15' => 1500, 'Samsung S24' => 1200]);

// Top 10 দেখুন (সবচেয়ে বেশি বিক্রি)
$topSellers = Redis::zrevrange('product:sales', 0, 9, 'WITHSCORES');

// ৫. Pub/Sub — Real-time Notification
// Publisher (Event হলে publish করুন)
Redis::publish('order.created', json_encode([
    'order_id' => $order->id,
    'user_id'  => $order->user_id,
    'total'    => $order->total,
]));

// Subscriber (Background এ চালু থাকবে)
Redis::subscribe(['order.created'], function ($message) {
    $data = json_decode($message);
    // WebSocket দিয়ে User কে notify করুন
    broadcast(new OrderCreatedEvent($data));
});

// ৬. Rate Limiter — Redis দিয়ে
class ApiRateLimiter
{
    public function check(string $key, int $maxAttempts, int $decaySeconds): bool
    {
        $current = Redis::incr($key);

        if ($current === 1) {
            Redis::expire($key, $decaySeconds);
        }

        return $current <= $maxAttempts;
    }
}

// Middleware এ ব্যবহার করুন
if (!$rateLimiter->check("api:{$userId}", 60, 60)) {
    return response()->json(['message' => 'Too many requests'], 429);
}
Enter fullscreen mode Exit fullscreen mode

🔄 PART 3 — SQL vs NoSQL কোনটা কখন?

php
// ✅ SQL ব্যবহার করুন যখন:
// - Data structure fixed এবং সম্পর্ক আছে
// - Financial data (Bank, Payment) — ACID দরকার
// - Complex Joins এবং Transactions দরকার
// - Data Integrity সবচেয়ে গুরুত্বপূর্ণ

// ✅ NoSQL ব্যবহার করুন যখন:
// - Data structure flexible বা unknown
// - Huge scale (লক্ষ লক্ষ record দ্রুত read/write)
// - JSON/Document store করতে হবে
// - Real-time বা Cache দরকার
Enter fullscreen mode Exit fullscreen mode

Real Project এ দুটো একসাথে ব্যবহার:

php
class ProductService
{
    // ১. প্রথমে Redis Cache চেক করুন
    public function getProduct(int $id): array
    {
        $cacheKey = "product:{$id}";

        // Cache এ আছে? → সরাসরি দিন (সবচেয়ে দ্রুত)
        if ($cached = Redis::get($cacheKey)) {
            return json_decode($cached, true);
        }

        // MySQL থেকে নিন → Cache এ রাখুন
        $product = Product::with(['category', 'brand'])->findOrFail($id);
        $productArray = $product->toArray();

        Redis::setex($cacheKey, 3600, json_encode($productArray));

        return $productArray;
    }

    // ২. Product View Track করুন MongoDB তে
    public function trackView(int $productId, string $userId): void
    {
        // MongoDB তে analytics store করুন
        ProductView::create([
            'product_id' => $productId,
            'user_id'    => $userId,
            'ip'         => request()->ip(),
            'user_agent' => request()->userAgent(),
            'viewed_at'  => now(),
            'session'    => [
                'source'   => request()->header('referer'),
                'platform' => $this->detectPlatform(),
            ],
        ]);

        // Redis এ view count বাড়ান
        Redis::incr("product:{$productId}:views");
    }

    // ৩. Search এর জন্য Elasticsearch
    public function search(string $query): Collection
    {
        // Elasticsearch এ full-text search
        $results = Elasticsearch::search([
            'index' => 'products',
            'body'  => [
                'query' => [
                    'multi_match' => [
                        'query'  => $query,
                        'fields' => ['name^3', 'description', 'tags^2'],
                        'fuzziness' => 'AUTO',
                    ]
                ]
            ]
        ]);

        // ID গুলো নিয়ে MySQL থেকে full data আনুন
        $ids = collect($results['hits']['hits'])->pluck('_id');
        return Product::whereIn('id', $ids)->with('category')->get();
    }
}
Enter fullscreen mode Exit fullscreen mode

🏗️ Database Design Best Practices

php
// ১. Naming Convention — সবসময় consistent রাখুন
// টেবিল: plural, snake_case
// products, order_items, product_categories

// Column: singular, snake_case
// user_id, created_at, is_active

// Foreign Key: table_name + _id
// category_id, brand_id, user_id

// ২. Soft Delete — পুরোপুরি মুছবেন না
class Product extends Model
{
    use SoftDeletes; // deleted_at column এ date রাখে

    // Trashed সহ query
    Product::withTrashed()->find(1);

    // শুধু Trashed
    Product::onlyTrashed()->get();

    // Restore করুন
    Product::withTrashed()->find(1)->restore();

    // সত্যিকার Delete
    Product::withTrashed()->find(1)->forceDelete();
}

// ৩. Observer — Model Events Handle করুন
class ProductObserver
{
    public function creating(Product $product): void
    {
        // Auto slug generate করুন
        $product->slug = Str::slug($product->name);
    }

    public function updating(Product $product): void
    {
        // Stock কমলে Alert দিন
        if ($product->isDirty('stock') && $product->stock < $product->min_stock) {
            LowStockAlert::dispatch($product);
        }
    }

    public function deleted(Product $product): void
    {
        // Cache clear করুন
        Cache::forget("product:{$product->id}");
        Cache::forget('popular_products');
    }
}

// ৪. Scope — বারবার একই Query না লিখতে
class Product extends Model
{
    // Global Scope — সবসময় apply হবে
    protected static function booted(): void
    {
        static::addGlobalScope('active', function($query) {
            $query->where('status', 'active');
        });
    }

    // Local Scope — দরকার মতো ব্যবহার করুন
    public function scopeInStock($query)
    {
        return $query->where('stock', '>', 0);
    }

    public function scopeFeatured($query)
    {
        return $query->where('is_featured', true);
    }

    public function scopePriceRange($query, $min, $max)
    {
        return $query->whereBetween('price', [$min, $max]);
    }
}

// ব্যবহার:
$products = Product::inStock()
                   ->featured()
                   ->priceRange(500, 2000)
                   ->get();
Enter fullscreen mode Exit fullscreen mode

📝 সংক্ষেপে মনে রাখুন
SQL (MySQL/PostgreSQL) ব্যবহার করুন যখন ডেটার মধ্যে সম্পর্ক আছে এবং Transactions দরকার — যেমন Orders, Payments, Users। MongoDB ব্যবহার করুন যখন ডেটার Structure flexible — যেমন Products এর Variants, Analytics। Redis ব্যবহার করুন Cache, Session, Queue এবং Real-time এর জন্য — এটা সবচেয়ে দ্রুত।
Real Project এ তিনটাই একসাথে ব্যবহার করা হয় — MySQL মূল ডেটার জন্য, Redis speed এর জন্য, MongoDB flexible data এর জন্য।

Q:4 - Distributed Systems (consistency, replication)
Answer:

🌐 Distributed System কী?
Distributed System হলো এমন একটি সিস্টেম যেখানে একাধিক কম্পিউটার/সার্ভার একসাথে কাজ করে — কিন্তু বাইরে থেকে দেখলে মনে হয় একটাই সিস্টেম

❌ Single Server (সমস্যা):
[সব User] → [একটা Server] → [একটা Database]
             ↑ এটা crash করলে সব শেষ!

✅ Distributed System (সমাধান):
[User] → [Load Balancer]
              ↓
    ┌─────────┬─────────┐
[Server 1] [Server 2] [Server 3]
    ↓           ↓          ↓
[DB Master] ←──────────────┘
    ↓
[DB Replica 1] [DB Replica 2]
Enter fullscreen mode Exit fullscreen mode

🧩 PART 1 — CAP Theorem
Distributed System এর সবচেয়ে গুরুত্বপূর্ণ Theory
CAP Theorem বলে — একটা Distributed System একসাথে তিনটার মধ্যে মাত্র দুটো গ্যারান্টি দিতে পারে:

C = Consistency    → সব Node এ একই Data দেখাবে
A = Availability   → সবসময় Response পাবেন
P = Partition      → Network ভাগ হলেও সিস্টেম চলবে
  Tolerance
Enter fullscreen mode Exit fullscreen mode
        Consistency (C)
              △
              │
              │
    CA ───────┼──────── CP
              │
              │
              │
    ──────────┼──────────
              │
    AP        │
              │
        Partition (P) ──── Availability (A)
Enter fullscreen mode Exit fullscreen mode
CA — MySQL, PostgreSQL
     (Network Partition হলে বন্ধ হয়ে যায়)

CP — MongoDB, Redis, HBase
     (Partition হলে Availability sacrifice করে)

AP — Cassandra, CouchDB
     (Partition হলে Consistency sacrifice করে)
Enter fullscreen mode Exit fullscreen mode

Real-world উদাহরণ:

php
// ধরুন আপনার E-commerce এ Payment হচ্ছে

// CP System (Bank/Payment) — Consistency বেশি জরুরি
// Network issue হলে → Transaction বন্ধ রাখো
// কারণ: ভুল balance দেখানো মানে বড় সমস্যা

// AP System (Product View Count) — Availability বেশি জরুরি
// Network issue হলে → পুরনো count দেখাও, পরে ঠিক করো
// কারণ: ১০০ এর জায়গায় ৯৮ দেখালে বড় ক্ষতি নেই
Enter fullscreen mode Exit fullscreen mode

🔄 PART 2 — Consistency (সামঞ্জস্যতা)

Consistency Levels — কতটা সঠিক ডেটা দরকার?

Strong Consistency      সবচ়ে নত Data  
Eventual Consistency    একট রন হত   
Weak Consistency        রন Data ি   সবচ়ে 
Enter fullscreen mode Exit fullscreen mode

Laravel-এ Consistency Implement করা:
১. Strong Consistency — Critical Data এর জন্য:

php
// Payment বা Financial Transaction — সবসময় Master DB পড়ুন
class PaymentService
{
    public function processPayment(Order $order, array $paymentData): Payment
    {
        return DB::transaction(function () use ($order, $paymentData) {

            // Master DB থেকে পড়ুন — সবচেয়ে নতুন data
            // Replica থেকে নয়! Stale data বিপজ্জনক
            $user = User::lockForUpdate()->findOrFail($order->user_id);
            $wallet = Wallet::lockForUpdate()
                            ->where('user_id', $user->id)
                            ->firstOrFail();

            // Balance চেক করুন
            if ($wallet->balance < $order->total) {
                throw new InsufficientBalanceException(
                    "Balance কম। প্রয়োজন: {$order->total}, আছে: {$wallet->balance}"
                );
            }

            // Balance কাটুন
            $wallet->decrement('balance', $order->total);

            // Payment Record তৈরি করুন
            $payment = Payment::create([
                'order_id'     => $order->id,
                'user_id'      => $user->id,
                'amount'       => $order->total,
                'status'       => 'completed',
                'reference_no' => $this->generateReference(),
            ]);

            // Order Status Update করুন
            $order->update(['status' => 'paid', 'payment_id' => $payment->id]);

            return $payment;
        });
    }
}

// Config — Critical Query সবসময় Master এ
class CriticalController extends Controller
{
    public function sensitiveAction(Request $request)
    {
        // Sticky Session — এই request এর সব query Master এ যাবে
        DB::statement('SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE');

        $result = DB::transaction(function() use ($request) {
            return $this->paymentService->process($request->all());
        });

        return response()->json($result);
    }
}
Enter fullscreen mode Exit fullscreen mode

২. Eventual Consistency — Non-critical Data এর জন্য:

php
// Product View Count — একটু পুরনো হলেও সমস্যা নেই
class ProductViewController
{
    public function track(int $productId): void
    {
        // Redis এ দ্রুত increment করুন (Eventual)
        // Database এ সাথে সাথে না গিয়ে পরে Sync হবে
        Redis::incr("product:{$productId}:views");
        Redis::expire("product:{$productId}:views", 86400);

        // Queue এ Job দিন — পরে DB Sync হবে
        SyncProductViews::dispatch($productId)
                        ->delay(now()->addMinutes(5));
    }
}

// Background Job — Redis থেকে DB তে Sync
class SyncProductViews implements ShouldQueue
{
    public function __construct(private int $productId) {}

    public function handle(): void
    {
        $views = Redis::get("product:{$this->productId}:views") ?? 0;

        Product::where('id', $this->productId)
               ->update(['view_count' => DB::raw("view_count + {$views}")]);

        // Redis reset করুন
        Redis::del("product:{$this->productId}:views");
    }
}
Enter fullscreen mode Exit fullscreen mode

৩. Read-Your-Writes Consistency:

php
// Problem: User profile update করলো,
// কিন্তু Replica থেকে পুরনো data দেখাচ্ছে!

class UserProfileService
{
    public function updateProfile(User $user, array $data): User
    {
        // Master এ update করুন
        $user->update($data);

        // Session এ flag রাখুন — এই user সবে update করেছে
        session(['read_from_master_until' => now()->addSeconds(5)]);

        return $user;
    }

    public function getProfile(int $userId): User
    {
        // Update করার পর ৫ সেকেন্ড Master থেকে পড়ুন
        if (session('read_from_master_until') > now()) {
            return DB::connection('mysql_master')
                     ->table('users')
                     ->where('id', $userId)
                     ->first();
        }

        // তারপর Replica থেকে পড়ুন (দ্রুত)
        return User::find($userId);
    }
}
Enter fullscreen mode Exit fullscreen mode

📋 PART 3 — Replication (প্রতিলিপি)

Replication কী?

একই ডেটা একাধিক সার্ভারে রাখা — যাতে একটা বন্ধ হলেও বাকিগুলো চলে।

Master-Slave Replication:

[Write] → [Master DB] ──→ [Slave/Replica 1] ← [Read]
                      └──→ [Slave/Replica 2] ← [Read]
                      └──→ [Slave/Replica 3] ← [Read]

Master: শুধু Write
Slave:  শুধু Read (অনেক বেশি Read Traffic সামলাতে পারে)
Enter fullscreen mode Exit fullscreen mode

Laravel-এ Master-Slave Replication:

php
// config/database.php
'mysql' => [
    'driver' => 'mysql',

    // Write — সব INSERT, UPDATE, DELETE এখানে যাবে
    'write' => [
        'host' => env('DB_MASTER_HOST', '10.0.0.1'),
        'port' => env('DB_MASTER_PORT', 3306),
    ],

    // Read — সব SELECT এখানে যাবে (Load Balance হবে)
    'read' => [
        ['host' => env('DB_REPLICA_1_HOST', '10.0.0.2')],
        ['host' => env('DB_REPLICA_2_HOST', '10.0.0.3')],
        ['host' => env('DB_REPLICA_3_HOST', '10.0.0.4')],
    ],

    'sticky'   => true, // Write এর পর সাথে সাথে Read করলে Master থেকে পড়বে
    'database' => env('DB_DATABASE'),
    'username' => env('DB_USERNAME'),
    'password' => env('DB_PASSWORD'),
],

// আলাদা Connection — Critical Read এর জন্য
'mysql_master' => [
    'driver'   => 'mysql',
    'host'     => env('DB_MASTER_HOST'),
    'database' => env('DB_DATABASE'),
    'username' => env('DB_USERNAME'),
    'password' => env('DB_PASSWORD'),
],
Enter fullscreen mode Exit fullscreen mode

Replication Lag Handle করা:

php
// Problem: Master এ write করলাম,
// কিন্তু Replica তে এখনো পৌঁছায়নি!
// এই সময়কে বলে Replication Lag

class OrderService
{
    public function createOrder(array $data): Order
    {
        // Master এ Order তৈরি করুন
        $order = Order::create($data);

        // Replication Lag এড়াতে — ID Cache এ রাখুন
        Cache::put(
            "order_exists:{$order->id}",
            true,
            now()->addSeconds(10) // ১০ সেকেন্ড — Lag এর চেয়ে বেশি
        );

        return $order;
    }

    public function getOrder(int $orderId): Order
    {
        // সবে তৈরি হয়েছে? Master থেকে পড়ুন
        if (Cache::has("order_exists:{$orderId}")) {
            return DB::connection('mysql_master')
                     ->table('orders')
                     ->where('id', $orderId)
                     ->first();
        }

        // পুরনো Order — Replica থেকে পড়ুন
        return Order::find($orderId);
    }
}
Enter fullscreen mode Exit fullscreen mode

🔀 PART 4 — Sharding (ডেটা ভাগ করা)

Sharding কী?

যখন একটা DB Server এ ডেটা অনেক বেশি হয়ে যায়, তখন ডেটাকে ভাগ করে একাধিক সার্ভারে রাখা হয়।

Without Sharding:
[১ কোটি User] → [একটা DB] ← অনেক Slow!

With Sharding:
User ID 1-25 লক্ষ    → [DB Shard 1]
User ID 25-50 লক্ষ   → [DB Shard 2]
User ID 50-75 লক্ষ   → [DB Shard 3]
User ID 75-100 লক্ষ  → [DB Shard 4]
Enter fullscreen mode Exit fullscreen mode

Laravel-এ Sharding Implement করা:

php
// app/Services/ShardingService.php
class ShardingService
{
    // Shard গুলোর Connection name
    private array $shards = [
        'db_shard_1',
        'db_shard_2',
        'db_shard_3',
        'db_shard_4',
    ];

    // User ID দিয়ে কোন Shard এ যাবে নির্ধারণ করুন
    public function getShardForUser(int $userId): string
    {
        $shardIndex = $userId % count($this->shards);
        return $this->shards[$shardIndex];
    }

    // Geographic Sharding — দেশ অনুযায়ী
    public function getShardForCountry(string $countryCode): string
    {
        return match(true) {
            in_array($countryCode, ['BD', 'IN', 'PK']) => 'db_shard_asia',
            in_array($countryCode, ['US', 'CA'])       => 'db_shard_na',
            in_array($countryCode, ['GB', 'DE', 'FR']) => 'db_shard_eu',
            default                                    => 'db_shard_global',
        };
    }
}

// Sharded User Repository
class ShardedUserRepository
{
    public function __construct(
        private ShardingService $sharding
    ) {}

    public function find(int $userId): ?object
    {
        $shard = $this->sharding->getShardForUser($userId);

        return DB::connection($shard)
                 ->table('users')
                 ->where('id', $userId)
                 ->first();
    }

    public function create(array $data): object
    {
        // User তৈরি করুন — ID আগে জানি না
        // তাই সব Shard এ sequentially try করুন
        $userId = $this->generateUserId();
        $shard  = $this->sharding->getShardForUser($userId);

        DB::connection($shard)->table('users')->insert([
            'id'         => $userId,
            'name'       => $data['name'],
            'email'      => $data['email'],
            'created_at' => now(),
        ]);

        return $this->find($userId);
    }

    // Cross-Shard Query — সব Shard এ Search করুন
    public function searchByEmail(string $email): ?object
    {
        foreach ($this->sharding->getAllShards() as $shard) {
            $user = DB::connection($shard)
                      ->table('users')
                      ->where('email', $email)
                      ->first();

            if ($user) return $user;
        }

        return null;
    }

    // Parallel Cross-Shard Query — দ্রুত Version
    public function searchAllShards(string $query): Collection
    {
        $results = collect();
        $jobs    = [];

        // সব Shard এ একসাথে Query পাঠান
        foreach ($this->sharding->getAllShards() as $shard) {
            $jobs[] = SearchShard::dispatch($shard, $query);
        }

        // Result collect করুন
        // (Real implementation এ Promise/Async ব্যবহার করুন)
        return $results;
    }
}
Enter fullscreen mode Exit fullscreen mode

🔒 PART 5 — Distributed Locking

সমস্যা — Race Condition:

জন User একস   Product িনত ইছ:

User A: Stock Check   আছ 
User B: Stock Check   আছ    একই সময়ে!
User A: Order Create  Stock 0
User B: Order Create  Stock -1   িপদ!
Enter fullscreen mode Exit fullscreen mode

Solution — Distributed Lock দিয়ে:

php
// app/Services/DistributedLockService.php
class DistributedLockService
{
    private Redis $redis;
    private string $lockPrefix = 'lock:';

    // Lock নিন
    public function acquire(
        string $resource,
        int $ttlSeconds = 30
    ): ?string {
        $lockKey   = $this->lockPrefix . $resource;
        $lockToken = Str::uuid()->toString(); // Unique token

        // SET NX — শুধু তখনই set হবে যদি না থাকে
        $acquired = Redis::set(
            $lockKey,
            $lockToken,
            'EX', $ttlSeconds,  // TTL
            'NX'                // Only if Not Exists
        );

        return $acquired ? $lockToken : null;
    }

    // Lock ছাড়ুন — শুধু যে নিয়েছে সেই ছাড়তে পারবে
    public function release(string $resource, string $token): bool
    {
        $lockKey = $this->lockPrefix . $resource;

        // Lua Script — Atomic operation
        $script = "
            if redis.call('get', KEYS[1]) == ARGV[1] then
                return redis.call('del', KEYS[1])
            else
                return 0
            end
        ";

        return (bool) Redis::eval($script, 1, $lockKey, $token);
    }

    // Lock সহ কাজ করুন — Automatic release
    public function withLock(
        string $resource,
        callable $callback,
        int $ttl = 30
    ): mixed {
        $token = $this->acquire($resource, $ttl);

        if (!$token) {
            throw new LockNotAcquiredException(
                "Resource '{$resource}' is currently locked. Please try again."
            );
        }

        try {
            return $callback();
        } finally {
            $this->release($resource, $token);
        }
    }
}

// ব্যবহার — Product Purchase এ
class PurchaseController extends Controller
{
    public function __construct(
        private DistributedLockService $lockService,
        private OrderService $orderService
    ) {}

    public function purchase(Request $request)
    {
        $productId = $request->product_id;

        try {
            // Product এর জন্য Lock নিন
            $result = $this->lockService->withLock(
                "product:purchase:{$productId}",
                function () use ($request, $productId) {

                    // Lock এর ভেতরে — এখন একজনই আছে
                    $product = Product::lockForUpdate()
                                      ->findOrFail($productId);

                    if ($product->stock < $request->quantity) {
                        throw new InsufficientStockException(
                            "মাত্র {$product->stock}টা বাকি আছে"
                        );
                    }

                    return $this->orderService->create([
                        'product_id' => $productId,
                        'quantity'   => $request->quantity,
                        'user_id'    => auth()->id(),
                    ]);
                },
                ttl: 10 // ১০ সেকেন্ডের মধ্যে শেষ করতে হবে
            );

            return response()->json([
                'success' => true,
                'order'   => $result,
            ]);

        } catch (LockNotAcquiredException $e) {
            return response()->json([
                'success' => false,
                'message' => 'এই মুহূর্তে অনেক চাপ। একটু পরে try করুন।',
            ], 429);

        } catch (InsufficientStockException $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
            ], 422);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

📨 PART 6 — Distributed Message Queue

Queue কেন দরকার Distributed System এ?

সমস  Services সরসরি কথ বলল:
[Order Service]  HTTP  [Email Service]
                           যদি down ?
                           Order fail হয়ে !

সম  Queue ি়ে:
[Order Service]  [Queue]  [Email Service]
                    Email Service down হল
                   Queue  কব, পর 
Enter fullscreen mode Exit fullscreen mode
php
// app/Jobs/ProcessOrderJob.php
class ProcessOrderJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    // Retry Configuration
    public int $tries   = 3;     // ৩ বার try করবে
    public int $timeout = 60;    // ৬০ সেকেন্ডের বেশি না
    public int $backoff = 5;     // Retry এর আগে ৫ সেকেন্ড অপেক্ষা

    public function __construct(
        private Order $order
    ) {}

    public function handle(
        EmailService $emailService,
        InventoryService $inventoryService,
        NotificationService $notificationService
    ): void {
        // ১. Inventory Update করুন
        $inventoryService->reduceStock(
            $this->order->product_id,
            $this->order->quantity
        );

        // ২. Email পাঠান
        $emailService->sendOrderConfirmation($this->order);

        // ৩. Push Notification পাঠান
        $notificationService->notify(
            $this->order->user_id,
            "আপনার Order #{$this->order->id} confirmed!"
        );

        // ৪. Analytics Update করুন
        SyncOrderAnalytics::dispatch($this->order)
                          ->onQueue('analytics') // আলাদা Queue
                          ->delay(now()->addMinutes(1));
    }

    // সব Retry ব্যর্থ হলে
    public function failed(\Throwable $exception): void
    {
        Log::error('Order processing failed', [
            'order_id' => $this->order->id,
            'error'    => $exception->getMessage(),
        ]);

        // Admin কে Alert করুন
        AdminAlert::dispatch(
            "Order #{$this->order->id} processing failed!",
            $exception->getMessage()
        );

        // Order Status Update করুন
        $this->order->update(['status' => 'processing_failed']);
    }
}

// Priority Queue — জরুরি কাজ আগে
class OrderController extends Controller
{
    public function store(Request $request)
    {
        $order = Order::create($request->validated());

        // High Priority — দ্রুত process হবে
        ProcessOrderJob::dispatch($order)
                       ->onQueue('high-priority');

        // Normal Priority
        GenerateInvoice::dispatch($order)
                       ->onQueue('default');

        // Low Priority — পরে হলেও চলবে
        UpdateRecommendations::dispatch($order)
                              ->onQueue('low-priority');

        return response()->json($order, 201);
    }
}

// Queue Worker চালান — আলাদা আলাদা Priority
// php artisan queue:work --queue=high-priority,default,low-priority
Enter fullscreen mode Exit fullscreen mode

🏥 PART 7 — Health Check & Fault Tolerance

php
// app/Http/Controllers/HealthController.php
class HealthController extends Controller
{
    public function check(): JsonResponse
    {
        $checks = [
            'database'   => $this->checkDatabase(),
            'redis'      => $this->checkRedis(),
            'queue'      => $this->checkQueue(),
            'disk'       => $this->checkDisk(),
        ];

        $allHealthy = collect($checks)->every(
            fn($check) => $check['status'] === 'healthy'
        );

        return response()->json([
            'status'    => $allHealthy ? 'healthy' : 'degraded',
            'timestamp' => now()->toIso8601String(),
            'services'  => $checks,
        ], $allHealthy ? 200 : 503);
    }

    private function checkDatabase(): array
    {
        try {
            $start = microtime(true);
            DB::select('SELECT 1');
            $responseTime = round((microtime(true) - $start) * 1000, 2);

            return [
                'status'        => 'healthy',
                'response_time' => "{$responseTime}ms",
            ];
        } catch (\Exception $e) {
            return ['status' => 'unhealthy', 'error' => $e->getMessage()];
        }
    }

    private function checkRedis(): array
    {
        try {
            $start = microtime(true);
            Redis::ping();
            $responseTime = round((microtime(true) - $start) * 1000, 2);

            return [
                'status'        => 'healthy',
                'response_time' => "{$responseTime}ms",
            ];
        } catch (\Exception $e) {
            return ['status' => 'unhealthy', 'error' => $e->getMessage()];
        }
    }

    private function checkQueue(): array
    {
        $failedJobs = DB::table('failed_jobs')->count();

        return [
            'status'      => $failedJobs < 10 ? 'healthy' : 'degraded',
            'failed_jobs' => $failedJobs,
        ];
    }

    private function checkDisk(): array
    {
        $freeSpace  = disk_free_space('/');
        $totalSpace = disk_total_space('/');
        $usedPercent = round((1 - $freeSpace / $totalSpace) * 100, 1);

        return [
            'status'       => $usedPercent < 85 ? 'healthy' : 'critical',
            'used_percent' => "{$usedPercent}%",
            'free_space'   => $this->formatBytes($freeSpace),
        ];
    }
}

// Circuit Breaker — Service বার বার fail করলে বন্ধ করুন
class CircuitBreaker
{
    private string $service;
    private int    $threshold;  // কতবার fail হলে open হবে
    private int    $timeout;    // কতক্ষণ open থাকবে

    // States: closed (normal), open (blocked), half-open (testing)
    public function call(callable $action): mixed
    {
        $state = $this->getState();

        // Open — Service বন্ধ, সরাসরি Error দিন
        if ($state === 'open') {
            if ($this->shouldAttemptReset()) {
                $this->setState('half-open');
            } else {
                throw new ServiceUnavailableException(
                    "{$this->service} is currently unavailable"
                );
            }
        }

        try {
            $result = $action();
            $this->onSuccess(); // Closed State এ ফিরুন
            return $result;

        } catch (\Exception $e) {
            $this->onFailure(); // Failure Count বাড়ান
            throw $e;
        }
    }

    private function onSuccess(): void
    {
        Redis::del("circuit:{$this->service}:failures");
        $this->setState('closed');
    }

    private function onFailure(): void
    {
        $failures = Redis::incr("circuit:{$this->service}:failures");
        Redis::expire("circuit:{$this->service}:failures", $this->timeout);

        if ($failures >= $this->threshold) {
            $this->setState('open');
            Log::warning("Circuit Breaker opened for {$this->service}");
        }
    }

    private function getState(): string
    {
        return Redis::get("circuit:{$this->service}:state") ?? 'closed';
    }

    private function setState(string $state): void
    {
        Redis::setex(
            "circuit:{$this->service}:state",
            $this->timeout,
            $state
        );
    }
}

// ব্যবহার
class ExternalApiService
{
    public function __construct(
        private CircuitBreaker $breaker
    ) {}

    public function getData(): array
    {
        return $this->breaker->call(function () {
            $response = Http::timeout(5)
                            ->get('https://external-api.com/data');

            if ($response->failed()) {
                throw new \Exception('API call failed');
            }

            return $response->json();
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

📝 সংক্ষেপে মনে রাখুন
Distributed Systems এর মূল বিষয়গুলো হলো —
CAP Theorem — Consistency, Availability, Partition Tolerance — যেকোনো দুটো বেছে নিতে হবে।
Consistency — Critical data (Payment) তে Strong, Non-critical (View Count) তে Eventual।
Replication — Master Write করে, Replica Read করে — Performance ও Availability দুটোই বাড়ে।
Sharding — ডেটা অনেক বেশি হলে ভাগ করে রাখুন।
Distributed Lock — Race Condition এড়াতে Redis Lock ব্যবহার করুন।
Circuit Breaker — একটা Service বারবার fail করলে বাকি সিস্টেম রক্ষা করুন।

Laravel এ Redis, Queue, Database Read/Write Split ব্যবহার করে এই সব Concept সহজেই Implement করা যায়।


Q:5 - Caching (Redis, Memcached)
Answer:

Caching কী?
Caching হলো ঘন ঘন ব্যবহৃত ডেটা দ্রুত অ্যাক্সেসের জন্য কাছে রেখে দেওয়া। প্রতিবার Database এ না গিয়ে Cache থেকে ডেটা নিলে সিস্টেম অনেক দ্রুত হয়।

❌ Without Cache:
[User Request] → [Laravel] → [Database] → [Response]
                              ↑ প্রতিবার DB Hit = ধীর, ব্যয়বহুল

✅ With Cache:
[User Request] → [Laravel] → [Cache] → [Response]  ← দ্রুত!
                                ↓ না থাকলে
                           [Database] → [Cache তে রাখো] → [Response]
Enter fullscreen mode Exit fullscreen mode

Cache কতটা দ্রুত?

Database Query  →  10-100ms
Redis Cache     →  0.1-1ms   ← ১০০ গুণ দ্রুত!
Memcached       →  0.1-1ms   ← ১০০ গুণ দ্রুত!
Memory (Array)  →  0.001ms   ← সবচেয়ে দ্রুত কিন্তু Persistent না
Enter fullscreen mode Exit fullscreen mode

🔧 PART 1 — Laravel Cache Setup
Redis Setup:

bash
# Redis Install করুন
composer require predis/predis
# অথবা PHP Redis Extension
pecl install redis
Enter fullscreen mode Exit fullscreen mode
php
// .env
CACHE_DRIVER=redis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
REDIS_DB=0           // Cache এর জন্য
REDIS_CACHE_DB=1     // আলাদা DB — Session এর সাথে মিশবে না

// config/cache.php
'redis' => [
    'driver'     => 'redis',
    'connection' => 'cache',
    'lock_connection' => 'default',
],

// config/database.php — Redis Connections
'redis' => [
    'default' => [
        'host'     => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD', null),
        'port'     => env('REDIS_PORT', 6379),
        'database' => 0, // Queue & General
    ],
    'cache' => [
        'host'     => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD', null),
        'port'     => env('REDIS_PORT', 6379),
        'database' => 1, // Cache আলাদা DB তে
    ],
    'session' => [
        'host'     => env('REDIS_HOST', '127.0.0.1'),
        'password' => env('REDIS_PASSWORD', null),
        'port'     => env('REDIS_PORT', 6379),
        'database' => 2, // Session আলাদা DB তে
    ],
],
Enter fullscreen mode Exit fullscreen mode

Memcached Setup:

bashcomposer require ext-memcached
Enter fullscreen mode Exit fullscreen mode
php
// .env
CACHE_DRIVER=memcached
MEMCACHED_HOST=127.0.0.1
MEMCACHED_PORT=11211

// config/cache.php
'memcached' => [
    'driver'  => 'memcached',
    'servers' => [
        [
            'host'   => env('MEMCACHED_HOST', '127.0.0.1'),
            'port'   => env('MEMCACHED_PORT', 11211),
            'weight' => 100,
        ],
        // Multiple Servers — Load Distribution
        [
            'host'   => env('MEMCACHED_HOST_2', '10.0.0.2'),
            'port'   => 11211,
            'weight' => 100,
        ],
    ],
],
Enter fullscreen mode Exit fullscreen mode

🎯 PART 2 — Cache Strategies (কৌশল)
Strategy 1 — Cache-Aside (Lazy Loading)
সবচেয়ে জনপ্রিয় Pattern। দরকার হলে Cache এ রাখা হয়।

php
// app/Services/ProductService.php
class ProductService
{
    // ১. Basic Cache Remember
    public function getProduct(int $id): Product
    {
        return Cache::remember(
            "product:{$id}",          // Cache Key
            now()->addHours(6),       // Expiry — ৬ ঘন্টা
            fn() => Product::with(['category', 'brand', 'images'])
                            ->findOrFail($id)
        );
    }

    // ২. Cache Forever — খুব কম পরিবর্তন হয় এমন Data
    public function getCategories(): Collection
    {
        return Cache::rememberForever(
            'all_categories',
            fn() => Category::where('is_active', true)
                            ->orderBy('name')
                            ->get()
        );
    }

    // ৩. Cache Tags — Related Cache একসাথে Clear করুন
    public function getProductsByCategory(int $categoryId): Collection
    {
        return Cache::tags(['products', "category:{$categoryId}"])
                    ->remember(
                        "products:category:{$categoryId}",
                        now()->addHours(2),
                        fn() => Product::where('category_id', $categoryId)
                                       ->where('status', 'active')
                                       ->with(['brand', 'images'])
                                       ->orderByDesc('created_at')
                                       ->get()
                    );
    }

    // ৪. Stale-While-Revalidate — Cache Expire হলেও পুরনো দেখাও
    //    Background এ নতুন করো
    public function getPopularProducts(): Collection
    {
        $cacheKey = 'popular_products';

        // Cache আছে? → দিন
        if ($cached = Cache::get($cacheKey)) {

            // Cache প্রায় শেষ? → Background এ refresh করুন
            $ttl = Cache::get("{$cacheKey}:ttl");
            if ($ttl && $ttl < now()->addMinutes(5)->timestamp) {
                RefreshPopularProductsCache::dispatch();
            }

            return $cached;
        }

        // Cache নেই → DB থেকে নিন
        return $this->refreshPopularProductsCache();
    }

    public function refreshPopularProductsCache(): Collection
    {
        $products = Product::withCount('orders')
                           ->orderByDesc('orders_count')
                           ->take(20)
                           ->get();

        Cache::put('popular_products', $products, now()->addHour());
        Cache::put('popular_products:ttl', now()->addHour()->timestamp, now()->addHour());

        return $products;
    }
}
Enter fullscreen mode Exit fullscreen mode

Strategy 2 — Write-Through Cache
Data লেখার সময় একসাথে Cache ও Database Update করা।

php
class ProductRepository
{
    // Create — DB ও Cache একসাথে
    public function create(array $data): Product
    {
        $product = Product::create($data);

        // সাথে সাথে Cache এ রাখুন
        Cache::put(
            "product:{$product->id}",
            $product->load(['category', 'brand']),
            now()->addHours(6)
        );

        // Category Cache Clear করুন
        Cache::tags(["category:{$product->category_id}"])->flush();
        Cache::forget('popular_products');

        return $product;
    }

    // Update — DB ও Cache একসাথে
    public function update(int $id, array $data): Product
    {
        $product = Product::findOrFail($id);
        $oldCategoryId = $product->category_id;

        $product->update($data);
        $product->refresh()->load(['category', 'brand', 'images']);

        // Cache Update করুন
        Cache::put("product:{$id}", $product, now()->addHours(6));

        // Related Cache Clear করুন
        Cache::tags(["category:{$oldCategoryId}"])->flush();

        if ($product->category_id !== $oldCategoryId) {
            Cache::tags(["category:{$product->category_id}"])->flush();
        }

        return $product;
    }

    // Delete — DB ও Cache একসাথে
    public function delete(int $id): bool
    {
        $product = Product::findOrFail($id);

        // Cache গুলো Clear করুন
        Cache::forget("product:{$id}");
        Cache::tags(["category:{$product->category_id}", 'products'])->flush();

        return $product->delete();
    }
}
Enter fullscreen mode Exit fullscreen mode

Strategy 3 — Write-Behind Cache (Lazy Write)
Cache এ লিখুন — Database এ পরে Sync করুন। অনেক দ্রুত।

php
// Counter বা Analytics এর জন্য দারুণ
class AnalyticsService
{
    // ১. View Count — Cache এ রাখুন, পরে DB Sync
    public function incrementView(int $productId): void
    {
        $key = "product:{$productId}:views:pending";

        // Redis এ Increment করুন — DB তে যাবে না এখন
        Redis::incr($key);
        Redis::expire($key, 3600);

        // ৫ মিনিট পর DB Sync করুন
        SyncViewCount::dispatch($productId)
                     ->delay(now()->addMinutes(5))
                     ->onQueue('analytics');
    }

    // ২. Like/Dislike — Batch এ DB তে লিখুন
    public function toggleLike(int $productId, int $userId): array
    {
        $likeKey    = "product:{$productId}:likes";
        $dislikeKey = "product:{$productId}:dislikes";
        $userLikeKey = "user:{$userId}:liked_products";

        // User আগে Like দিয়েছে?
        $hasLiked = Redis::sismember($userLikeKey, $productId);

        if ($hasLiked) {
            // Unlike করুন
            Redis::srem($userLikeKey, $productId);
            Redis::decr($likeKey);
            $action = 'unliked';
        } else {
            // Like করুন
            Redis::sadd($userLikeKey, $productId);
            Redis::incr($likeKey);
            $action = 'liked';
        }

        // Background এ DB Sync করুন
        SyncProductLikes::dispatch($productId, $userId, $action)
                        ->onQueue('analytics');

        return [
            'action' => $action,
            'likes'  => (int) Redis::get($likeKey),
        ];
    }
}

// Background Sync Job
class SyncViewCount implements ShouldQueue
{
    public function __construct(private int $productId) {}

    public function handle(): void
    {
        $key   = "product:{$this->productId}:views:pending";
        $views = Redis::getdel($key); // Get and Delete atomically

        if ($views) {
            Product::where('id', $this->productId)
                   ->increment('view_count', (int) $views);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

🏗️ PART 3 — Cache Key Design (গুরুত্বপূর্ণ!)

php
// app/Services/CacheKeyService.php
class CacheKey
{
    // ভালো Cache Key Design — Hierarchical
    const VERSION = 'v1'; // Schema change হলে version বাড়ান

    // Product Keys
    public static function product(int $id): string
    {
        return self::VERSION . ":product:{$id}";
    }

    public static function productList(array $filters = []): string
    {
        $filterHash = md5(serialize($filters)); // Filters এর Hash
        return self::VERSION . ":products:list:{$filterHash}";
    }

    public static function productsByCategory(int $catId, int $page = 1): string
    {
        return self::VERSION . ":products:cat:{$catId}:page:{$page}";
    }

    // User Keys
    public static function userProfile(int $userId): string
    {
        return self::VERSION . ":user:{$userId}:profile";
    }

    public static function userCart(int $userId): string
    {
        return self::VERSION . ":user:{$userId}:cart";
    }

    public static function userPermissions(int $userId): string
    {
        return self::VERSION . ":user:{$userId}:permissions";
    }

    // Config Keys
    public static function siteSettings(): string
    {
        return self::VERSION . ":config:site_settings";
    }
}

// ব্যবহার
Cache::remember(CacheKey::product($id), 3600, fn() => Product::find($id));
Cache::remember(CacheKey::userCart($userId), 1800, fn() => Cart::forUser($userId));

// ❌ ভুল Cache Key Design
Cache::remember('product', ...);          // Collision!
Cache::remember("product_$id", ...);      // Version নেই
Cache::remember("p_{$id}", ...);          // বোঝা যাচ্ছে না
Enter fullscreen mode Exit fullscreen mode

🔴 PART 4 — Redis Advanced Features
Redis Data Structures — Laravel এ:

php
// ১. String — Simple Value Store
Redis::set('app:version', '2.5.1');
Redis::setex('otp:01712345678', 300, '123456'); // ৫ মিনিট
$otp = Redis::get('otp:01712345678');

// ২. Hash — Object Store (User Session এর মতো)
Redis::hmset("cart:{$userId}", [
    'items_count' => 5,
    'total'       => 2500,
    'coupon'      => 'SAVE20',
    'updated_at'  => now()->timestamp,
]);

// একটা field update
Redis::hset("cart:{$userId}", 'items_count', 6);

// সব field পড়ুন
$cart = Redis::hgetall("cart:{$userId}");

// ৩. List — Queue / Recent Items
// Left Push — নতুন item সামনে
Redis::lpush("user:{$userId}:recent_products", $productId);

// List এর size limit — শুধু ১০টা রাখুন
Redis::ltrim("user:{$userId}:recent_products", 0, 9);

// সব recent products
$recentProducts = Redis::lrange("user:{$userId}:recent_products", 0, -1);

// ৪. Set — Unique Values (Tags, Followers)
// Product Tags
Redis::sadd("product:{$productId}:tags", 'smartphone', 'apple', '5g');

// User Wishlist
Redis::sadd("user:{$userId}:wishlist", $productId);
Redis::srem("user:{$userId}:wishlist", $productId); // Remove

// Wishlist এ আছে?
$inWishlist = Redis::sismember("user:{$userId}:wishlist", $productId);

// Common Products (Intersection) — দুজনের Common Wishlist
$commonWishlist = Redis::sinter(
    "user:{$user1Id}:wishlist",
    "user:{$user2Id}:wishlist"
);

// ৫. Sorted Set — Leaderboard / Ranking
// Sales Score যোগ করুন
Redis::zadd('leaderboard:products:weekly', [
    'iPhone 15'   => 1500,
    'Samsung S24' => 1200,
    'MacBook Pro' => 800,
]);

// Score বাড়ান
Redis::zincrby('leaderboard:products:weekly', 10, 'iPhone 15');

// Top 10 Products
$topProducts = Redis::zrevrange(
    'leaderboard:products:weekly',
    0, 9,
    'WITHSCORES'
);

// Rank জানুন
$rank = Redis::zrevrank('leaderboard:products:weekly', 'iPhone 15');

// ৬. HyperLogLog — Unique Visitor Count (Memory দক্ষ)
// প্রতিটা Visitor এর IP যোগ করুন
Redis::pfadd("page:{$pageId}:visitors:today", $request->ip());

// Approximate unique count (৯৯.৫% accurate, মাত্র ১২KB memory!)
$uniqueVisitors = Redis::pfcount("page:{$pageId}:visitors:today");

// ৭. Pub/Sub — Real-time Notification
// Publisher
Redis::publish('notifications', json_encode([
    'user_id' => $userId,
    'message' => 'আপনার Order Confirm হয়েছে!',
    'type'    => 'order_confirmed',
]));

// Subscriber (Artisan Command হিসেবে চালান)
Redis::subscribe(['notifications'], function ($message) {
    $data = json_decode($message, true);
    // WebSocket দিয়ে User কে পাঠান
    broadcast(new UserNotification($data));
});
Enter fullscreen mode Exit fullscreen mode

Redis Pipeline — Multiple Commands একসাথে:

php
// ❌ ধীর — প্রতিটা Command আলাদা Network Trip
Redis::set('key1', 'value1');
Redis::set('key2', 'value2');
Redis::set('key3', 'value3');
Redis::expire('key1', 3600);
Redis::expire('key2', 3600);

// ✅ দ্রুত — একটাই Network Trip এ সব
Redis::pipeline(function ($pipe) {
    $pipe->set('key1', 'value1');
    $pipe->set('key2', 'value2');
    $pipe->set('key3', 'value3');
    $pipe->expire('key1', 3600);
    $pipe->expire('key2', 3600);
});

// Real Example — User Login Cache
public function cacheUserData(User $user): void
{
    Redis::pipeline(function ($pipe) use ($user) {
        // User Profile
        $pipe->setex(
            "user:{$user->id}:profile",
            3600,
            json_encode($user->toArray())
        );

        // Permissions
        $pipe->setex(
            "user:{$user->id}:permissions",
            3600,
            json_encode($user->getAllPermissions()->pluck('name'))
        );

        // Last Login Update
        $pipe->hset(
            "user:{$user->id}:meta",
            'last_login',
            now()->timestamp
        );

        // Online Status
        $pipe->setex("user:{$user->id}:online", 300, 1);
    });
}
Enter fullscreen mode Exit fullscreen mode

Redis Lua Script — Atomic Operations:

php
// Atomic Stock Deduction — Race Condition ছাড়া
class AtomicStockService
{
    public function deductStock(int $productId, int $quantity): bool
    {
        $script = "
            local stock = tonumber(redis.call('get', KEYS[1]))
            if stock == nil then
                return -1  -- Stock Cache নেই
            end
            if stock < tonumber(ARGV[1]) then
                return 0   -- Stock কম
            end
            redis.call('decrby', KEYS[1], ARGV[1])
            return 1       -- সফল
        ";

        $result = Redis::eval(
            $script,
            1,                              // Key count
            "product:{$productId}:stock",   // KEYS[1]
            $quantity                       // ARGV[1]
        );

        return match($result) {
            1  => true,
            0  => throw new InsufficientStockException(),
            -1 => $this->deductFromDatabase($productId, $quantity),
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

🔵 PART 5 — Memcached

Redis vs Memcached কখন কোনটা?

Redis                          Memcached
─────────────────────          ─────────────────────
Data Structures আছ      vs    Key-Value
Persistence আছ          vs   Memory Only (Restart = Data )
Pub/Sub আছ              vs   
Replication আছ          vs   
ি Features            vs   Simple  
Cache + Queue + Session  vs    Cache
Enter fullscreen mode Exit fullscreen mode

কখন Memcached বেছে নেবেন:
✓ শুধু Simple Caching দরকার
✓ Multi-threaded Performance দরকার
✓ Very Large Cache (কয়েক TB)
✓ Simple Setup চান

php
// Memcached Operations — Laravel এ একই Cache Facade
Cache::store('memcached')->put('key', 'value', 3600);
Cache::store('memcached')->get('key');
Cache::store('memcached')->forget('key');

// Multiple Servers — Consistent Hashing
// config/cache.php
'memcached' => [
    'driver'        => 'memcached',
    'persistent_id' => env('MEMCACHED_PERSISTENT_ID'),
    'sasl'          => [
        env('MEMCACHED_USERNAME'),
        env('MEMCACHED_PASSWORD'),
    ],
    'options' => [
        // Consistent Hashing — Server যোগ/বাদ দিলে কম Cache Miss
        Memcached::OPT_DISTRIBUTION => Memcached::DISTRIBUTION_CONSISTENT,
        Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
    ],
    'servers' => [
        ['host' => '10.0.0.1', 'port' => 11211, 'weight' => 100],
        ['host' => '10.0.0.2', 'port' => 11211, 'weight' => 100],
        ['host' => '10.0.0.3', 'port' => 11211, 'weight' => 100],
    ],
],
Enter fullscreen mode Exit fullscreen mode

🛡️ PART 6 — Cache Problems & Solutions

১. Cache Stampede (Thunder Herd Problem):

সমস: Cache Expire হল   Request একস DB    DB Crash!

                Cache Expire!
                     
[1000 Requests] ──→ [DB]  অন !
Enter fullscreen mode Exit fullscreen mode
php
// Solution 1 — Mutex/Lock ব্যবহার করুন
class CacheStampedeProtection
{
    public function remember(string $key, int $ttl, callable $callback): mixed
    {
        // Cache আছে? দিন
        if ($value = Cache::get($key)) {
            return $value;
        }

        // Lock নিন — শুধু একজন DB তে যাবে
        $lock = Cache::lock("lock:{$key}", 10);

        try {
            if ($lock->get()) {
                // আমি Lock পেয়েছি — DB থেকে নিই
                $value = $callback();
                Cache::put($key, $value, $ttl);
                return $value;
            }
        } finally {
            $lock->release();
        }

        // Lock পাইনি — একটু অপেক্ষা করে আবার চেক করুন
        sleep(1);
        return Cache::get($key) ?? $callback();
    }
}

// Solution 2 — Probabilistic Early Expiration
class EarlyExpirationCache
{
    public function get(string $key, int $ttl, callable $callback): mixed
    {
        $cached = Cache::get($key);

        if ($cached) {
            // TTL এর ১০% বাকি থাকলে ১% chance এ early refresh
            $remainingTtl = Redis::ttl($key);
            $shouldRefresh = $remainingTtl < ($ttl * 0.1)
                          && random_int(1, 100) === 1;

            if (!$shouldRefresh) {
                return $cached;
            }
        }

        $value = $callback();
        Cache::put($key, $value, $ttl);
        return $value;
    }
}
Enter fullscreen mode Exit fullscreen mode

২. Cache Penetration (DB Attack):

সমস: Hacker রব Non-existent Key ি়ে Request করছ
 Cache Miss  DB Hit  DB Overwhelmed!

GET /api/products/99999999   এই Product !
GET /api/products/88888888   এট !
... লকষব!
Enter fullscreen mode Exit fullscreen mode
php
// Solution — Null Value Cache করুন
class ProductService
{
    public function getProduct(int $id): ?Product
    {
        $cacheKey = "product:{$id}";

        $cached = Cache::get($cacheKey);

        // Null String মানে আগে চেক করা হয়েছে, নেই
        if ($cached === 'NULL') {
            return null;
        }

        if ($cached !== null) {
            return $cached;
        }

        // DB তে খুঁজুন
        $product = Product::find($id);

        if ($product) {
            Cache::put($cacheKey, $product, now()->addHours(6));
        } else {
            // নেই → Null Cache করুন — ৫ মিনিটের জন্য
            Cache::put($cacheKey, 'NULL', now()->addMinutes(5));
        }

        return $product;
    }
}

// Bloom Filter — আরো ভালো Solution
// সব Valid Product ID Redis এ রাখুন
class BloomFilterService
{
    public function productExists(int $productId): bool
    {
        // Bloom Filter Check — O(1) time
        return Redis::getbit("bloom:products", $productId % 10000000);
    }

    public function addProduct(int $productId): void
    {
        Redis::setbit("bloom:products", $productId % 10000000, 1);
    }
}

// Middleware তে ব্যবহার করুন
public function getProduct(int $id, BloomFilterService $bloom): JsonResponse
{
    // Bloom Filter এ নেই → সরাসরি 404, DB তে যাবে না
    if (!$bloom->productExists($id)) {
        return response()->json(['message' => 'Not found'], 404);
    }

    return response()->json($this->productService->getProduct($id));
}
Enter fullscreen mode Exit fullscreen mode

৩. Cache Avalanche (একসাথে অনেক Cache Expire):

সমস:  ১২ সব Cache Expire হল
 সব Request DB    DB Down!
Enter fullscreen mode Exit fullscreen mode

php
// Solution — TTL তে Random Jitter যোগ করুন
class CacheService
{
// সব Cache একসাথে Expire না হওয়ার জন্য
public function rememberWithJitter(
string $key,
int $baseTtl,
callable $callback,
int $jitterPercent = 20
): mixed {
// Base TTL এর ±20% Random করুন
$jitter = (int) ($baseTtl * $jitterPercent / 100);
$ttl = $baseTtl + random_int(-$jitter, $jitter);

    return Cache::remember($key, $ttl, $callback);
}
Enter fullscreen mode Exit fullscreen mode

}

// ব্যবহার
// ৩৬০০ সেকেন্ড (±২০%) = ২৮৮০ থেকে ৪৩২০ সেকেন্ডের মধ্যে যেকোনো সময় expire
$product = $cacheService->rememberWithJitter(
"product:{$id}",
3600,
fn() => Product::find($id)
);


---

📊 **PART 7  Full Cache Layer  Real Project**
Enter fullscreen mode Exit fullscreen mode

php
// app/Services/CacheLayerService.php
// সব ধরনের Cache একটা Service এ

class CacheLayerService
{
// L1: Array Cache (Request scope — সবচেয়ে দ্রুত)
private array $requestCache = [];

// L2: Redis (Application scope)
// L3: Database (সবচেয়ে ধীর)

public function getProduct(int $id): ?Product
{
    // L1 — Same Request এ আগে দেখা? Array থেকে নিন
    if (isset($this->requestCache["product:{$id}"])) {
        return $this->requestCache["product:{$id}"];
    }

    // L2 — Redis Cache তে আছে?
    $product = Cache::remember(
        CacheKey::product($id),
        now()->addHours(6),
        function () use ($id) {
            // L3 — Database থেকে নিন
            return Product::with(['category', 'brand', 'images'])
                          ->find($id);
        }
    );

    // L1 এ রাখুন — এই Request এ আবার লাগলে দ্রুত পাবো
    $this->requestCache["product:{$id}"] = $product;

    return $product;
}

// Dashboard Cache — Multiple Data একসাথে
public function getDashboardData(int $userId): array
{
    return Cache::remember(
        "dashboard:user:{$userId}",
        now()->addMinutes(15),
        function () use ($userId) {
            // সব Data একসাথে Load করুন
            return [
                'total_orders'    => Order::where('user_id', $userId)->count(),
                'pending_orders'  => Order::where('user_id', $userId)
                                          ->where('status', 'pending')->count(),
                'total_spent'     => Order::where('user_id', $userId)
                                          ->where('status', 'completed')
                                          ->sum('total'),
                'wishlist_count'  => Wishlist::where('user_id', $userId)->count(),
                'recent_orders'   => Order::where('user_id', $userId)
                                          ->with('items.product')
                                          ->latest()
                                          ->take(5)
                                          ->get(),
                'recommended'     => $this->getRecommendations($userId),
            ];
        }
    );
}

// Cache Warming — Deploy এর পরে Cache গরম করুন
public function warmCache(): void
{
    // Popular Products Cache করুন
    $popularProducts = Product::withCount('orders')
                              ->orderByDesc('orders_count')
                              ->take(100)
                              ->get();

    foreach ($popularProducts as $product) {
        Cache::put(
            CacheKey::product($product->id),
            $product->load(['category', 'brand']),
            now()->addHours(6)
        );
    }

    // Categories Cache করুন
    Cache::put(
        'all_categories',
        Category::where('is_active', true)->get(),
        now()->addDay()
    );

    Log::info('Cache warmed successfully', [
        'products_cached' => $popularProducts->count()
    ]);
}
Enter fullscreen mode Exit fullscreen mode

}

// Artisan Command — Deploy এর পরে Cache Warm করুন
class WarmCacheCommand extends Command
{
protected $signature = 'cache:warm';
protected $description = 'Warm application cache after deployment';

public function handle(CacheLayerService $cacheService): void
{
    $this->info('Cache warming শুরু হচ্ছে...');

    $start = microtime(true);
    $cacheService->warmCache();
    $time  = round(microtime(true) - $start, 2);

    $this->info("✅ Cache warming সম্পন্ন! সময় লেগেছে: {$time}s");
}
Enter fullscreen mode Exit fullscreen mode

}
// চালান: php artisan cache:warm


---

📈 **PART 8 — Cache Monitoring**
Enter fullscreen mode Exit fullscreen mode

php
// Cache Hit Rate Monitor করুন
class CacheMonitorMiddleware
{
public function handle(Request $request, Closure $next): Response
{
// Cache Event Listen করুন
Event::listen(CacheHit::class, function ($event) {
Redis::incr('metrics:cache:hits');
});

    Event::listen(CacheMissed::class, function ($event) {
        Redis::incr('metrics:cache:misses');
    });

    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

}

// Cache Stats দেখুন
class CacheStatsController extends Controller
{
public function stats(): JsonResponse
{
$hits = (int) Redis::get('metrics:cache:hits') ?? 0;
$misses = (int) Redis::get('metrics:cache:misses') ?? 0;
$total = $hits + $misses;

    $info   = Redis::info('memory');

    return response()->json([
        'hit_rate'    => $total > 0
                          ? round($hits / $total * 100, 2) . '%'
                          : '0%',
        'total_hits'  => $hits,
        'total_misses'=> $misses,
        'memory_used' => $info['used_memory_human'],
        'memory_peak' => $info['used_memory_peak_human'],
    ]);
}
Enter fullscreen mode Exit fullscreen mode

}


---

📝 **সংক্ষেপে মনে রাখুন**
**Cache কেন** — Database চাপ কমাতে এবং Response দ্রুত করতে। 
**Redis** — Cache ছাড়াও Queue, Session, Pub/Sub, Leaderboard সব করা যায় — Laravel এ সবচেয়ে জনপ্রিয়। 
**Memcached** — শুধু Simple Caching দরকার হলে এবং Multi-threaded Performance দরকার হলে। 
**Cache Strategies** — Cache-Aside সবচেয়ে জনপ্রিয়, Write-Through Critical Data এর জন্য, Write-Behind Analytics এর জন্য। 
**সমস্যাগুলো** — Stampede (Lock দিয়ে), Penetration (Null Cache দিয়ে), Avalanche (Jitter দিয়ে) সমাধান করুন।
Real Project এ **Redis** ব্যবহার করুন — এটাই যথেষ্ট। Cache Key Design ভালো রাখুন এবং Cache Invalidation সতর্কভাবে করুন।

**_Q:6_** - Security (OAuth2, JWT, encryption)
**Answer:**

🔐 **Security কেন গুরুত্বপূর্ণ?**

Enter fullscreen mode Exit fullscreen mode

একটা Security Breach এর পরিণাম:
├── User এর Personal Data চুরি
├── Financial Loss
├── Company এর Reputation নষ্ট
├── Legal Penalty (GDPR Fine)
└── Service Down

বাস্তব উদাহরণ:
2013 → Adobe: 38 মিলিয়ন User Data চুরি
2016 → Uber: 57 মিলিয়ন User Data চুরি
2021 → Facebook: 533 মিলিয়ন User Data Leak


---

🔵 **PART 1 — Authentication vs Authorization**

Enter fullscreen mode Exit fullscreen mode

Authentication (প্রমাণ) Authorization (অনুমতি)
──────────────────────── ────────────────────────
"তুমি কে?" vs "তুমি কী করতে পারবে?"

Login করা vs Admin Panel Access
Password Check vs Role Permission Check
JWT Token Verify vs Policy Check

উদাহরণ:
আপনি Bank এ গেলেন → Authentication (NID দেখালেন)
Locker Room এ গেলেন → Authorization (আপনার Locker নম্বর আছে)


---

🟡 **PART 2 — JWT (JSON Web Token)**
**JWT কী?**
JWT হলো একটা **Self-contained Token** — যেখানে User এর তথ্য **Encoded** থাকে। Server কে Database এ যেতে হয় না Token Verify করতে।

Enter fullscreen mode Exit fullscreen mode

JWT Structure — তিনটা Part, Dot দিয়ে আলাদা:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ← Header (Base64)
.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6InRlc3Q ← Payload (Base64)
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV ← Signature (HMAC)

Header: {"alg": "HS256", "typ": "JWT"}
Payload: {"user_id": 1, "email": "test@example.com", "exp": 1700000000}
Signature: HMACSHA256(header + payload, SECRET_KEY)

**Laravel-এ JWT Implementation:**

Enter fullscreen mode Exit fullscreen mode

bash
composer require php-open-source-saver/jwt-auth
php artisan vendor:publish --provider="PHPOpenSourceSaver\JWTAuth\Providers\LaravelServiceProvider"
php artisan jwt:secret


Enter fullscreen mode Exit fullscreen mode

php
// .env
JWT_SECRET=your-super-secret-key-minimum-32-characters
JWT_TTL=60 // Access Token — ৬০ মিনিট
JWT_REFRESH_TTL=20160 // Refresh Token — ১৪ দিন

// config/auth.php
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],

**JWT Auth Controller  Complete Implementation:**

Enter fullscreen mode Exit fullscreen mode

php
// app/Http/Controllers/Api/AuthController.php
class AuthController extends Controller
{
// ১. Register
public function register(RegisterRequest $request): JsonResponse
{
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password),
]);

    // Token Generate করুন
    $token = auth('api')->login($user);

    return $this->respondWithToken($token, $user, 201);
}

// ২. Login
public function login(LoginRequest $request): JsonResponse
{
    $credentials = $request->only('email', 'password');

    // Brute Force Protection
    if ($this->hasTooManyLoginAttempts($request)) {
        return response()->json([
            'message' => 'অনেকবার চেষ্টা করেছেন। ১৫ মিনিট পরে try করুন।',
            'retry_after' => $this->limiter->availableIn(
                $this->throttleKey($request)
            ),
        ], 429);
    }

    if (!$token = auth('api')->attempt($credentials)) {
        // Failed Attempt Count বাড়ান
        $this->incrementLoginAttempts($request);

        return response()->json([
            'message' => 'Email বা Password ভুল।',
        ], 401);
    }

    // Success — Attempt Reset করুন
    $this->clearLoginAttempts($request);

    // Login Log রাখুন
    $this->logLoginActivity($request);

    return $this->respondWithToken($token);
}

// ৩. Logout
public function logout(): JsonResponse
{
    // Token Blacklist এ রাখুন
    auth('api')->logout();

    return response()->json([
        'message' => 'সফলভাবে Logout হয়েছেন।'
    ]);
}

// ৪. Token Refresh
public function refresh(): JsonResponse
{
    try {
        $newToken = auth('api')->refresh();
        return $this->respondWithToken($newToken);

    } catch (TokenExpiredException $e) {
        return response()->json([
            'message' => 'Refresh Token Expired। আবার Login করুন।'
        ], 401);
    }
}

// ৫. Current User
public function me(): JsonResponse
{
    $user = auth('api')->user();

    return response()->json([
        'user' => new UserResource($user),
    ]);
}

// Token Response Format
private function respondWithToken(
    string $token,
    ?User $user = null,
    int $status = 200
): JsonResponse {
    $user ??= auth('api')->user();

    return response()->json([
        'access_token' => $token,
        'token_type'   => 'Bearer',
        'expires_in'   => auth('api')->factory()->getTTL() * 60,
        'user'         => new UserResource($user),
    ], $status);
}

// Login Activity Log
private function logLoginActivity(Request $request): void
{
    LoginActivity::create([
        'user_id'    => auth('api')->id(),
        'ip_address' => $request->ip(),
        'user_agent' => $request->userAgent(),
        'location'   => $this->getLocationFromIp($request->ip()),
        'logged_at'  => now(),
    ]);
}
Enter fullscreen mode Exit fullscreen mode

}

**JWT Middleware — Token Verify করুন:**

Enter fullscreen mode Exit fullscreen mode

php
// app/Http/Middleware/JwtMiddleware.php
class JwtMiddleware
{
public function handle(Request $request, Closure $next): Response
{
try {
// Token থেকে User নিন
$user = JWTAuth::parseToken()->authenticate();

        if (!$user) {
            return response()->json([
                'message' => 'User not found'
            ], 401);
        }

        // User Active আছে?
        if (!$user->is_active) {
            return response()->json([
                'message' => 'আপনার Account Suspended।'
            ], 403);
        }

    } catch (TokenExpiredException $e) {
        return response()->json([
            'message' => 'Token Expired। Refresh করুন।',
            'error'   => 'token_expired',
        ], 401);

    } catch (TokenInvalidException $e) {
        return response()->json([
            'message' => 'Token Invalid।',
            'error'   => 'token_invalid',
        ], 401);

    } catch (JWTException $e) {
        return response()->json([
            'message' => 'Token দেওয়া হয়নি।',
            'error'   => 'token_absent',
        ], 401);
    }

    return $next($request);
}
Enter fullscreen mode Exit fullscreen mode

}

**Access Token + Refresh Token Pattern:**
Enter fullscreen mode Exit fullscreen mode

php
// app/Services/TokenService.php
class TokenService
{
// Short-lived Access Token + Long-lived Refresh Token
public function generateTokenPair(User $user): array
{
// Access Token — ১৫ মিনিট
$accessToken = JWTAuth::customClaims([
'type' => 'access',
'user_id' => $user->id,
])->fromUser($user);

    // Refresh Token — ৩০ দিন (DB তে Store করুন)
    $refreshToken = Str::random(64);

    // DB তে Refresh Token Store করুন
    RefreshToken::create([
        'user_id'    => $user->id,
        'token'      => hash('sha256', $refreshToken), // Hash করে রাখুন
        'expires_at' => now()->addDays(30),
        'ip_address' => request()->ip(),
    ]);

    return [
        'access_token'  => $accessToken,
        'refresh_token' => $refreshToken,
        'expires_in'    => 900, // ১৫ মিনিট
    ];
}

// Refresh Token দিয়ে নতুন Access Token নিন
public function refreshAccessToken(string $refreshToken): array
{
    $hashedToken = hash('sha256', $refreshToken);

    $storedToken = RefreshToken::where('token', $hashedToken)
                               ->where('expires_at', '>', now())
                               ->whereNull('revoked_at')
                               ->with('user')
                               ->firstOrFail();

    // পুরনো Refresh Token Revoke করুন (Rotation)
    $storedToken->update(['revoked_at' => now()]);

    // নতুন Token Pair Generate করুন
    return $this->generateTokenPair($storedToken->user);
}

// Logout — সব Token Revoke করুন
public function revokeAllTokens(int $userId): void
{
    RefreshToken::where('user_id', $userId)
                ->whereNull('revoked_at')
                ->update(['revoked_at' => now()]);

    JWTAuth::invalidate(JWTAuth::getToken());
}
Enter fullscreen mode Exit fullscreen mode

}


---

🟠 **PART 3  OAuth2**

**OAuth2 ?**

OAuth2 হল **Third-party Authorization** এর Standard "Google দিয়ে Login"  "Facebook দিয়ে Login"  এট OAuth2
Enter fullscreen mode Exit fullscreen mode

OAuth2 Flow:

[User] → "Google দিয়ে Login" → [Your App]

[Google Auth Server]
↓ (User Allow করলো)
Authorization Code

[Your App এ Code পাঠালো]

Code দিয়ে Access Token নিন

Access Token দিয়ে User Info নিন

[User Logged In!]

**Laravel Passport  Full OAuth2 Server:**

Enter fullscreen mode Exit fullscreen mode

bash
composer require laravel/passport
php artisan passport:install
php artisan passport:keys

Enter fullscreen mode Exit fullscreen mode

php
// app/Models/User.php
class User extends Authenticatable
{
use HasApiTokens; // Passport এর জন্য
}

// app/Providers/AppServiceProvider.php
public function boot(): void
{
// Token Expiry Configure করুন
Passport::tokensExpireIn(now()->addMinutes(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::personalAccessTokensExpireIn(now()->addMonths(6));

// Scopes Define করুন — Permission Level
Passport::tokensCan([
    'read-products'   => 'Products দেখার অনুমতি',
    'write-products'  => 'Products তৈরি/edit করার অনুমতি',
    'read-orders'     => 'Orders দেখার অনুমতি',
    'write-orders'    => 'Orders তৈরি করার অনুমতি',
    'admin'           => 'সব কিছু করার অনুমতি',
]);
Enter fullscreen mode Exit fullscreen mode

}

**OAuth2 Client Credentials (Machine to Machine):**

Enter fullscreen mode Exit fullscreen mode


php
php
// routes/api.php
Route::middleware('client')->group(function () {
// শুধু Authenticated Client Access করতে পারবে
Route::get('/internal/products', [InternalProductController::class, 'index']);
Route::post('/internal/sync', [SyncController::class, 'sync']);
});

// Client Credentials Grant — Service to Service Auth
class InternalSyncService
{
private ?string $accessToken = null;

public function getToken(): string
{
    // Cache করুন — বারবার নেওয়া লাগবে না
    if ($this->accessToken && !$this->isTokenExpired()) {
        return $this->accessToken;
    }

    $response = Http::post(config('services.auth.url') . '/oauth/token', [
        'grant_type'    => 'client_credentials',
        'client_id'     => config('services.auth.client_id'),
        'client_secret' => config('services.auth.client_secret'),
        'scope'         => 'read-products write-products',
    ]);

    $this->accessToken = $response->json('access_token');

    Cache::put(
        'service_access_token',
        $this->accessToken,
        now()->addMinutes(14) // Expire এর ১ মিনিট আগে
    );

    return $this->accessToken;
}

public function syncProducts(): void
{
    $token = $this->getToken();

    Http::withToken($token)
        ->post(config('services.inventory.url') . '/internal/sync', [
            'products' => Product::all()->toArray()
        ]);
}
Enter fullscreen mode Exit fullscreen mode

}

**Social Login — Google/Facebook দিয়ে Login:**

Enter fullscreen mode Exit fullscreen mode


php
bash
composer require laravel/socialite

Enter fullscreen mode Exit fullscreen mode


shell
php
// config/services.php
'google' => [
'client_id' => env('GOOGLE_CLIENT_ID'),
'client_secret' => env('GOOGLE_CLIENT_SECRET'),
'redirect' => env('GOOGLE_REDIRECT_URI'),
],

'facebook' => [
'client_id' => env('FACEBOOK_APP_ID'),
'client_secret' => env('FACEBOOK_APP_SECRET'),
'redirect' => env('FACEBOOK_REDIRECT_URI'),
],

// app/Http/Controllers/Auth/SocialAuthController.php
class SocialAuthController extends Controller
{
// ১. Google/Facebook এ Redirect করুন
public function redirect(string $provider): RedirectResponse
{
$validProviders = ['google', 'facebook', 'github'];

    if (!in_array($provider, $validProviders)) {
        return response()->json(['message' => 'Invalid provider'], 400);
    }

    return Socialite::driver($provider)->redirect();
}

// ২. Callback — Google/Facebook থেকে ফিরে আসলে
public function callback(string $provider): JsonResponse
{
    try {
        $socialUser = Socialite::driver($provider)->user();

    } catch (\Exception $e) {
        return response()->json([
            'message' => 'Social Login ব্যর্থ হয়েছে।'
        ], 401);
    }

    // Email দিয়ে User খুঁজুন
    $user = User::where('email', $socialUser->getEmail())->first();

    if ($user) {
        // আগে Register করেছে → Social Account Link করুন
        $user->socialAccounts()->updateOrCreate(
            ['provider' => $provider],
            [
                'provider_id'    => $socialUser->getId(),
                'provider_token' => $socialUser->token,
                'avatar'         => $socialUser->getAvatar(),
            ]
        );

    } else {
        // নতুন User → Register করুন
        $user = DB::transaction(function () use ($socialUser, $provider) {
            $newUser = User::create([
                'name'              => $socialUser->getName(),
                'email'             => $socialUser->getEmail(),
                'email_verified_at' => now(), // Social Login = Email Verified
                'password'          => bcrypt(Str::random(32)), // Random Password
                'avatar'            => $socialUser->getAvatar(),
            ]);

            $newUser->socialAccounts()->create([
                'provider'       => $provider,
                'provider_id'    => $socialUser->getId(),
                'provider_token' => $socialUser->token,
            ]);

            return $newUser;
        });
    }

    // JWT Token Generate করুন
    $token = auth('api')->login($user);

    return response()->json([
        'access_token' => $token,
        'token_type'   => 'Bearer',
        'user'         => new UserResource($user),
    ]);
}
Enter fullscreen mode Exit fullscreen mode

}


---

🟢 **PART 4 — Encryption (এনক্রিপশন)**

**Encryption এর ধরন:**
Enter fullscreen mode Exit fullscreen mode


php
Symmetric Encryption → একই Key দিয়ে Encrypt ও Decrypt
AES-256 — দ্রুত, Bulk Data এর জন্য

Asymmetric Encryption → Public Key দিয়ে Encrypt
Private Key দিয়ে Decrypt
RSA — ধীর, Key Exchange এর জন্য

Hashing → One-way, Decrypt করা যায় না
bcrypt, argon2 — Password Storage এর জন্য

**Laravel-এ Encryption:**
Enter fullscreen mode Exit fullscreen mode


plaintext
php
// ১. Built-in Encryption — Sensitive Data
class SensitiveDataService
{
// Encrypt করুন — DB তে Store করার আগে
public function storeSensitiveData(array $data): UserProfile
{
return UserProfile::create([
'user_id' => auth()->id(),
// Encrypt করে রাখুন
'national_id' => encrypt($data['national_id']),
'bank_account' => encrypt($data['bank_account']),
'phone' => encrypt($data['phone']),
// Normal Data
'name' => $data['name'],
]);
}

// Decrypt করুন — ব্যবহারের সময়
public function getSensitiveData(int $userId): array
{
    $profile = UserProfile::where('user_id', $userId)->firstOrFail();

    return [
        'name'         => $profile->name,
        'national_id'  => decrypt($profile->national_id),
        'bank_account' => decrypt($profile->bank_account),
        'phone'        => decrypt($profile->phone),
    ];
}
Enter fullscreen mode Exit fullscreen mode

}

// ২. Model Casting — Automatic Encrypt/Decrypt
class UserProfile extends Model
{
protected $casts = [
// Laravel 9+ — Automatic Encrypt/Decrypt
'national_id' => 'encrypted',
'bank_account' => 'encrypted',
'metadata' => 'encrypted:array', // Array ও Encrypt করা যায়
];
}

// ব্যবহার — Automatic!
$profile = UserProfile::create([
'national_id' => '1234567890', // Automatically Encrypted
]);

echo $profile->national_id; // Automatically Decrypted → 1234567890

// ৩. Custom Encryption — AES-256
class EncryptionService
{
private string $key;
private string $cipher = 'AES-256-CBC';

public function __construct()
{
    $this->key = base64_decode(config('app.encryption_key'));
}

public function encrypt(string $data): string
{
    $iv        = random_bytes(openssl_cipher_iv_length($this->cipher));
    $encrypted = openssl_encrypt($data, $this->cipher, $this->key, 0, $iv);
    $hmac      = hash_hmac('sha256', $encrypted, $this->key, true);

    // IV + HMAC + Encrypted Data একসাথে রাখুন
    return base64_encode($iv . $hmac . $encrypted);
}

public function decrypt(string $encryptedData): string
{
    $decoded   = base64_decode($encryptedData);
    $ivLength  = openssl_cipher_iv_length($this->cipher);
    $iv        = substr($decoded, 0, $ivLength);
    $hmac      = substr($decoded, $ivLength, 32);
    $encrypted = substr($decoded, $ivLength + 32);

    // HMAC Verify করুন — Tamper Check
    $expectedHmac = hash_hmac('sha256', $encrypted, $this->key, true);

    if (!hash_equals($expectedHmac, $hmac)) {
        throw new DecryptException('Data tampered! HMAC mismatch.');
    }

    return openssl_decrypt($encrypted, $this->cipher, $this->key, 0, $iv);
}
Enter fullscreen mode Exit fullscreen mode

}

**Password Hashing — সঠিকভাবে:**
Enter fullscreen mode Exit fullscreen mode


php
php
// ১. Password Store করুন — bcrypt (Default)
$user = User::create([
'password' => Hash::make($request->password), // bcrypt
]);

// অথবা Argon2 (আরো শক্তিশালী)
$hash = Hash::driver('argon2id')->make($request->password);

// ২. Password Verify করুন
if (!Hash::check($request->password, $user->password)) {
throw new UnauthorizedException('Password ভুল।');
}

// ৩. Password Rehash — Algorithm Update হলে
if (Hash::needsRehash($user->password)) {
$user->update([
'password' => Hash::make($request->password)
]);
}

// ❌ কখনো এভাবে করবেন না
$user->password = md5($password); // MD5 — ভাঙা!
$user->password = sha1($password); // SHA1 — ভাঙা!
$user->password = $password; // Plain text — মারাত্মক!


---

🛡️ **PART 5 — Authorization (Role & Permission)**
**Spatie Permission — Professional RBAC:**

Enter fullscreen mode Exit fullscreen mode


php
bash
composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate

Enter fullscreen mode Exit fullscreen mode


shell
php
// Roles ও Permissions তৈরি করুন
class RolePermissionSeeder extends Seeder
{
public function run(): void
{
// Permissions তৈরি করুন
$permissions = [
// Products
'products.view', 'products.create',
'products.edit', 'products.delete',
// Orders
'orders.view', 'orders.manage',
'orders.refund',
// Users
'users.view', 'users.create',
'users.edit', 'users.delete', 'users.ban',
// Settings
'settings.view', 'settings.manage',
];

    foreach ($permissions as $permission) {
        Permission::create(['name' => $permission]);
    }

    // Roles তৈরি করুন এবং Permissions দিন
    $superAdmin = Role::create(['name' => 'super-admin']);
    $superAdmin->givePermissionTo(Permission::all()); // সব Permission

    $admin = Role::create(['name' => 'admin']);
    $admin->givePermissionTo([
        'products.view', 'products.create', 'products.edit',
        'orders.view', 'orders.manage',
        'users.view',
    ]);

    $seller = Role::create(['name' => 'seller']);
    $seller->givePermissionTo([
        'products.view', 'products.create', 'products.edit',
        'orders.view',
    ]);

    $customer = Role::create(['name' => 'customer']);
    $customer->givePermissionTo([
        'products.view',
        'orders.view',
    ]);
}
Enter fullscreen mode Exit fullscreen mode

}

// Policy — Object Level Permission
class ProductPolicy
{
// Product দেখার অনুমতি
public function view(User $user, Product $product): bool
{
return $user->hasPermissionTo('products.view');
}

// Product Edit — নিজের বা Admin
public function update(User $user, Product $product): bool
{
    return $user->id === $product->user_id
        || $user->hasRole(['admin', 'super-admin']);
}

// Product Delete — শুধু Admin
public function delete(User $user, Product $product): bool
{
    return $user->hasRole(['admin', 'super-admin'])
        || ($user->id === $product->user_id
            && $user->hasPermissionTo('products.delete'));
}
Enter fullscreen mode Exit fullscreen mode

}

// Controller এ ব্যবহার করুন
class ProductController extends Controller
{
public function update(UpdateProductRequest $request, Product $product)
{
// Policy Check — Permission নেই? Automatically 403
$this->authorize('update', $product);

    $product->update($request->validated());

    return new ProductResource($product);
}

public function destroy(Product $product)
{
    $this->authorize('delete', $product);

    $product->delete();

    return response()->json(['message' => 'Product deleted']);
}
Enter fullscreen mode Exit fullscreen mode

}

// Blade এ Permission Check
@can('products.create')
নতুন Product যোগ করুন
@endcan

@role('admin|super-admin')
Admin Panel
@endrole


---

🔒 **PART 6 — Common Security Vulnerabilities**
**SQL Injection থেকে রক্ষা:**
Enter fullscreen mode Exit fullscreen mode


php
php
// ❌ ভুল — SQL Injection এর সুযোগ!
$products = DB::select(
"SELECT * FROM products WHERE name = '$request->name'"
);

// ✅ সঠিক — Parameterized Query
$products = DB::select(
'SELECT * FROM products WHERE name = ?',
[$request->name]
);

// ✅ আরো ভালো — Eloquent ORM
$products = Product::where('name', $request->name)->get();

**XSS (Cross-Site Scripting) থেকে রক্ষা:**
Enter fullscreen mode Exit fullscreen mode


php
php
// ❌ ভুল — XSS এর সুযোগ!
return response("

Hello {$request->name}

");

// ✅ সঠিক — Escape করুন
return response("

Hello " . e($request->name) . "

");

// ✅ Blade Template — Automatically Escape
{{ $user->name }} // Safe — Escaped
{!! $user->bio !!} // Unsafe — Raw HTML

// Input Sanitize করুন
class SanitizeInput
{
public static function clean(string $input): string
{
// HTML Tags সরিয়ে দিন
$input = strip_tags($input);

    // Special Characters Encode করুন
    $input = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');

    // Extra Whitespace সরান
    return trim($input);
}
Enter fullscreen mode Exit fullscreen mode

}

// Content Security Policy Header
class SecurityHeadersMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);

    $response->headers->set(
        'Content-Security-Policy',
        "default-src 'self'; " .
        "script-src 'self' 'nonce-" . csrf_token() . "'; " .
        "style-src 'self' https://fonts.googleapis.com; " .
        "img-src 'self' data: https:; " .
        "font-src 'self' https://fonts.gstatic.com;"
    );

    $response->headers->set('X-Content-Type-Options', 'nosniff');
    $response->headers->set('X-Frame-Options', 'DENY');
    $response->headers->set('X-XSS-Protection', '1; mode=block');
    $response->headers->set(
        'Strict-Transport-Security',
        'max-age=31536000; includeSubDomains'
    );

    return $response;
}
Enter fullscreen mode Exit fullscreen mode

}

**CSRF Protection:**
Enter fullscreen mode Exit fullscreen mode


php
php
// Laravel Automatically CSRF Protect করে Web Routes এ
// API Routes এ Stateless Token ব্যবহার করুন

// Exempt করুন — Webhook Routes
// app/Http/Middleware/VerifyCsrfToken.php
protected $except = [
'webhook/*',
'api/payment/callback',
];

// API এ Sanctum Token ব্যবহার করুন
Route::middleware('auth:sanctum')->group(function () {
Route::post('/products', [ProductController::class, 'store']);
});

**Rate Limiting — Advanced:**
Enter fullscreen mode Exit fullscreen mode


php
php
// app/Providers/AppServiceProvider.php
public function boot(): void
{
// API Rate Limit
RateLimiter::for('api', function (Request $request) {
return $request->user()
? Limit::perMinute(120)->by($request->user()->id)
: Limit::perMinute(30)->by($request->ip());
});

// Login Rate Limit — Brute Force Protection
RateLimiter::for('login', function (Request $request) {
    return [
        Limit::perMinute(5)->by($request->input('email')),
        Limit::perMinute(10)->by($request->ip()),
    ];
});

// OTP Rate Limit
RateLimiter::for('otp', function (Request $request) {
    return Limit::perHour(3)->by($request->input('phone'));
});

// Expensive Operations
RateLimiter::for('reports', function (Request $request) {
    return Limit::perHour(5)
                ->by($request->user()->id)
                ->response(function () {
                    return response()->json([
                        'message' => 'প্রতি ঘন্টায় সর্বোচ্চ ৫টা Report Generate করা যাবে।'
                    ], 429);
                });
});
Enter fullscreen mode Exit fullscreen mode

}


---


🔏 **PART 7 — API Security Best Practices**
Enter fullscreen mode Exit fullscreen mode


php
php
// app/Http/Middleware/ApiSecurityMiddleware.php
class ApiSecurityMiddleware
{
public function handle(Request $request, Closure $next): Response
{
// ১. HTTPS Check
if (!$request->secure() && config('app.env') === 'production') {
return response()->json([
'message' => 'HTTPS Required'
], 403);
}

    // ২. API Version Check
    $version = $request->header('X-API-Version', '1');
    if (!in_array($version, ['1', '2'])) {
        return response()->json([
            'message' => 'Unsupported API Version'
        ], 400);
    }

    // ৩. Request Size Limit — Large Payload Attack থেকে রক্ষা
    $contentLength = $request->header('Content-Length', 0);
    if ($contentLength > 10 * 1024 * 1024) { // ১০ MB
        return response()->json([
            'message' => 'Request Too Large'
        ], 413);
    }

    // ৪. Suspicious Pattern Detect করুন
    $body = $request->getContent();
    if ($this->hasSqlInjection($body) || $this->hasXssPattern($body)) {
        Log::warning('Suspicious request detected', [
            'ip'  => $request->ip(),
            'url' => $request->fullUrl(),
        ]);

        return response()->json([
            'message' => 'Invalid Request'
        ], 400);
    }

    $response = $next($request);

    // ৫. Sensitive Data Response Header সরিয়ে দিন
    $response->headers->remove('X-Powered-By');
    $response->headers->remove('Server');
    $response->headers->set('X-Request-ID', Str::uuid());

    return $response;
}

private function hasSqlInjection(string $input): bool
{
    $patterns = [
        '/(\bunion\b.*\bselect\b)/i',
        '/(\bselect\b.*\bfrom\b)/i',
        '/(\bdrop\b.*\btable\b)/i',
        '/(\bdelete\b.*\bfrom\b)/i',
        "/(--|\#|\/\*)/",
    ];

    foreach ($patterns as $pattern) {
        if (preg_match($pattern, $input)) {
            return true;
        }
    }

    return false;
}

private function hasXssPattern(string $input): bool
{
    $patterns = [
        '/<script\b[^>]*>/i',
        '/javascript:/i',
        '/on\w+\s*=/i',
    ];

    foreach ($patterns as $pattern) {
        if (preg_match($pattern, $input)) {
            return true;
        }
    }

    return false;
}
Enter fullscreen mode Exit fullscreen mode

}

// Sensitive Data Masking — Log এ Sensitive Data রাখবেন না
class MaskSensitiveData
{
private array $sensitiveFields = [
'password', 'password_confirmation',
'credit_card', 'card_number', 'cvv',
'national_id', 'bank_account',
'token', 'secret', 'api_key',
];

public function mask(array $data): array
{
    foreach ($data as $key => $value) {
        if (in_array(strtolower($key), $this->sensitiveFields)) {
            $data[$key] = '***MASKED***';
        } elseif (is_array($value)) {
            $data[$key] = $this->mask($value);
        }
    }

    return $data;
}
Enter fullscreen mode Exit fullscreen mode

}


---


🏗️ **PART 8 — Two Factor Authentication (2FA)**
Enter fullscreen mode Exit fullscreen mode


php
php
// app/Services/TwoFactorService.php
class TwoFactorService
{
// ১. 2FA Enable করুন
public function enable(User $user): array
{
// Secret Key Generate করুন
$secret = Google2FA::generateSecretKey();

    // Backup Codes Generate করুন
    $backupCodes = collect(range(1, 8))
                        ->map(fn() => Str::random(10))
                        ->toArray();

    // DB তে রাখুন (Encrypted)
    $user->update([
        'two_factor_secret'  => encrypt($secret),
        'two_factor_backup'  => encrypt(json_encode(
            array_map('bcrypt', $backupCodes)
        )),
        'two_factor_enabled' => false, // Confirm না হওয়া পর্যন্ত
    ]);

    // QR Code URL Generate করুন
    $qrCodeUrl = Google2FA::getQRCodeUrl(
        config('app.name'),
        $user->email,
        $secret
    );

    return [
        'secret'       => $secret,
        'qr_code_url'  => $qrCodeUrl,
        'backup_codes' => $backupCodes,
    ];
}

// ২. OTP Verify করুন
public function verify(User $user, string $otp): bool
{
    $secret = decrypt($user->two_factor_secret);

    // Google Authenticator OTP Check
    $valid = Google2FA::verifyKey($secret, $otp, 1); // 1 = 30 sec window

    if (!$valid) {
        // Backup Code Check
        $valid = $this->verifyBackupCode($user, $otp);
    }

    if ($valid && !$user->two_factor_enabled) {
        $user->update(['two_factor_enabled' => true]);
    }

    return $valid;
}

// ৩. Login এ 2FA Check
public function challengeCheck(Request $request): JsonResponse
{
    $user = auth('api')->user();

    if (!$user->two_factor_enabled) {
        return response()->json(['requires_2fa' => false]);
    }

    // 2FA Token পাঠান Session এ রাখুন
    $challengeToken = Str::random(32);
    Cache::put(
        "2fa_challenge:{$challengeToken}",
        $user->id,
        now()->addMinutes(5)
    );

    // Current Token Invalidate করুন
    auth('api')->logout();

    return response()->json([
        'requires_2fa'    => true,
        'challenge_token' => $challengeToken,
        'message'         => 'Google Authenticator থেকে OTP দিন।',
    ]);
}
Enter fullscreen mode Exit fullscreen mode

}


---


📝 **সংক্ষেপে মনে রাখুন**
**JWT** — Stateless Authentication, Self-contained Token, Microservices এর জন্য আদর্শ। Access Token ছোট (১৫ মিনিট), Refresh Token বড় (৩০ দিন) রাখুন।
**OAuth2** — Third-party Login (Google, Facebook) এবং Machine-to-Machine Auth এর জন্য। Laravel Passport দিয়ে সহজে করা যায়।
**Encryption** — Sensitive Data (NID, Bank Account) সবসময় Encrypt করে রাখুন। Password সবসময় bcrypt/argon2 দিয়ে Hash করুন, কখনো Plain text না।
**Security Best Practices** — SQL Injection (Eloquent ORM), XSS (Escape), CSRF (Token), Rate Limiting, HTTPS — এই সবগুলো একসাথে মেনে চলুন।

**_Q:7_** - **DevOps (CI/CD, Docker, Kubernetes)**
**Answer**: 

🚀 **DevOps কী?**

Enter fullscreen mode Exit fullscreen mode


php
আগে (Traditional):
Developer → Code লিখলো → IT Team কে দিলো → ২ সপ্তাহ পরে Deploy!
↑ ধীর, Error প্রবণ

DevOps (এখন):
Developer → Code Push → Auto Test → Auto Build → Auto Deploy
↑ মিনিটের মধ্যে!

DevOps = Development + Operations
লক্ষ্য: দ্রুত, নিরাপদ, বারবার Software Deliver করা


---

🐳 **PART 1 — Docker**
**Docker কী?**
Docker হলো **Container** Technology। আপনার অ্যাপ এবং তার সব Dependencies একটা **Box (Container)** এ রেখে দেওয়া — যেকোনো Server এ একই ভাবে চলবে।

Enter fullscreen mode Exit fullscreen mode


php
সমস্যা (Docker ছাড়া):
"আমার Machine এ কাজ করে কিন্তু
Server এ করে না!" ← সবচেয়ে বিখ্যাত সমস্যা 😅

সমাধান (Docker সহ):
Container এ সব আছে:
├── PHP 8.3
├── Laravel App
├── Composer Dependencies
├── Nginx Config
└── Environment Variables
→ যেকোনো জায়গায় একই রেজাল্ট!

VM vs Container:
┌─────────────────────┐ ┌─────────────────────┐
│ Virtual Machine │ │ Docker Container │
├─────────────────────┤ ├─────────────────────┤
│ Full OS (2-4 GB) │ │ App + Libs (50 MB) │
│ Slow Start (মিনিট) │ │ Fast Start (সেকেন্ড)│
│ Heavy │ │ Lightweight │
└─────────────────────┘ └─────────────────────┘

**Laravel Project এর জন্য Dockerfile:**
Enter fullscreen mode Exit fullscreen mode


plaintext
dockerfile# Dockerfile

─────────────────────────────────────────

Stage 1: Dependencies Install

─────────────────────────────────────────

FROM php:8.3-fpm-alpine AS dependencies

System Dependencies Install করুন

RUN apk add --no-cache \
git \
curl \
libpng-dev \
libxml2-dev \
zip \
unzip \
nodejs \
npm

PHP Extensions Install করুন

RUN docker-php-ext-install \
pdo \
pdo_mysql \
mbstring \
exif \
pcntl \
bcmath \
gd \
opcache

Redis Extension

RUN pecl install redis && docker-php-ext-enable redis

Composer Install করুন

COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

Working Directory Set করুন

WORKDIR /var/www/html

Dependencies Install করুন (Code আগে না)

এতে Docker Cache কাজ করে — দ্রুত Build

COPY composer.json composer.lock ./
RUN composer install \
--no-dev \
--no-interaction \
--prefer-dist \
--optimize-autoloader

NPM Dependencies

COPY package.json package-lock.json ./
RUN npm ci

─────────────────────────────────────────

Stage 2: Production Image

─────────────────────────────────────────

FROM php:8.3-fpm-alpine AS production

WORKDIR /var/www/html

Dependencies Stage থেকে নিন

COPY --from=dependencies /var/www/html/vendor ./vendor
COPY --from=dependencies /usr/local/lib/php /usr/local/lib/php

Application Code Copy করুন

COPY . .

Assets Build করুন

RUN npm run build && rm -rf node_modules

Laravel Optimization

RUN php artisan config:cache && \
php artisan route:cache && \
php artisan view:cache && \
php artisan event:cache

Permissions Set করুন

RUN chown -R www-data:www-data /var/www/html/storage \
/var/www/html/bootstrap/cache

Non-root User হিসেবে চালান — Security

USER www-data

Health Check

HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD php-fpm -t || exit 1

EXPOSE 9000

CMD ["php-fpm"]

**Docker Compose — Local Development:**
Enter fullscreen mode Exit fullscreen mode


plaintext
yaml

docker-compose.yml

version: '3.8'

services:
# ─────────────────────────
# Laravel Application
# ─────────────────────────
app:
build:
context: .
dockerfile: Dockerfile
target: dependencies # Development এ dependencies stage
container_name: laravel_app
restart: unless-stopped
working_dir: /var/www/html
volumes:
- .:/var/www/html # Code Mount — Hot Reload
- ./storage:/var/www/html/storage
environment:
- APP_ENV=local
- APP_DEBUG=true
- DB_HOST=mysql
- REDIS_HOST=redis
- QUEUE_CONNECTION=redis
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
networks:
- laravel_network

# ─────────────────────────
# Nginx Web Server
# ─────────────────────────
nginx:
image: nginx:alpine
container_name: laravel_nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- .:/var/www/html
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./docker/nginx/ssl:/etc/nginx/ssl # SSL Certificates
depends_on:
- app
networks:
- laravel_network

# ─────────────────────────
# MySQL Database
# ─────────────────────────
mysql:
image: mysql:8.0
container_name: laravel_mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_DATABASE}
MYSQL_USER: ${DB_USERNAME}
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql # Data Persist
- ./docker/mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "3306:3306"
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 20s
retries: 10
networks:
- laravel_network

# ─────────────────────────
# Redis Cache
# ─────────────────────────
redis:
image: redis:7-alpine
container_name: laravel_redis
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- laravel_network

# ─────────────────────────
# Queue Worker
# ─────────────────────────
queue:
build:
context: .
dockerfile: Dockerfile
target: dependencies
container_name: laravel_queue
restart: unless-stopped
command: php artisan queue:work redis \
--sleep=3 \
--tries=3 \
--timeout=90 \
--queue=high-priority,default,low-priority
volumes:
- .:/var/www/html
depends_on:
- app
- redis
networks:
- laravel_network

# ─────────────────────────
# Laravel Scheduler
# ─────────────────────────
scheduler:
build:
context: .
dockerfile: Dockerfile
target: dependencies
container_name: laravel_scheduler
restart: unless-stopped
command: >
sh -c "while true; do
php artisan schedule:run --verbose --no-interaction &
sleep 60
done"
volumes:
- .:/var/www/html
depends_on:
- app
- mysql
networks:
- laravel_network

# ─────────────────────────
# phpMyAdmin (Development)
# ─────────────────────────
phpmyadmin:
image: phpmyadmin/phpmyadmin
container_name: laravel_phpmyadmin
environment:
PMA_HOST: mysql
PMA_PORT: 3306
ports:
- "8080:80"
profiles:
- dev # শুধু Development এ
networks:
- laravel_network

─────────────────────────

Networks & Volumes

─────────────────────────

networks:
laravel_network:
driver: bridge

volumes:
mysql_data:
driver: local
redis_data:
driver: local

**Nginx Config:**
Enter fullscreen mode Exit fullscreen mode


docker
nginx

docker/nginx/default.conf

server {
listen 80;
listen [::]:80;
server_name myapp.com www.myapp.com;

# HTTP → HTTPS Redirect
return 301 https://$server_name$request_uri;
Enter fullscreen mode Exit fullscreen mode

}

server {
listen 443 ssl http2;
server_name myapp.com www.myapp.com;

# SSL Configuration
ssl_certificate     /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_protocols       TLSv1.2 TLSv1.3;
ssl_ciphers         HIGH:!aNULL:!MD5;

root /var/www/html/public;
index index.php;

# Gzip Compression
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_min_length 1000;

# Security Headers
add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";

# Static Files Cache
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
}

# PHP Files
location / {
    try_files $uri $uri/ /index.php?$query_string;
}

location ~ \.php$ {
    fastcgi_pass app:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
    include fastcgi_params;

    # Timeout Settings
    fastcgi_connect_timeout 60;
    fastcgi_send_timeout 300;
    fastcgi_read_timeout 300;
}

# Sensitive Files Hide করুন
location ~ /\.(env|git|htaccess) {
    deny all;
}

# Max Upload Size
client_max_body_size 20M;
Enter fullscreen mode Exit fullscreen mode

}


---

🔄 **PART 2 — CI/CD Pipeline**

**CI/CD কী?**
Enter fullscreen mode Exit fullscreen mode


yaml
CI = Continuous Integration
→ Code Push করলেই Auto Test চলবে

CD = Continuous Delivery/Deployment
→ Test Pass হলে Auto Deploy হবে

পুরো Flow:
Developer → Git Push → CI Server

[Code Checkout]

[Dependencies Install]

[Code Quality Check]

[Unit Tests Run]

[Integration Tests]

[Docker Image Build]

[Push to Registry]

[Deploy to Staging]

[Smoke Tests]

[Deploy to Production] ✅

**GitHub Actions — CI/CD Pipeline:**
Enter fullscreen mode Exit fullscreen mode


nginx
yaml

.github/workflows/ci-cd.yml

name: Laravel CI/CD Pipeline

on:
push:
branches: [main, develop]
pull_request:
branches: [main]

env:
PHP_VERSION: '8.3'
NODE_VERSION: '20'
DOCKER_IMAGE: ghcr.io/${{ github.repository }}

jobs:
# ─────────────────────────────────
# Job 1: Code Quality Check
# ─────────────────────────────────
code-quality:
name: 🔍 Code Quality
runs-on: ubuntu-latest

steps:
  - name: Checkout Code
    uses: actions/checkout@v4

  - name: Setup PHP
    uses: shivammathur/setup-php@v2
    with:
      php-version: ${{ env.PHP_VERSION }}
      extensions: mbstring, pdo, redis
      coverage: xdebug

  - name: Cache Composer
    uses: actions/cache@v3
    with:
      path: vendor
      key: composer-${{ hashFiles('composer.lock') }}

  - name: Install Dependencies
    run: composer install --no-interaction --prefer-dist

  - name: PHP Code Style Check (Pint)
    run: ./vendor/bin/pint --test

  - name: Static Analysis (PHPStan)
    run: ./vendor/bin/phpstan analyse --level=8

  - name: Security Check
    run: composer audit
Enter fullscreen mode Exit fullscreen mode

# ─────────────────────────────────
# Job 2: Tests
# ─────────────────────────────────
tests:
name: 🧪 Run Tests
runs-on: ubuntu-latest
needs: code-quality

services:
  mysql:
    image: mysql:8.0
    env:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: testing
    options: >-
      --health-cmd="mysqladmin ping"
      --health-interval=10s
      --health-timeout=5s
      --health-retries=3

  redis:
    image: redis:7-alpine
    options: >-
      --health-cmd="redis-cli ping"
      --health-interval=10s

steps:
  - uses: actions/checkout@v4

  - name: Setup PHP
    uses: shivammathur/setup-php@v2
    with:
      php-version: ${{ env.PHP_VERSION }}
      extensions: mbstring, pdo_mysql, redis
      coverage: xdebug

  - name: Install Dependencies
    run: composer install --no-interaction

  - name: Copy .env
    run: |
      cp .env.testing.example .env.testing
      php artisan key:generate --env=testing

  - name: Run Migrations
    run: php artisan migrate --env=testing --force
    env:
      DB_CONNECTION: mysql
      DB_HOST: 127.0.0.1
      DB_DATABASE: testing
      DB_USERNAME: root
      DB_PASSWORD: secret

  - name: Run Unit Tests
    run: |
      php artisan test \
        --parallel \
        --coverage-clover=coverage.xml \
        --min=80
    env:
      DB_CONNECTION: mysql
      DB_HOST: 127.0.0.1
      DB_DATABASE: testing
      DB_USERNAME: root
      DB_PASSWORD: secret
      REDIS_HOST: 127.0.0.1

  - name: Upload Coverage
    uses: codecov/codecov-action@v3
    with:
      file: ./coverage.xml
Enter fullscreen mode Exit fullscreen mode

# ─────────────────────────────────
# Job 3: Build Docker Image
# ─────────────────────────────────
build:
name: 🐳 Build & Push Docker Image
runs-on: ubuntu-latest
needs: tests
if: github.ref == 'refs/heads/main'

steps:
  - uses: actions/checkout@v4

  - name: Set up Docker Buildx
    uses: docker/setup-buildx-action@v3

  - name: Login to GitHub Container Registry
    uses: docker/login-action@v3
    with:
      registry: ghcr.io
      username: ${{ github.actor }}
      password: ${{ secrets.GITHUB_TOKEN }}

  - name: Extract Metadata
    id: meta
    uses: docker/metadata-action@v5
    with:
      images: ${{ env.DOCKER_IMAGE }}
      tags: |
        type=ref,event=branch
        type=semver,pattern={{version}}
        type=sha,prefix=sha-

  - name: Build & Push
    uses: docker/build-push-action@v5
    with:
      context: .
      push: true
      tags: ${{ steps.meta.outputs.tags }}
      labels: ${{ steps.meta.outputs.labels }}
      target: production
      cache-from: type=gha
      cache-to: type=gha,mode=max
Enter fullscreen mode Exit fullscreen mode

# ─────────────────────────────────
# Job 4: Deploy to Staging
# ─────────────────────────────────
deploy-staging:
name: 🚀 Deploy to Staging
runs-on: ubuntu-latest
needs: build
environment: staging

steps:
  - name: Deploy to Staging Server
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.STAGING_HOST }}
      username: ${{ secrets.STAGING_USER }}
      key: ${{ secrets.STAGING_SSH_KEY }}
      script: |
        cd /var/www/staging
        # নতুন Image Pull করুন
        docker pull ${{ env.DOCKER_IMAGE }}:main

        # Zero-downtime Deploy
        docker-compose up -d --no-deps app
        docker-compose exec -T app php artisan migrate --force
        docker-compose exec -T app php artisan config:cache
        docker-compose exec -T app php artisan route:cache

        # Health Check
        sleep 10
        curl -f http://localhost/health || exit 1

        echo "✅ Staging Deploy সফল!"
Enter fullscreen mode Exit fullscreen mode

# ─────────────────────────────────
# Job 5: Deploy to Production
# ─────────────────────────────────
deploy-production:
name: 🌟 Deploy to Production
runs-on: ubuntu-latest
needs: deploy-staging
environment: production # Manual Approval লাগবে!

steps:
  - name: Blue-Green Deploy to Production
    uses: appleboy/ssh-action@v1
    with:
      host: ${{ secrets.PROD_HOST }}
      username: ${{ secrets.PROD_USER }}
      key: ${{ secrets.PROD_SSH_KEY }}
      script: |
        cd /var/www/production

        # Current Version Backup
        CURRENT=$(docker-compose ps -q app)

        # নতুন Container চালু করুন (Blue-Green)
        docker pull ${{ env.DOCKER_IMAGE }}:main
        docker-compose up -d --no-deps --scale app=2 app

        # Health Check
        sleep 15
        NEW_CONTAINER=$(docker-compose ps -q app | tail -1)
        docker exec $NEW_CONTAINER curl -f http://localhost/health

        # Migration Run করুন
        docker-compose exec -T app php artisan migrate --force

        # Cache Clear
        docker-compose exec -T app php artisan optimize

        # পুরনো Container সরিয়ে দিন
        docker-compose up -d --no-deps --scale app=1 app

        echo "✅ Production Deploy সফল!"

  - name: Notify Slack
    uses: 8398a7/action-slack@v3
    with:
      status: ${{ job.status }}
      text: |
        Production Deploy ${{ job.status }}!
        Commit: ${{ github.sha }}
        By: ${{ github.actor }}
    env:
      SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
    if: always()
Enter fullscreen mode Exit fullscreen mode
**Test Classes — Laravel:**
Enter fullscreen mode Exit fullscreen mode


plaintext
php
// tests/Feature/Api/ProductTest.php
class ProductApiTest extends TestCase
{
use RefreshDatabase, WithFaker;

private User $user;
private string $token;

protected function setUp(): void
{
    parent::setUp();

    // Test User তৈরি করুন
    $this->user  = User::factory()->create();
    $this->token = JWTAuth::fromUser($this->user);
}

/** @test */
public function it_can_list_products(): void
{
    Product::factory()->count(15)->create();

    $response = $this->withToken($this->token)
                     ->getJson('/api/v1/products');

    $response->assertOk()
             ->assertJsonStructure([
                 'data' => [
                     '*' => ['id', 'name', 'price', 'stock']
                 ],
                 'meta' => ['total', 'current_page']
             ])
             ->assertJsonCount(15, 'data');
}

/** @test */
public function it_can_create_product(): void
{
    $category = Category::factory()->create();

    $productData = [
        'name'        => 'Test Product',
        'price'       => 999.99,
        'stock'       => 50,
        'category_id' => $category->id,
    ];

    $response = $this->withToken($this->token)
                     ->postJson('/api/v1/products', $productData);

    $response->assertCreated()
             ->assertJsonFragment(['name' => 'Test Product']);

    $this->assertDatabaseHas('products', [
        'name'  => 'Test Product',
        'price' => 999.99,
    ]);
}

/** @test */
public function it_validates_required_fields(): void
{
    $response = $this->withToken($this->token)
                     ->postJson('/api/v1/products', []);

    $response->assertUnprocessable()
             ->assertJsonValidationErrors(['name', 'price', 'stock']);
}

/** @test */
public function it_returns_404_for_nonexistent_product(): void
{
    $response = $this->withToken($this->token)
                     ->getJson('/api/v1/products/99999');

    $response->assertNotFound();
}
Enter fullscreen mode Exit fullscreen mode

}


---

☸️ **PART 3 — Kubernetes (K8s)**

**Kubernetes কী?**

Kubernetes হলো **Container Orchestration** Platform। অনেক Container কে Manage, Scale, ও Monitor করার Tool।
Enter fullscreen mode Exit fullscreen mode


yaml
Docker = একটা Container চালানো
Kubernetes = হাজারো Container পরিচালনা করা

Kubernetes এর কাজ:
├── Auto Scaling → Traffic বাড়লে Container বাড়াও
├── Self Healing → Container Crash করলে নতুন তৈরি করো
├── Load Balancing → Traffic ভাগ করো
├── Rolling Update → Zero-downtime Deploy করো
└── Service Discovery→ Services খুঁজে পেতে সাহায্য করো

Kubernetes Architecture:
┌─────────────────────────────────────────────┐
│ Control Plane │
│ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
│ │ API │ │Scheduler │ │ Controller │ │
│ │ Server │ │ │ │ Manager │ │
│ └──────────┘ └──────────┘ └─────────────┘ │
└─────────────────────────────────────────────┘
↓ Manages
┌─────────────────────────────────────────────┐
│ Worker Nodes │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ Node 1 │ │ Node 2 │ │
│ │ ┌──────────┐ │ │ ┌──────────┐ │ │
│ │ │ Pod │ │ │ │ Pod │ │ │
│ │ │(Container│ │ │ │(Container│ │ │
│ │ │+Laravel) │ │ │ │+Laravel) │ │ │
│ │ └──────────┘ │ │ └──────────┘ │ │
│ └────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────┘

**Laravel App এর জন্য Kubernetes Manifests:**
Enter fullscreen mode Exit fullscreen mode


php
yaml

k8s/namespace.yml

apiVersion: v1
kind: Namespace
metadata:
name: laravel-app
labels:
app: laravel

Enter fullscreen mode Exit fullscreen mode


plaintext
yaml

k8s/configmap.yml — Non-sensitive Configuration

apiVersion: v1
kind: ConfigMap
metadata:
name: laravel-config
namespace: laravel-app
data:
APP_ENV: production
APP_DEBUG: "false"
LOG_CHANNEL: stderr
CACHE_DRIVER: redis
QUEUE_CONNECTION: redis
SESSION_DRIVER: redis
DB_CONNECTION: mysql
DB_PORT: "3306"

Enter fullscreen mode Exit fullscreen mode


yaml
yaml

k8s/secret.yml — Sensitive Configuration

apiVersion: v1
kind: Secret
metadata:
name: laravel-secrets
namespace: laravel-app
type: Opaque
data:
# Base64 Encoded Values
APP_KEY: YmFzZTY0ZW5jb2RlZGtleQ==
DB_PASSWORD: c2VjcmV0cGFzc3dvcmQ=
REDIS_PASSWORD: cmVkaXNwYXNz
JWT_SECRET: and0c2VjcmV0a2V5

Enter fullscreen mode Exit fullscreen mode


yaml
yaml

k8s/deployment.yml — Laravel App Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
name: laravel-app
namespace: laravel-app
labels:
app: laravel
version: "1.0"
spec:
replicas: 3 # ৩টা Instance চালাবে
selector:
matchLabels:
app: laravel
strategy:
type: RollingUpdate # Zero-downtime Update
rollingUpdate:
maxSurge: 1 # একটা বেশি তৈরি করো
maxUnavailable: 0 # কোনটা বন্ধ করো না

template:
metadata:
labels:
app: laravel
spec:
containers:
- name: laravel
image: ghcr.io/myapp/laravel:latest
imagePullPolicy: Always
ports:
- containerPort: 9000

      # Environment Variables
      envFrom:
        - configMapRef:
            name: laravel-config
        - secretRef:
            name: laravel-secrets

      # Resource Limits — একটা Pod বেশি Resource নিতে পারবে না
      resources:
        requests:
          memory: "256Mi"
          cpu: "250m"
        limits:
          memory: "512Mi"
          cpu: "500m"

      # Health Checks
      livenessProbe:
        httpGet:
          path: /health
          port: 80
        initialDelaySeconds: 30
        periodSeconds: 10
        failureThreshold: 3

      readinessProbe:
        httpGet:
          path: /health/ready
          port: 80
        initialDelaySeconds: 10
        periodSeconds: 5

      # Lifecycle Hooks
      lifecycle:
        preStop:
          exec:
            command: ["/bin/sh", "-c", "sleep 10"]  # Graceful shutdown

  # Image Pull Secret
  imagePullSecrets:
    - name: ghcr-secret
Enter fullscreen mode Exit fullscreen mode

yaml# k8s/hpa.yml — Auto Scaling
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: laravel-hpa
namespace: laravel-app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: laravel-app

minReplicas: 3 # সর্বনিম্ন ৩টা
maxReplicas: 20 # সর্বোচ্চ ২০টা

metrics:
# CPU বেশি হলে Scale করুন
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # ৭০% CPU হলে নতুন Pod

# Memory বেশি হলে Scale করুন
- type: Resource
  resource:
    name: memory
    target:
      type: Utilization
      averageUtilization: 80
Enter fullscreen mode Exit fullscreen mode

behavior:
scaleUp:
stabilizationWindowSeconds: 60 # ১ মিনিট দেখো
policies:
- type: Pods
value: 3 # একসাথে ৩টা যোগ করো
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300 # ৫ মিনিট দেখো তারপর কমাও
yaml# k8s/queue-worker.yml — Queue Worker Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: laravel-queue
namespace: laravel-app
spec:
replicas: 2

template:
spec:
containers:
- name: queue-worker
image: ghcr.io/myapp/laravel:latest
command:
- php
- artisan
- queue:work
- redis
- --sleep=3
- --tries=3
- --timeout=90
- --queue=high-priority,default,low-priority

      envFrom:
        - configMapRef:
            name: laravel-config
        - secretRef:
            name: laravel-secrets

      resources:
        requests:
          memory: "128Mi"
          cpu: "100m"
        limits:
          memory: "256Mi"
          cpu: "250m"

      # Queue Worker Health Check
      livenessProbe:
        exec:
          command:
            - php
            - artisan
            - queue:monitor
            - --max=100
        initialDelaySeconds: 60
        periodSeconds: 60
Enter fullscreen mode Exit fullscreen mode

yaml# k8s/service.yml — Service (Load Balancer)
apiVersion: v1
kind: Service
metadata:
name: laravel-service
namespace: laravel-app
spec:
selector:
app: laravel
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP


k8s/ingress.yml — External Access

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: laravel-ingress
namespace: laravel-app
annotations:
# Nginx Ingress Controller
nginx.ingress.kubernetes.io/rewrite-target: /
# SSL Auto-renew
cert-manager.io/cluster-issuer: "letsencrypt-prod"
# Rate Limiting
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/rate-limit-window: "1m"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.com
secretName: myapp-tls
rules:
- host: myapp.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: laravel-service
port:
number: 80

**Kubernetes Commands — Daily Use:**
Enter fullscreen mode Exit fullscreen mode


yaml
bash

─────────────────────────────────────

Deploy করুন

─────────────────────────────────────

kubectl apply -f k8s/ # সব yaml apply করুন
kubectl apply -f k8s/deployment.yml

─────────────────────────────────────

Status দেখুন

─────────────────────────────────────

kubectl get pods -n laravel-app
kubectl get deployments -n laravel-app
kubectl get services -n laravel-app
kubectl get hpa -n laravel-app # Auto Scaling Status

─────────────────────────────────────

Logs দেখুন

─────────────────────────────────────

kubectl logs -f deployment/laravel-app -n laravel-app
kubectl logs -f pod/laravel-app-xxx -n laravel-app

─────────────────────────────────────

Pod এ ঢুকুন

─────────────────────────────────────

kubectl exec -it deployment/laravel-app \
-n laravel-app -- bash

Laravel Commands Run করুন

kubectl exec -it deployment/laravel-app \
-n laravel-app -- php artisan migrate

kubectl exec -it deployment/laravel-app \
-n laravel-app -- php artisan tinker

─────────────────────────────────────

Scale করুন

─────────────────────────────────────

kubectl scale deployment laravel-app \
--replicas=5 -n laravel-app

─────────────────────────────────────

Rolling Update — Zero Downtime

─────────────────────────────────────

kubectl set image deployment/laravel-app \
laravel=ghcr.io/myapp/laravel:v2.0 \
-n laravel-app

kubectl rollout status deployment/laravel-app -n laravel-app

─────────────────────────────────────

Rollback করুন — কিছু ভুল হলে

─────────────────────────────────────

kubectl rollout undo deployment/laravel-app -n laravel-app
kubectl rollout undo deployment/laravel-app \
--to-revision=3 -n laravel-app

─────────────────────────────────────

Resource Usage দেখুন

─────────────────────────────────────

kubectl top pods -n laravel-app
kubectl top nodes

🏥 **PART 4 — Health Check Endpoint**
Enter fullscreen mode Exit fullscreen mode


yaml
php
// routes/api.php
Route::get('/health', [HealthController::class, 'check']);
Route::get('/health/ready', [HealthController::class, 'readiness']);
Route::get('/health/live', [HealthController::class, 'liveness']);

// app/Http/Controllers/HealthController.php
class HealthController extends Controller
{
// Liveness — Container জীবিত আছে?
public function liveness(): JsonResponse
{
return response()->json([
'status' => 'alive',
'timestamp' => now()->toIso8601String(),
]);
}

// Readiness — Traffic নেওয়ার জন্য Ready?
public function readiness(): JsonResponse
{
    $checks = [];
    $healthy = true;

    // Database Check
    try {
        DB::select('SELECT 1');
        $checks['database'] = 'ok';
    } catch (\Exception $e) {
        $checks['database'] = 'fail';
        $healthy = false;
    }

    // Redis Check
    try {
        Redis::ping();
        $checks['redis'] = 'ok';
    } catch (\Exception $e) {
        $checks['redis'] = 'fail';
        $healthy = false;
    }

    // Queue Check
    $failedJobs = DB::table('failed_jobs')->count();
    $checks['queue'] = $failedJobs < 50 ? 'ok' : 'degraded';

    return response()->json([
        'status' => $healthy ? 'ready' : 'not_ready',
        'checks' => $checks,
    ], $healthy ? 200 : 503);
}
Enter fullscreen mode Exit fullscreen mode

}


---


📝 **সংক্ষেপে মনে রাখুন**
**Docker** — আপনার Laravel App এবং সব Dependencies একটা Container এ রাখুন। যেকোনো Server এ একই ভাবে চলবে। Local Development এ `docker-compose` ব্যবহার করুন।
**CI/CD** — GitHub Actions দিয়ে Code Push করলেই Auto Test, Build, Deploy হবে। Test ছাড়া Production এ যাবে না। এতে Human Error কমে।
**Kubernetes** — Production এ অনেক Container Manage করার জন্য। Auto Scaling, Self-healing, Zero-downtime Deploy — সব পাবেন। ছোট Project এ দরকার নেই, বড় হলে অপরিহার্য।
**শেখার ক্রম** — প্রথমে Docker শিখুন → তারপর GitHub Actions দিয়ে CI/CD → পরে Kubernetes।

**_Q:8_** - **Performance Optimization (profiling, load balancing)**
**Answer**: 

⚡ **Performance কেন গুরুত্বপূর্ণ?**
গবেষণা বলছে:

Enter fullscreen mode Exit fullscreen mode


shell
├── 1 সেকেন্ড Delay → ৭% Conversion কমে
├── 3 সেকেন্ড Delay → ৫৩% Mobile User চলে যায়
├── Google Ranking → Page Speed একটা Factor
└── Amazon: ১০০ms → ১% Revenue কমে

আদর্শ Response Time:
├── API Response → < 200ms ✅ খুব ভালো
├── Page Load → < 1s ✅ ভালো
├── Complex Report → < 3s ⚠️ গ্রহণযোগ্য
└── যেকোনো কিছু → > 5s ❌ User চলে যাবে


---

🔍 **PART 1 — Profiling (সমস্যা খুঁজে বের করা)**
**Profiling কী?**
Profiling মানে আপনার অ্যাপের **কোথায় সময় নষ্ট হচ্ছে** সেটা খুঁজে বের করা। Optimize করার আগে অবশ্যই Profile করুন।

Enter fullscreen mode Exit fullscreen mode


php
❌ ভুল পদ্ধতি:
"মনে হচ্ছে Database Slow" → সেটা Optimize করলাম
→ কোনো পার্থক্য নেই!

✅ সঠিক পদ্ধতি:
Profile করুন → সমস্যা খুঁজুন → সেটা Fix করুন
→ আবার Profile করুন → Verify করুন

**Laravel Telescope — Development Profiling:**

Enter fullscreen mode Exit fullscreen mode


plaintext
bash
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrate


Enter fullscreen mode Exit fullscreen mode


plaintext
php
// config/telescope.php
return [
'enabled' => env('TELESCOPE_ENABLED', true),

// কী কী Track করবেন
'watchers' => [
    Watchers\QueryWatcher::class => [
        'enabled' => true,
        'slow'    => 100, // ১০০ms এর বেশি = Slow Query Alert
    ],
    Watchers\RequestWatcher::class => [
        'enabled'          => true,
        'size_limit'       => env('TELESCOPE_RESPONSE_SIZE_LIMIT', 64),
    ],
    Watchers\CacheWatcher::class  => ['enabled' => true],
    Watchers\JobWatcher::class    => ['enabled' => true],
    Watchers\MailWatcher::class   => ['enabled' => true],
    Watchers\ModelWatcher::class  => [
        'enabled' => true,
        'events'  => ['eloquent.created*', 'eloquent.updated*'],
    ],
],
Enter fullscreen mode Exit fullscreen mode

];

// app/Providers/TelescopeServiceProvider.php
class TelescopeServiceProvider extends ServiceProvider
{
public function register(): void
{
Telescope::night(); // Dark Mode

    // Slow Query হলে Alert দিন
    Telescope::filter(function (IncomingEntry $entry) {
        if ($entry->type === EntryType::QUERY) {
            return $entry->content['slow'] ?? false;
        }
        return true;
    });
}
Enter fullscreen mode Exit fullscreen mode

}

**Custom Profiler — Detailed Performance Tracking:**
Enter fullscreen mode Exit fullscreen mode


shell
php
// app/Services/PerformanceProfiler.php
class PerformanceProfiler
{
private array $timings = [];
private array $memories = [];
private float $startTime;
private int $startMemory;

public function start(string $label): void
{
    $this->timings[$label]  = microtime(true);
    $this->memories[$label] = memory_get_usage(true);
}

public function end(string $label): array
{
    $duration    = (microtime(true) - $this->timings[$label]) * 1000;
    $memoryUsed  = memory_get_usage(true) - $this->memories[$label];

    $result = [
        'label'       => $label,
        'duration_ms' => round($duration, 2),
        'memory_kb'   => round($memoryUsed / 1024, 2),
        'slow'        => $duration > 100, // ১০০ms এর বেশি = Slow
    ];

    // Slow Operation Log করুন
    if ($result['slow']) {
        Log::warning("Slow Operation Detected", $result);
    }

    return $result;
}

public function measure(string $label, callable $callback): mixed
{
    $this->start($label);
    $result = $callback();
    $timing = $this->end($label);

    return $result;
}
Enter fullscreen mode Exit fullscreen mode

}

// ব্যবহার
class DashboardController extends Controller
{
public function index(PerformanceProfiler $profiler): JsonResponse
{
$profiler->start('total_dashboard');

    // Products Load
    $products = $profiler->measure('load_products', function () {
        return Product::with(['category', 'brand'])
                      ->where('status', 'active')
                      ->take(20)
                      ->get();
    });

    // Sales Report
    $salesData = $profiler->measure('load_sales', function () {
        return Order::selectRaw('DATE(created_at) as date, SUM(total) as revenue')
                    ->whereBetween('created_at', [
                        now()->subDays(30),
                        now()
                    ])
                    ->groupBy('date')
                    ->get();
    });

    // User Stats
    $userStats = $profiler->measure('load_user_stats', function () {
        return [
            'total'  => User::count(),
            'active' => User::where('last_login_at', '>', now()->subDays(7))->count(),
            'new'    => User::whereDate('created_at', today())->count(),
        ];
    });

    $totalTime = $profiler->end('total_dashboard');

    // Production এ Log করুন
    if (app()->environment('production')) {
        Log::info('Dashboard Performance', [
            'total_ms'   => $totalTime['duration_ms'],
            'user_id'    => auth()->id(),
            'request_id' => request()->header('X-Request-ID'),
        ]);
    }

    return response()->json([
        'products'   => $products,
        'sales'      => $salesData,
        'user_stats' => $userStats,
        '_debug'     => app()->environment('local') ? $totalTime : null,
    ]);
}
Enter fullscreen mode Exit fullscreen mode

}

**Query Profiling — Slow Query খুঁজুন:**
Enter fullscreen mode Exit fullscreen mode


php
php
// app/Providers/AppServiceProvider.php
public function boot(): void
{
if (config('app.debug')) {
// সব Query Log করুন
DB::listen(function (QueryExecuted $query) {
$duration = $query->time; // milliseconds

        // ১০০ms এর বেশি হলে Log করুন
        if ($duration > 100) {
            Log::warning('Slow Query Detected', [
                'sql'        => $query->sql,
                'bindings'   => $query->bindings,
                'duration'   => "{$duration}ms",
                'connection' => $query->connectionName,
                'trace'      => collect(debug_backtrace())
                                ->take(5)
                                ->pluck('file')
                                ->toArray(),
            ]);
        }
    });
}
Enter fullscreen mode Exit fullscreen mode

}

// Query Analysis — EXPLAIN দিয়ে
class QueryAnalyzer
{
public function explain(string $sql, array $bindings = []): array
{
// MySQL EXPLAIN — Query Plan দেখুন
$explained = DB::select("EXPLAIN " . $sql, $bindings);

    $issues = [];

    foreach ($explained as $row) {
        // Full Table Scan — Index নেই!
        if ($row->type === 'ALL') {
            $issues[] = [
                'type'    => 'full_table_scan',
                'table'   => $row->table,
                'rows'    => $row->rows,
                'message' => "⚠️ {$row->table} এ Full Table Scan!
                              Index যোগ করুন।",
            ];
        }

        // No Index Used
        if (empty($row->key)) {
            $issues[] = [
                'type'    => 'no_index',
                'table'   => $row->table,
                'message' => "⚠️ {$row->table} এ কোনো Index ব্যবহার
                              হচ্ছে না।",
            ];
        }
    }

    return [
        'plan'   => $explained,
        'issues' => $issues,
    ];
}
Enter fullscreen mode Exit fullscreen mode

}


---


🚀 **PART 2 — Laravel Specific Optimizations**
**Database Optimization:**
Enter fullscreen mode Exit fullscreen mode


php
php
// ─────────────────────────────────────
// ১. N+1 Query Problem — সবচেয়ে Common
// ─────────────────────────────────────

// ❌ ভুল — ১০১টা Query!
$orders = Order::all(); // 1 Query
foreach ($orders as $order) {
echo $order->user->name; // +1 Query প্রতিটায়
echo $order->product->name; // +1 Query প্রতিটায়
}
// ১০০ Orders = ২০১ Queries!

// ✅ সঠিক — মাত্র ৩টা Query
$orders = Order::with(['user', 'product', 'payment'])
->get();

// ✅ আরো ভালো — দরকারী Field শুধু নিন
$orders = Order::with([
'user:id,name,email', // শুধু এই Fields
'product:id,name,price', // শুধু এই Fields
])
->select(['id', 'user_id', 'product_id', 'total', 'status'])
->get();

// ─────────────────────────────────────
// ২. Select Only Required Columns
// ─────────────────────────────────────

// ❌ ভুল — সব Column নিচ্ছে (SELECT *)
$users = User::all();

// ✅ সঠিক — দরকারী Column শুধু
$users = User::select(['id', 'name', 'email', 'created_at'])->get();

// ─────────────────────────────────────
// ৩. Index Optimization
// ─────────────────────────────────────
class AddPerformanceIndexes extends Migration
{
public function up(): void
{
// Single Column Index
Schema::table('orders', function (Blueprint $table) {
$table->index('status');
$table->index('user_id');
$table->index('created_at');
});

    // Composite Index — একসাথে filter করলে
    Schema::table('orders', function (Blueprint $table) {
        // WHERE user_id = ? AND status = ?
        $table->index(['user_id', 'status']);

        // WHERE status = ? ORDER BY created_at DESC
        $table->index(['status', 'created_at']);
    });

    // Covering Index — Index থেকেই সব Data পাবে, Table যেতে হবে না
    Schema::table('products', function (Blueprint $table) {
        // SELECT id, name, price WHERE category_id = ? AND status = ?
        $table->index(['category_id', 'status', 'id', 'name', 'price']);
    });

    // Full-text Index — Search এর জন্য
    Schema::table('products', function (Blueprint $table) {
        $table->fullText(['name', 'description']);
    });
}
Enter fullscreen mode Exit fullscreen mode

}

// ─────────────────────────────────────
// ৪. Query Chunking — Memory Optimize
// ─────────────────────────────────────

// ❌ ভুল — লক্ষ Row Memory তে Load
$products = Product::all(); // Out of Memory!

// ✅ সঠিক — Chunk করে Process
Product::chunkById(1000, function (Collection $products) {
foreach ($products as $product) {
// Process করুন
$this->processProduct($product);
}
});

// ✅ আরো ভালো — Lazy Collection
Product::lazy(500)->each(function (Product $product) {
$this->processProduct($product);
// Memory: শুধু ৫০০টা একসাথে
});

// ─────────────────────────────────────
// ৫. Aggregate Query Optimization
// ─────────────────────────────────────

// ❌ ভুল — সব Record Load করে Count
$total = Order::where('status', 'completed')->get()->count();

// ✅ সঠিক — DB তেই Count করুন
$total = Order::where('status', 'completed')->count();

// ❌ ভুল — PHP তে Sum করুন
$revenue = Order::where('status', 'completed')
->get()
->sum('total');

// ✅ সঠিক — DB তে Sum করুন
$revenue = Order::where('status', 'completed')->sum('total');

// Multiple Aggregates একসাথে
$stats = Order::where('status', 'completed')
->selectRaw('
COUNT(*) as total_orders,
SUM(total) as total_revenue,
AVG(total) as avg_order_value,
MAX(total) as highest_order,
MIN(total) as lowest_order
')
->first();

**Laravel Cache Optimization:**
Enter fullscreen mode Exit fullscreen mode


php
php
// app/Services/OptimizedProductService.php
class OptimizedProductService
{
// ─────────────────────────────────────
// Multi-level Cache
// ─────────────────────────────────────
private array $localCache = []; // Request-level Cache

public function getProduct(int $id): ?Product
{
    // Level 1: Local Array Cache (সবচেয়ে দ্রুত)
    if (isset($this->localCache["product:{$id}"])) {
        return $this->localCache["product:{$id}"];
    }

    // Level 2: Redis Cache
    $product = Cache::remember(
        "product:{$id}",
        now()->addHours(6),
        // Level 3: Database
        fn() => Product::with(['category:id,name', 'brand:id,name'])
                        ->find($id)
    );

    // Local Cache এ রাখুন
    $this->localCache["product:{$id}"] = $product;

    return $product;
}

// ─────────────────────────────────────
// Batch Cache Loading
// ─────────────────────────────────────
public function getProducts(array $ids): Collection
{
    // Cache থেকে যা আছে নিন
    $cached    = [];
    $missing   = [];

    foreach ($ids as $id) {
        $product = Cache::get("product:{$id}");
        if ($product) {
            $cached[$id] = $product;
        } else {
            $missing[] = $id;
        }
    }

    // শুধু Missing গুলো DB থেকে নিন
    if (!empty($missing)) {
        $products = Product::with(['category', 'brand'])
                           ->whereIn('id', $missing)
                           ->get()
                           ->keyBy('id');

        // Cache এ রাখুন — Pipeline দিয়ে (দ্রুত)
        Redis::pipeline(function ($pipe) use ($products) {
            foreach ($products as $product) {
                $pipe->setex(
                    "product:{$product->id}",
                    3600 * 6,
                    serialize($product)
                );
            }
        });

        $cached = array_merge($cached, $products->toArray());
    }

    return collect($cached);
}

// ─────────────────────────────────────
// Computed Properties Cache
// ─────────────────────────────────────
public function getProductStats(int $productId): array
{
    return Cache::remember(
        "product:{$productId}:stats",
        now()->addMinutes(30),
        function () use ($productId) {
            return [
                'total_sold'   => OrderItem::where('product_id', $productId)
                                           ->sum('quantity'),
                'revenue'      => OrderItem::where('product_id', $productId)
                                           ->sum(DB::raw('quantity * price')),
                'avg_rating'   => Review::where('product_id', $productId)
                                        ->avg('rating'),
                'review_count' => Review::where('product_id', $productId)
                                        ->count(),
                'view_count'   => (int) Redis::get("product:{$productId}:views"),
            ];
        }
    );
}
Enter fullscreen mode Exit fullscreen mode

}

**PHP OpCode Cache & Configuration:**
Enter fullscreen mode Exit fullscreen mode


php
ini
; php.ini / docker/php/php.ini

; ─────────────────────────────────────
; OPcache — PHP Code Cache
; ─────────────────────────────────────
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=256 ; 256MB
opcache.interned_strings_buffer=16 ; 16MB
opcache.max_accelerated_files=20000 ; File Count
opcache.revalidate_freq=0 ; Production: 0 (Never Recheck)
opcache.validate_timestamps=0 ; Production: 0
opcache.save_comments=1
opcache.fast_shutdown=1

; ─────────────────────────────────────
; PHP Configuration
; ─────────────────────────────────────
memory_limit=512M
max_execution_time=60
max_input_time=60
upload_max_filesize=20M
post_max_size=20M

; Realpath Cache
realpath_cache_size=4096K
realpath_cache_ttl=600

Enter fullscreen mode Exit fullscreen mode


php
php
// Laravel Production Optimization Commands
// Deploy এর সময় চালান:

// php artisan optimize — সব একসাথে
// অথবা আলাদা আলাদা:
php artisan config:cache // Config Cache করুন
php artisan route:cache // Route Cache করুন
php artisan view:cache // Blade View Cache করুন
php artisan event:cache // Event Cache করুন

// app/Console/Commands/OptimizeForProduction.php
class OptimizeForProduction extends Command
{
protected $signature = 'app:optimize-production';
protected $description = 'Production Optimization চালান';

public function handle(): void
{
    $this->info('🚀 Optimization শুরু হচ্ছে...');

    $steps = [
        ['php artisan config:cache',  'Config Cache'],
        ['php artisan route:cache',   'Route Cache'],
        ['php artisan view:cache',    'View Cache'],
        ['php artisan event:cache',   'Event Cache'],
        ['php artisan icons:cache',   'Icons Cache'],
    ];

    foreach ($steps as [$command, $label]) {
        $this->call(trim(str_replace('php artisan ', '', $command)));
        $this->line("  ✅ {$label} সম্পন্ন");
    }

    // Cache Warm করুন
    $this->call('cache:warm');
    $this->line("  ✅ Cache Warm সম্পন্ন");

    $this->info('✨ Optimization সম্পন্ন!');
}
Enter fullscreen mode Exit fullscreen mode

}


---

⚖️ **PART 3 — Load Balancing**

**Load Balancing কী?**
Enter fullscreen mode Exit fullscreen mode


php
Load Balancing = Traffic কে একাধিক Server এ ভাগ করা

একটা Server:
[১০০০ Requests] → [Server 1] ← Overwhelmed! 😰

Load Balanced:
[১০০০ Requests] → [Load Balancer]
├── [Server 1] ← ৩৩৩ Requests
├── [Server 2] ← ৩৩৩ Requests
└── [Server 3] ← ৩৩৪ Requests

Load Balancing Algorithms:
├── Round Robin → পালাক্রমে (১→২→৩→১→২→৩)
├── Least Connection → যার Connection কম তাকে দাও
├── IP Hash → Same User → Same Server
├── Weighted → শক্তিশালী Server বেশি পাবে
└── Random → Random Server এ পাঠাও

**Nginx Load Balancer Configuration:**
Enter fullscreen mode Exit fullscreen mode


php
nginx

/etc/nginx/nginx.conf

─────────────────────────────────────

Upstream — Backend Servers Define করুন

─────────────────────────────────────

upstream laravel_app {
# Load Balancing Algorithm
least_conn; # Least Connection — সবচেয়ে কম Busy Server

# Backend Servers
server 10.0.0.1:9000 weight=3;  # শক্তিশালী — বেশি Traffic পাবে
server 10.0.0.2:9000 weight=2;
server 10.0.0.3:9000 weight=1;

# Health Check — Dead Server বাদ দিন
server 10.0.0.4:9000 backup;    # Primary সব Down হলে Backup

# Keepalive Connections — Performance বাড়ায়
keepalive 32;
Enter fullscreen mode Exit fullscreen mode

}

API Servers — আলাদা Upstream

upstream laravel_api {
ip_hash; # Same User → Same Server (Session Consistency)

server 10.0.1.1:9000;
server 10.0.1.2:9000;
server 10.0.1.3:9000;

# Server Down হলে Timeout
server 10.0.1.4:9000 max_fails=3 fail_timeout=30s;
Enter fullscreen mode Exit fullscreen mode

}

─────────────────────────────────────

Rate Limiting Zones

─────────────────────────────────────

limit_req_zone $binary_remote_addr
zone=api_limit:10m # 10MB Memory
rate=100r/m; # প্রতি মিনিটে ১০০ Request

limit_req_zone $binary_remote_addr
zone=login_limit:10m
rate=5r/m; # Login: প্রতি মিনিটে ৫ Request

limit_conn_zone $binary_remote_addr
zone=conn_limit:10m; # Connection Limit

─────────────────────────────────────

Main Server Block

─────────────────────────────────────

server {
listen 443 ssl http2;
server_name myapp.com;

# SSL
ssl_certificate     /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
ssl_session_cache   shared:SSL:10m;
ssl_session_timeout 10m;

# ─────────────────────────
# Gzip Compression
# ─────────────────────────
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types
    text/plain
    text/css
    text/javascript
    application/json
    application/javascript
    application/xml
    image/svg+xml;

# ─────────────────────────
# Static Files — CDN থেকে
# ─────────────────────────
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2|ttf)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    add_header Vary "Accept-Encoding";

    # CDN Origin — File না থাকলে S3 থেকে নিন
    try_files $uri @cdn_origin;
}

location @cdn_origin {
    proxy_pass https://myapp-assets.s3.amazonaws.com;
}

# ─────────────────────────
# API Routes
# ─────────────────────────
location /api/ {
    # Rate Limiting
    limit_req zone=api_limit burst=20 nodelay;
    limit_conn conn_limit 10;

    proxy_pass http://laravel_api;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Request-ID $request_id;

    # Timeouts
    proxy_connect_timeout 5s;
    proxy_send_timeout 30s;
    proxy_read_timeout 30s;

    # Buffer Settings
    proxy_buffering on;
    proxy_buffer_size 4k;
    proxy_buffers 8 4k;
}

# Login Rate Limiting
location /api/auth/login {
    limit_req zone=login_limit burst=3 nodelay;
    proxy_pass http://laravel_api;
}

# ─────────────────────────
# Web Routes
# ─────────────────────────
location / {
    proxy_pass http://laravel_app;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

# ─────────────────────────
# Health Check
# ─────────────────────────
location /health {
    access_log off;
    proxy_pass http://laravel_app;
}

# ─────────────────────────
# Monitoring
# ─────────────────────────
location /nginx-status {
    stub_status on;
    allow 10.0.0.0/8;    # শুধু Internal IP
    deny all;
}
Enter fullscreen mode Exit fullscreen mode

}

**HAProxy — Advanced Load Balancing:**
Enter fullscreen mode Exit fullscreen mode


ini
haproxy

/etc/haproxy/haproxy.cfg

global
maxconn 50000
log /dev/log local0
user haproxy
group haproxy
daemon

defaults
mode http
log global
option httplog
option dontlognull
option forwardfor
option http-server-close
timeout connect 5s
timeout client 30s
timeout server 30s
retries 3

─────────────────────────────────────

Frontend — User Facing

─────────────────────────────────────

frontend http_front
bind *:80
bind *:443 ssl crt /etc/ssl/myapp.pem

# HTTP → HTTPS Redirect
redirect scheme https if !{ ssl_fc }

# ACL — URL Pattern দিয়ে Route করুন
acl is_api  path_beg /api/
acl is_ws   path_beg /ws/       # WebSocket

# Different Backend এ পাঠান
use_backend api_backend  if is_api
use_backend ws_backend   if is_ws
default_backend web_backend
Enter fullscreen mode Exit fullscreen mode

─────────────────────────────────────

Web Backend

─────────────────────────────────────

backend web_backend
balance leastconn

# Sticky Session — Same User → Same Server
cookie SERVERID insert indirect nocache

# Health Check
option      httpchk GET /health HTTP/1.1\r\nHost:\ localhost
http-check  expect status 200

server web1 10.0.0.1:80 check cookie s1 weight 100
server web2 10.0.0.2:80 check cookie s2 weight 100
server web3 10.0.0.3:80 check cookie s3 weight 100
server web4 10.0.0.4:80 check cookie s4 backup  # Backup
Enter fullscreen mode Exit fullscreen mode

─────────────────────────────────────

API Backend

─────────────────────────────────────

backend api_backend
balance roundrobin
option httpchk GET /health

# Slow Start — নতুন Server ধীরে ধীরে Traffic নেবে
server api1 10.0.1.1:80 check slowstart 30s
server api2 10.0.1.2:80 check slowstart 30s
server api3 10.0.1.3:80 check slowstart 30s
Enter fullscreen mode Exit fullscreen mode

─────────────────────────────────────

Stats Dashboard

─────────────────────────────────────

listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 30s
stats auth admin:secret_password

📊 **PART 4 — Application Performance Monitoring (APM)**
Enter fullscreen mode Exit fullscreen mode


php
php
// app/Http/Middleware/PerformanceMonitorMiddleware.php
class PerformanceMonitorMiddleware
{
public function handle(Request $request, Closure $next): Response
{
$startTime = microtime(true);
$startMemory = memory_get_usage(true);

    $response = $next($request);

    $duration    = (microtime(true) - $startTime) * 1000;
    $memoryUsed  = (memory_get_usage(true) - $startMemory) / 1024 / 1024;
    $queryCount  = DB::getQueryLog() ? count(DB::getQueryLog()) : 0;
    $peakMemory  = memory_get_peak_usage(true) / 1024 / 1024;

    // Response Header এ দিন — Debug এর জন্য
    if (config('app.debug')) {
        $response->headers->set('X-Response-Time', round($duration, 2) . 'ms');
        $response->headers->set('X-Memory-Usage', round($memoryUsed, 2) . 'MB');
        $response->headers->set('X-Query-Count', $queryCount);
        $response->headers->set('X-Peak-Memory', round($peakMemory, 2) . 'MB');
    }

    // Metrics Collect করুন — Prometheus Format
    $this->recordMetrics([
        'route'       => $request->route()?->getName() ?? 'unknown',
        'method'      => $request->method(),
        'status'      => $response->getStatusCode(),
        'duration_ms' => round($duration, 2),
        'memory_mb'   => round($memoryUsed, 2),
        'queries'     => $queryCount,
    ]);

    // Slow Request Alert
    if ($duration > 2000) { // ২ সেকেন্ড
        Log::warning('Slow Request Detected', [
            'url'         => $request->fullUrl(),
            'method'      => $request->method(),
            'duration_ms' => round($duration, 2),
            'queries'     => $queryCount,
            'user_id'     => auth()->id(),
            'ip'          => $request->ip(),
        ]);
    }

    return $response;
}

private function recordMetrics(array $metrics): void
{
    // Redis এ Metrics Store করুন
    $key = "metrics:" . date('Y-m-d-H');

    Redis::pipeline(function ($pipe) use ($key, $metrics) {
        $pipe->hincrby($key, 'total_requests', 1);
        $pipe->hincrbyfloat($key, 'total_duration', $metrics['duration_ms']);
        $pipe->hincrby($key, "status_{$metrics['status']}", 1);

        // Slow Request Count
        if ($metrics['duration_ms'] > 1000) {
            $pipe->hincrby($key, 'slow_requests', 1);
        }

        // Query Heavy Request
        if ($metrics['queries'] > 20) {
            $pipe->hincrby($key, 'heavy_query_requests', 1);
        }

        $pipe->expire($key, 86400 * 7); // ৭ দিন রাখুন
    });
}
Enter fullscreen mode Exit fullscreen mode

}

// Metrics Dashboard
class MetricsDashboardController extends Controller
{
public function index(): JsonResponse
{
$hours = [];

    for ($i = 23; $i >= 0; $i--) {
        $key  = "metrics:" . now()->subHours($i)->format('Y-m-d-H');
        $data = Redis::hgetall($key);

        $totalRequests = (int) ($data['total_requests'] ?? 0);
        $totalDuration = (float) ($data['total_duration'] ?? 0);

        $hours[] = [
            'hour'            => now()->subHours($i)->format('H:00'),
            'total_requests'  => $totalRequests,
            'avg_response_ms' => $totalRequests > 0
                                  ? round($totalDuration / $totalRequests, 2)
                                  : 0,
            'slow_requests'   => (int) ($data['slow_requests'] ?? 0),
            'status_200'      => (int) ($data['status_200'] ?? 0),
            'status_500'      => (int) ($data['status_500'] ?? 0),
            'error_rate'      => $totalRequests > 0
                ? round(($data['status_500'] ?? 0) / $totalRequests * 100, 2)
                : 0,
        ];
    }

    return response()->json([
        'hourly_metrics' => $hours,
        'summary'        => $this->getSummary($hours),
    ]);
}

private function getSummary(array $hours): array
{
    $totalRequests = array_sum(array_column($hours, 'total_requests'));
    $avgResponse   = collect($hours)
                        ->where('total_requests', '>', 0)
                        ->avg('avg_response_ms');

    return [
        'total_requests_24h' => $totalRequests,
        'avg_response_ms'    => round($avgResponse, 2),
        'peak_hour'          => collect($hours)
                                    ->sortByDesc('total_requests')
                                    ->first()['hour'],
    ];
}
Enter fullscreen mode Exit fullscreen mode

}


---

🗄️ **PART 5 — Database Connection Pooling**
Enter fullscreen mode Exit fullscreen mode


plaintext
php
// config/database.php — Connection Pool Configuration
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST'),
'database' => env('DB_DATABASE'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),

// Connection Pool Settings
'options' => [
    PDO::ATTR_PERSISTENT         => true,  // Persistent Connection
    PDO::ATTR_EMULATE_PREPARES   => false,
    PDO::MYSQL_ATTR_INIT_COMMAND => "SET time_zone='+06:00'", // Bangladesh
],
Enter fullscreen mode Exit fullscreen mode

],

// PgBouncer — PostgreSQL Connection Pooler
// docker-compose.yml এ যোগ করুন:
//
// pgbouncer:
// image: edoburu/pgbouncer
// environment:
// DB_HOST: postgres
// DB_PORT: 5432
// MAX_CLIENT_CONN: 1000 # সর্বোচ্চ Client
// DEFAULT_POOL_SIZE: 20 # প্রতি DB ২০ Connection
// POOL_MODE: transaction # Transaction Level Pooling
// ports:
// - "5432:5432"

// app/Services/ConnectionPoolMonitor.php
class ConnectionPoolMonitor
{
public function getStats(): array
{
// Active Connections দেখুন
$connections = DB::select("
SELECT
count(*) as total,
sum(case when command = 'Sleep' then 1 else 0 end) as idle,
sum(case when command != 'Sleep' then 1 else 0 end) as active,
max(time) as max_query_time
FROM information_schema.processlist
WHERE user = ?
", [config('database.connections.mysql.username')]);

    return [
        'total_connections' => $connections[0]->total,
        'idle_connections'  => $connections[0]->idle,
        'active_connections'=> $connections[0]->active,
        'max_query_time_s'  => $connections[0]->max_query_time,
        'pool_usage_pct'    => round(
            $connections[0]->active / max($connections[0]->total, 1) * 100,
            1
        ),
    ];
}
Enter fullscreen mode Exit fullscreen mode

}

🔬 **PART 6 — Performance Testing**
Enter fullscreen mode Exit fullscreen mode


nginx
php
// tests/Performance/ApiPerformanceTest.php
class ApiPerformanceTest extends TestCase
{
/** @test */
public function product_list_api_responds_within_200ms(): void
{
// Seed করুন
Product::factory()->count(100)->create();

    $start = microtime(true);

    $response = $this->getJson('/api/v1/products');

    $duration = (microtime(true) - $start) * 1000;

    $response->assertOk();

    $this->assertLessThan(
        200,
        $duration,
        "Product List API {$duration}ms নিয়েছে। ২০০ms এর মধ্যে হওয়া উচিত।"
    );
}

/** @test */
public function no_n_plus_one_query_on_product_list(): void
{
    Product::factory()->count(20)->create();

    // Query Count Check করুন
    DB::enableQueryLog();

    $this->getJson('/api/v1/products');

    $queryCount = count(DB::getQueryLog());
    DB::disableQueryLog();

    // ৫টার বেশি Query হওয়া উচিত না
    $this->assertLessThanOrEqual(
        5,
        $queryCount,
        "Product List এ {$queryCount} টা Query চললো। N+1 সমস্যা আছে!"
    );
}
Enter fullscreen mode Exit fullscreen mode

}

Enter fullscreen mode Exit fullscreen mode


plaintext
bash

Apache Bench — Simple Load Test

ab -n 1000 -c 100 \
-H "Authorization: Bearer TOKEN" \
https://myapp.com/api/v1/products

Output:

Requests per second: 450 [#/sec]

Time per request: 222 ms

Failed requests: 0

k6 — Advanced Load Testing

k6/load-test.js

import http from 'k6/http';
import { check, sleep } from 'k6';

export let options = {
stages: [
{ duration: '2m', target: 100 }, // Ramp up
{ duration: '5m', target: 100 }, // Stay
{ duration: '2m', target: 200 }, // Spike
{ duration: '5m', target: 200 }, // Stay
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500'], // ৯৫% Request ৫০০ms এর মধ্যে
http_req_failed: ['rate<0.01'], // ১% এর কম Error
},
};

export default function () {
let response = http.get('https://myapp.com/api/v1/products', {
headers: { Authorization: 'Bearer TOKEN' }
});

check(response, {
    'status is 200':          (r) => r.status === 200,
    'response time < 500ms':  (r) => r.timings.duration < 500,
    'has data':               (r) => JSON.parse(r.body).data.length > 0,
});

sleep(1);
Enter fullscreen mode Exit fullscreen mode

}

// চালান:
// k6 run k6/load-test.js


---

📋 **PART 7 — Performance Checklist**
Enter fullscreen mode Exit fullscreen mode


php
php
// app/Console/Commands/PerformanceAudit.php
class PerformanceAudit extends Command
{
protected $signature = 'app:performance-audit';
protected $description = 'Performance সমস্যা খুঁজুন';

public function handle(): void
{
    $this->info('🔍 Performance Audit শুরু হচ্ছে...');
    $issues = [];

    // ১. Cache Check
    if (!config('cache.default') === 'redis') {
        $issues[] = '⚠️ Redis Cache ব্যবহার করুন';
    }

    // ২. Queue Check
    if (config('queue.default') === 'sync') {
        $issues[] = '⚠️ Sync Queue Production এ ব্যবহার করবেন না';
    }

    // ৩. Debug Mode Check
    if (config('app.debug')) {
        $issues[] = '❌ APP_DEBUG=true Production এ বিপজ্জনক!';
    }

    // ৪. Slow Query Check
    $slowQueries = DB::select("
        SELECT query_time, sql_text
        FROM mysql.slow_log
        WHERE query_time > 1
        ORDER BY query_time DESC
        LIMIT 10
    ");

    if (!empty($slowQueries)) {
        $issues[] = '⚠️ ' . count($slowQueries) . 'টা Slow Query পাওয়া গেছে';
    }

    // ৫. Missing Index Check
    $missingIndexes = DB::select("
        SELECT table_name, column_name
        FROM information_schema.columns
        WHERE table_schema = ?
        AND column_name LIKE '%_id'
        AND column_name NOT IN (
            SELECT column_name
            FROM information_schema.statistics
            WHERE table_schema = ?
        )
    ", [config('database.connections.mysql.database'),
        config('database.connections.mysql.database')]);

    if (!empty($missingIndexes)) {
        $issues[] = '⚠️ ' . count($missingIndexes) . 'টা Foreign Key এ Index নেই';
    }

    // Result
    if (empty($issues)) {
        $this->info('✅ কোনো সমস্যা পাওয়া যায়নি!');
    } else {
        $this->error('❌ ' . count($issues) . 'টা সমস্যা পাওয়া গেছে:');
        foreach ($issues as $issue) {
            $this->line("  {$issue}");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

}
// চালান: php artisan app:performance-audit




---

📝 **সংক্ষেপে মনে রাখুন**
**Profiling আগে, Optimize পরে** — অনুমান না করে আগে Profile করুন, সমস্যা খুঁজুন, তারপর Fix করুন।
**Database** — N+1 Query সবচেয়ে Common সমস্যা। Eager Loading, Index, এবং Select Specific Columns ব্যবহার করুন।

**Cache** — Multi-level Cache ব্যবহার করুন। Local Array → Redis → Database এই ক্রমে।

**Load Balancing** — Nginx বা HAProxy দিয়ে Traffic ভাগ করুন। Least Connection Algorithm বেশিরভাগ ক্ষেত্রে ভালো।

**Testing** — Performance Test CI/CD এ যোগ করুন — যাতে Regression ধরা পড়ে।

**Laravel Specific** — `php artisan optimize` Deploy এ চালান, OPcache Enable করুন, Queue Worker আলাদা রাখুন।
Enter fullscreen mode Exit fullscreen mode

Top comments (0)