DEV Community

Ruhul Amin Sujon
Ruhul Amin Sujon

Posted on • Edited 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!

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);
    }
}

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

📊 PART 7 — Full Cache Layer — Real Project

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()
        ]);
    }
}

// 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");
    }
}
// চালান: php artisan cache:warm
Enter fullscreen mode Exit fullscreen mode

📈 PART 8 — Cache Monitoring

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);
    }
}

// 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 কেন গুরুত্বপূর্ণ?

একটা 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
Enter fullscreen mode Exit fullscreen mode

🔵 PART 1 — Authentication vs Authorization

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 নম্বর আছে)
Enter fullscreen mode Exit fullscreen mode

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

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)
Enter fullscreen mode Exit fullscreen mode

Laravel-এ JWT Implementation:

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',
    ],
],
Enter fullscreen mode Exit fullscreen mode

JWT Auth Controller — Complete Implementation:

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 করুন:

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:

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।

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!]
Enter fullscreen mode Exit fullscreen mode

Laravel Passport — Full OAuth2 Server:

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):

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:

bash
composer require laravel/socialite
Enter fullscreen mode Exit fullscreen mode
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 এর ধরন:

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 এর জন্য
Enter fullscreen mode Exit fullscreen mode

Laravel-এ Encryption:

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),
        ];
    }
}

// ২. 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 — সঠিকভাবে:

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 — মারাত্মক!
Enter fullscreen mode Exit fullscreen mode

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

bash
composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
php artisan migrate
Enter fullscreen mode Exit fullscreen mode
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',
        ]);
    }
}

// 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'));
    }
}

// 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']);
    }
}

// Blade এ Permission Check
@can('products.create')
    <button>নত Product  কর</button>
@endcan

@role('admin|super-admin')
    <a href="/admin">Admin Panel</a>
@endrole
Enter fullscreen mode Exit fullscreen mode

🔒 PART 6 — Common Security Vulnerabilities
SQL Injection থেকে রক্ষা:

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();
Enter fullscreen mode Exit fullscreen mode

XSS (Cross-Site Scripting) থেকে রক্ষা:

php
// ❌ ভুল — XSS এর সুযোগ!
return response("<p>Hello {$request->name}</p>");

// ✅ সঠিক — Escape করুন
return response("<p>Hello " . e($request->name) . "</p>");

// ✅ 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);
    }
}

// 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:

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']);
});
Enter fullscreen mode Exit fullscreen mode

Rate Limiting — Advanced:

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

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;
    }
}

// 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)

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 কী?

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

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

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

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

সমস্যা (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          │
└─────────────────────┘  └─────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Laravel Project এর জন্য Dockerfile:

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"]
Enter fullscreen mode Exit fullscreen mode

Docker Compose — Local Development:

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
Enter fullscreen mode Exit fullscreen mode

Nginx Config:

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;
}

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 কী?

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] ✅
Enter fullscreen mode Exit fullscreen mode

GitHub Actions — CI/CD Pipeline:

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

  # ─────────────────────────────────
  # 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

  # ─────────────────────────────────
  # 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

  # ─────────────────────────────────
  # 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 সফল!"

  # ─────────────────────────────────
  # 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:

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।

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) │  │     │
│  │  └──────────┘  │  │  └──────────┘  │     │
│  └────────────────┘  └────────────────┘     │
└─────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Laravel App এর জন্য Kubernetes Manifests:

yaml
# k8s/namespace.yml
apiVersion: v1
kind: Namespace
metadata:
  name: laravel-app
  labels:
    app: laravel
Enter fullscreen mode Exit fullscreen mode
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
# 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
# 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

  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60   # ১ মিনিট দেখো
      policies:
        - type: Pods
          value: 3                      # একসাথে ৩টা যোগ করো
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300  # ৫ মিনিট দেখো তারপর কমাও
Enter fullscreen mode Exit fullscreen mode
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
Enter fullscreen mode Exit fullscreen mode

Kubernetes Commands — Daily Use:

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
Enter fullscreen mode Exit fullscreen mode

🏥 PART 4 — Health Check Endpoint

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 কেন গুরুত্বপূর্ণ?
গবেষণা বলছে:

├── 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 চলে যাবে
Enter fullscreen mode Exit fullscreen mode

🔍 PART 1 — Profiling (সমস্যা খুঁজে বের করা)
Profiling কী?
Profiling মানে আপনার অ্যাপের কোথায় সময় নষ্ট হচ্ছে সেটা খুঁজে বের করা। Optimize করার আগে অবশ্যই Profile করুন।

❌ ভুল পদ্ধতি:
"মনে হচ্ছে Database Slow" → সেটা Optimize করলাম
→ কোনো পার্থক্য নেই!

✅ সঠিক পদ্ধতি:
Profile করুন → সমস্যা খুঁজুন → সেটা Fix করুন
→ আবার Profile করুন → Verify করুন
Enter fullscreen mode Exit fullscreen mode

Laravel Telescope — Development Profiling:

bash
composer require laravel/telescope --dev
php artisan telescope:install
php artisan migrate
Enter fullscreen mode Exit fullscreen mode
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*'],
        ],
    ],
];

// 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:

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;
    }
}

// ব্যবহার
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 খুঁজুন:

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(),
                ]);
            }
        });
    }
}

// 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:

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']);
        });
    }
}

// ─────────────────────────────────────
// ৪. 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();
Enter fullscreen mode Exit fullscreen mode

Laravel Cache Optimization:

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:

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
// 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 কী?

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 এ পাঠাও
Enter fullscreen mode Exit fullscreen mode

Nginx Load Balancer Configuration:

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;
}

# 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;
}

# ─────────────────────────────────────
# 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:

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

# ─────────────────────────────────────
# 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

# ─────────────────────────────────────
# 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

# ─────────────────────────────────────
# Stats Dashboard
# ─────────────────────────────────────
listen stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 30s
    stats auth admin:secret_password
Enter fullscreen mode Exit fullscreen mode

📊 PART 4 — Application Performance Monitoring (APM)

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); // ৭ দিন রাখুন
        });
    }
}

// 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

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
    ],
],

// 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

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
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] (mean)
# 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);
}

// চালান:
// k6 run k6/load-test.js
Enter fullscreen mode Exit fullscreen mode

📋 PART 7 — Performance Checklist

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}");
            }
        }
    }
}
// চালান: php artisan app:performance-audit
Enter fullscreen mode Exit fullscreen mode

📝 সংক্ষেপে মনে রাখুন

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 Specificphp artisan optimize Deploy এ চালান, OPcache Enable করুন, Queue Worker আলাদা রাখুন।

Q:10- Cloud Services (AWS, GCP, Azure)
Answer:

☁️ Cloud কী?

আগে (Traditional):
নিজের Server কিনুন → Rack এ রাখুন → Power দিন
→ Network লাগান → Maintain করুন → ব্যয়বহুল!

এখন (Cloud):
Credit Card দিন → API Call করুন → Server Ready!
→ Use করুন → Pay করুন → Scale করুন

Cloud এর সুবিধা:
├── Pay-as-you-go    → ব্যবহার অনুযায়ী পেমেন্ট
├── Auto Scaling     → Traffic বাড়লে Auto বড় হয়
├── Global Reach     → বিশ্বের যেকোনো জায়গায় Deploy
├── Managed Services → Database, Cache সব Managed
└── High Availability → ৯৯.৯৯% Uptime গ্যারান্টি

তিনটা বড় Cloud Provider:
AWS   → সবচেয়ে বড়, সবচেয়ে বেশি Service (200+)
GCP   → Google এর, AI/ML এ সেরা
Azure → Microsoft এর, Enterprise এ জনপ্রিয়
Enter fullscreen mode Exit fullscreen mode

🟡 PART 1 — AWS (Amazon Web Services)
Laravel Backend Engineer এর জন্য গুরুত্বপূর্ণ AWS Services:

EC2    Virtual Server (আপন Laravel চলব)
RDS    Managed MySQL/PostgreSQL
S3     File Storage (Image, Document)
SQS    Message Queue
ElastiCache  Managed Redis
CloudFront   CDN (Static Files  )
Lambda       Serverless Functions
ECS/EKS      Container/Kubernetes
Route 53     DNS Management
SES          Email Service
SNS          Push Notification
Enter fullscreen mode Exit fullscreen mode

AWS SDK — Laravel Setup:

bash
composer require aws/aws-sdk-php
composer require league/flysystem-aws-s3-v3
php// .env
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=abc123...
AWS_DEFAULT_REGION=ap-southeast-1  // Singapore — Bangladesh এর কাছে
AWS_BUCKET=myapp-production
AWS_URL=https://myapp-production.s3.amazonaws.com

// config/filesystems.php
's3' => [
    'driver'                  => 's3',
    'key'                     => env('AWS_ACCESS_KEY_ID'),
    'secret'                  => env('AWS_SECRET_ACCESS_KEY'),
    'region'                  => env('AWS_DEFAULT_REGION'),
    'bucket'                  => env('AWS_BUCKET'),
    'url'                     => env('AWS_URL'),
    'endpoint'                => env('AWS_ENDPOINT'), // LocalStack এর জন্য
    'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
    'throw'                   => false,
],

AWS S3  File Storage:
php// app/Services/S3StorageService.php
class S3StorageService
{
    // ১. File Upload করুন
    public function uploadFile(
        UploadedFile $file,
        string $folder = 'uploads'
    ): array {
        // Unique Filename তৈরি করুন
        $filename  = Str::uuid() . '.' . $file->getClientOriginalExtension();
        $path      = "{$folder}/" . date('Y/m/d') . "/{$filename}";

        // S3 তে Upload করুন
        $storedPath = Storage::disk('s3')->putFileAs(
            dirname($path),
            $file,
            basename($path),
            [
                'visibility'  => 'public',
                'ContentType' => $file->getMimeType(),
                'Metadata'    => [
                    'original_name' => $file->getClientOriginalName(),
                    'uploaded_by'   => (string) auth()->id(),
                ],
            ]
        );

        return [
            'path'     => $storedPath,
            'url'      => Storage::disk('s3')->url($storedPath),
            'size'     => $file->getSize(),
            'mimetype' => $file->getMimeType(),
        ];
    }

    // ২. Image Resize করে Upload করুন
    public function uploadImage(
        UploadedFile $file,
        array $sizes = []
    ): array {
        $defaultSizes = [
            'original' => [null, null],
            'large'    => [1200, 800],
            'medium'   => [600, 400],
            'thumb'    => [150, 150],
        ];

        $sizes    = array_merge($defaultSizes, $sizes);
        $uploaded = [];
        $baseName = Str::uuid();
        $ext      = 'webp'; // Modern Format

        foreach ($sizes as $sizeName => [$width, $height]) {
            $image = Image::make($file);

            if ($width && $height) {
                $image->fit($width, $height, function ($constraint) {
                    $constraint->upsize(); // ছোট Image Stretch করবে না
                });
            }

            // WebP Format এ Convert করুন — ছোট Size
            $imageData = $image->encode('webp', 85)->getEncoded();
            $path      = "images/{$sizeName}/" . date('Y/m') . "/{$baseName}.{$ext}";

            Storage::disk('s3')->put($path, $imageData, [
                'visibility'  => 'public',
                'ContentType' => 'image/webp',
                'CacheControl'=> 'max-age=31536000', // 1 Year Cache
            ]);

            $uploaded[$sizeName] = [
                'path' => $path,
                'url'  => Storage::disk('s3')->url($path),
            ];
        }

        return $uploaded;
    }

    // ৩. Pre-signed URL — Private File কিছু সময়ের জন্য Share করুন
    public function getTemporaryUrl(
        string $path,
        int $minutes = 30
    ): string {
        return Storage::disk('s3')->temporaryUrl(
            $path,
            now()->addMinutes($minutes),
            [
                'ResponseContentDisposition' =>
                    'attachment; filename="' . basename($path) . '"',
            ]
        );
    }

    // ৪. Direct Upload URL — Client সরাসরি S3 তে Upload করবে
    //    Server এ File আসবে না — দ্রুত ও কম Server Load
    public function getPresignedUploadUrl(
        string $folder,
        string $mimeType,
        int $maxSize = 10
    ): array {
        $s3Client = new S3Client([
            'version'     => 'latest',
            'region'      => config('filesystems.disks.s3.region'),
            'credentials' => [
                'key'    => config('filesystems.disks.s3.key'),
                'secret' => config('filesystems.disks.s3.secret'),
            ],
        ]);

        $key     = $folder . '/' . Str::uuid() . '.' . $this->getExtFromMime($mimeType);
        $command = $s3Client->getCommand('PutObject', [
            'Bucket'      => config('filesystems.disks.s3.bucket'),
            'Key'         => $key,
            'ContentType' => $mimeType,
        ]);

        $request = $s3Client->createPresignedRequest($command, '+10 minutes');

        return [
            'upload_url' => (string) $request->getUri(),
            'file_key'   => $key,
            'file_url'   => Storage::disk('s3')->url($key),
            'expires_in' => 600, // ১০ মিনিট
        ];
    }

    // ৫. Bulk Delete
    public function deleteFiles(array $paths): void
    {
        Storage::disk('s3')->delete($paths);
    }
}

// Controller এ ব্যবহার
class ProductImageController extends Controller
{
    public function store(
        Request $request,
        S3StorageService $storage
    ): JsonResponse {
        $request->validate([
            'image' => 'required|image|max:5120|mimes:jpeg,png,jpg,webp',
        ]);

        $images = $storage->uploadImage($request->file('image'));

        // DB তে Save করুন
        $productImage = ProductImage::create([
            'product_id'   => $request->product_id,
            'original_url' => $images['original']['url'],
            'large_url'    => $images['large']['url'],
            'medium_url'   => $images['medium']['url'],
            'thumb_url'    => $images['thumb']['url'],
            'paths'        => json_encode($images),
        ]);

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

    // Direct Upload URL দিন — Large File এর জন্য
    public function getUploadUrl(
        Request $request,
        S3StorageService $storage
    ): JsonResponse {
        $presigned = $storage->getPresignedUploadUrl(
            'products',
            $request->mime_type,
            10 // 10MB max
        );

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

AWS SQS — Laravel Queue:

php
// .env
SQS_KEY=AKIA...
SQS_SECRET=abc123...
SQS_PREFIX=https://sqs.ap-southeast-1.amazonaws.com/123456789
SQS_QUEUE=myapp-production
SQS_REGION=ap-southeast-1
QUEUE_CONNECTION=sqs

// config/queue.php
'sqs' => [
    'driver'      => 'sqs',
    'key'         => env('SQS_KEY'),
    'secret'      => env('SQS_SECRET'),
    'prefix'      => env('SQS_PREFIX'),
    'queue'       => env('SQS_QUEUE'),
    'suffix'      => env('SQS_SUFFIX'),
    'region'      => env('SQS_REGION'),
    'after_commit'=> false,
],

// Priority Queue — আলাদা SQS Queue
// config/queue.php
'sqs-high' => [
    'driver' => 'sqs',
    'queue'  => 'myapp-high-priority',
    // ...
],

// Job Dispatch করুন
SendOrderConfirmation::dispatch($order)
    ->onConnection('sqs')
    ->onQueue('myapp-high-priority');

// Dead Letter Queue — Failed Jobs Handle করুন
class ProcessPaymentJob implements ShouldQueue
{
    public int $tries   = 3;
    public int $timeout = 30;

    public function failed(\Throwable $exception): void
    {
        // Failed Job SNS দিয়ে Alert করুন
        $sns = new SnsClient([
            'region'  => config('services.sns.region'),
            'version' => 'latest',
        ]);

        $sns->publish([
            'TopicArn' => config('services.sns.alert_topic'),
            'Message'  => json_encode([
                'job'     => self::class,
                'error'   => $exception->getMessage(),
                'payload' => $this->order->id,
            ]),
            'Subject'  => 'Payment Job Failed',
        ]);
    }
}
Enter fullscreen mode Exit fullscreen mode

AWS RDS — Database Setup:

php
// config/database.php — RDS Multi-AZ Setup
'mysql' => [
    'driver'   => 'mysql',
    // Write → RDS Primary
    'write' => [
        'host' => env('DB_WRITE_HOST', 'myapp.cluster-xxx.ap-southeast-1.rds.amazonaws.com'),
    ],
    // Read → RDS Read Replica
    'read' => [
        ['host' => env('DB_READ_HOST_1', 'myapp.cluster-ro-xxx.ap-southeast-1.rds.amazonaws.com')],
        ['host' => env('DB_READ_HOST_2', 'myapp.cluster-ro-yyy.ap-southeast-1.rds.amazonaws.com')],
    ],
    'sticky'   => true,
    'database' => env('DB_DATABASE'),
    'username' => env('DB_USERNAME'),
    'password' => env('DB_PASSWORD'),
    'charset'  => 'utf8mb4',
    'options'  => [
        // SSL — RDS সবসময় SSL চায়
        PDO::MYSQL_ATTR_SSL_CA => '/etc/ssl/certs/rds-combined-ca-bundle.pem',
    ],
],

// RDS Proxy — Connection Pooling
// Lambda বা ECS এ অনেক Connection হলে RDS Proxy ব্যবহার করুন
'mysql_proxy' => [
    'driver'   => 'mysql',
    'host'     => env('RDS_PROXY_ENDPOINT'),
    'database' => env('DB_DATABASE'),
    'username' => env('DB_USERNAME'),
    'password' => env('DB_PASSWORD'),
    // IAM Authentication
    'options'  => [
        PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => true,
    ],
],

AWS ElastiCache  Redis:
php// .env
REDIS_HOST=myapp.xxxxx.ng.0001.apse1.cache.amazonaws.com
REDIS_PASSWORD=null
REDIS_PORT=6379

// ElastiCache Cluster Mode
// config/database.php
'redis' => [
    'client' => 'phpredis',
    'clusters' => [
        'default' => [
            [
                'host'     => env('REDIS_CLUSTER_HOST_1'),
                'password' => null,
                'port'     => 6379,
                'database' => 0,
            ],
            [
                'host'     => env('REDIS_CLUSTER_HOST_2'),
                'password' => null,
                'port'     => 6379,
                'database' => 0,
            ],
        ],
    ],
    'options' => [
        'cluster'   => 'redis',
        'prefix'    => 'myapp_' . env('APP_ENV') . '_',
    ],
],
Enter fullscreen mode Exit fullscreen mode

AWS SES — Email Service:

php
// .env
MAIL_MAILER=ses
AWS_SES_KEY=AKIA...
AWS_SES_SECRET=abc123...
AWS_SES_REGION=ap-southeast-1

// config/mail.php
'ses' => [
    'transport' => 'ses',
],

// config/services.php
'ses' => [
    'key'    => env('AWS_SES_KEY'),
    'secret' => env('AWS_SES_SECRET'),
    'region' => env('AWS_SES_REGION', 'ap-southeast-1'),
    'options' => [
        'ConfigurationSetName' => 'myapp-production',
        'Tags' => [
            ['Name' => 'environment', 'Value' => 'production'],
        ],
    ],
],

// Email Bounce ও Complaint Handle করুন
// SNS Webhook Route
Route::post('/webhooks/ses', [SesWebhookController::class, 'handle']);

class SesWebhookController extends Controller
{
    public function handle(Request $request): JsonResponse
    {
        $message = json_decode($request->getContent(), true);
        $type    = $message['notificationType'] ?? $message['Type'];

        match($type) {
            // SubscriptionConfirmation
            'SubscriptionConfirmation' => $this->confirmSubscription($message),

            // Email Bounce — Invalid Email
            'Bounce' => $this->handleBounce($message['bounce']),

            // Complaint — Spam Report
            'Complaint' => $this->handleComplaint($message['complaint']),

            // Delivery Success
            'Delivery' => $this->handleDelivery($message['delivery']),

            default => null,
        };

        return response()->json(['status' => 'ok']);
    }

    private function handleBounce(array $bounce): void
    {
        foreach ($bounce['bouncedRecipients'] as $recipient) {
            // Bounced Email Blacklist করুন
            EmailBlacklist::firstOrCreate([
                'email'  => $recipient['emailAddress'],
                'reason' => 'bounce',
                'type'   => $bounce['bounceType'], // Permanent/Transient
            ]);

            // User কে Disable করুন (Permanent Bounce)
            if ($bounce['bounceType'] === 'Permanent') {
                User::where('email', $recipient['emailAddress'])
                    ->update(['email_invalid' => true]);
            }
        }
    }

    private function handleComplaint(array $complaint): void
    {
        foreach ($complaint['complainedRecipients'] as $recipient) {
            // Unsubscribe করুন
            User::where('email', $recipient['emailAddress'])
                ->update(['marketing_emails' => false]);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

AWS Lambda — Serverless:

php
// Serverless Laravel — Bref ব্যবহার করুন
// composer require bref/bref bref/laravel-bridge

// serverless.yml
//
// service: myapp
// provider:
//   name: aws
//   region: ap-southeast-1
//   runtime: provided.al2
//   environment:
//     APP_ENV: production
//     DB_HOST: ${env:DB_HOST}
//
// functions:
//   web:
//     handler: public/index.php
//     layers:
//       - ${bref:layer.php-83-fpm}
//     events:
//       - httpApi: '*'
//
//   queue:
//     handler: worker.php
//     layers:
//       - ${bref:layer.php-83}
//     events:
//       - sqs:
//           arn: !GetAtt OrderQueue.Arn
//           batchSize: 10

// Lambda Function — Image Processing
class ImageProcessingLambda
{
    public function handle(array $event): array
    {
        // S3 Event থেকে File Info নিন
        $s3Event = $event['Records'][0]['s3'];
        $bucket  = $s3Event['bucket']['name'];
        $key     = urldecode($s3Event['object']['key']);

        // S3 থেকে Image Download করুন
        $s3 = new S3Client(['region' => 'ap-southeast-1', 'version' => 'latest']);
        $result = $s3->getObject(['Bucket' => $bucket, 'Key' => $key]);
        $image  = $result['Body']->getContents();

        // Image Process করুন (Resize, Watermark)
        $processed = $this->processImage($image);

        // Processed Image S3 তে Save করুন
        $newKey = str_replace('uploads/', 'processed/', $key);
        $s3->putObject([
            'Bucket' => $bucket,
            'Key'    => $newKey,
            'Body'   => $processed,
            'ContentType' => 'image/webp',
        ]);

        return ['statusCode' => 200, 'body' => 'Processed: ' . $newKey];
    }
}
Enter fullscreen mode Exit fullscreen mode

🔵 PART 2 — GCP (Google Cloud Platform)
Laravel এর জন্য গুরুত্বপূর্ণ GCP Services:

Compute Engine  → Virtual Machine (EC2 এর মতো)
Cloud Run       → Serverless Container (সবচেয়ে সহজ Deploy)
Cloud SQL       → Managed MySQL/PostgreSQL
Cloud Storage   → File Storage (S3 এর মতো)
Cloud Pub/Sub   → Message Queue (SQS এর মতো)
Memorystore     → Managed Redis (ElastiCache এর মতো)
Cloud CDN       → CDN (CloudFront এর মতো)
BigQuery        → Data Warehouse (Analytics)
Vertex AI       → AI/ML Services
Cloud Functions → Serverless (Lambda এর মতো)
Enter fullscreen mode Exit fullscreen mode

Cloud Run — সবচেয়ে সহজ Laravel Deploy:

dockerfile
# Dockerfile — Cloud Run এর জন্য
FROM php:8.3-fpm-alpine

# Cloud Run PORT Environment Variable
ENV PORT=8080

# Nginx + PHP-FPM একসাথে চালানোর জন্য Supervisor
RUN apk add --no-cache nginx supervisor

# PHP Extensions
RUN docker-php-ext-install pdo pdo_mysql opcache

# App Copy করুন
WORKDIR /var/www/html
COPY . .
RUN composer install --no-dev --optimize-autoloader

# Nginx Config
COPY docker/nginx/cloud-run.conf /etc/nginx/http.d/default.conf

# Start Script
COPY docker/start.sh /start.sh
RUN chmod +x /start.sh

EXPOSE 8080
CMD ["/start.sh"]
Enter fullscreen mode Exit fullscreen mode
bash
# Cloud Run Deploy করুন
# gcloud CLI ব্যবহার করুন

# ১. Docker Image Build ও Push করুন
gcloud builds submit \
  --tag gcr.io/MY_PROJECT_ID/laravel-app

# ২. Cloud Run এ Deploy করুন
gcloud run deploy laravel-app \
  --image gcr.io/MY_PROJECT_ID/laravel-app \
  --platform managed \
  --region asia-southeast1 \
  --allow-unauthenticated \
  --min-instances 1 \
  --max-instances 20 \
  --memory 512Mi \
  --cpu 1 \
  --concurrency 80 \
  --set-env-vars APP_ENV=production \
  --set-secrets DB_PASSWORD=db-password:latest \
  --set-cloudsql-instances MY_PROJECT_ID:asia-southeast1:myapp-db
Enter fullscreen mode Exit fullscreen mode

GCP Cloud Storage — Laravel:

bash
composer require google/cloud-storage
php// config/filesystems.php
'gcs' => [
    'driver'         => 'gcs',
    'key_file_path'  => env('GOOGLE_CLOUD_KEY_FILE', null),
    'project_id'     => env('GOOGLE_CLOUD_PROJECT_ID'),
    'bucket'         => env('GOOGLE_CLOUD_STORAGE_BUCKET'),
    'path_prefix'    => env('GOOGLE_CLOUD_STORAGE_PATH_PREFIX', null),
    'api_uri'        => env('GOOGLE_CLOUD_STORAGE_API_URI', null),
    'visibility'     => 'public',
    'metadata'       => ['cacheControl' => 'public,max-age=86400'],
],

// Signed URL — Private File Share
class GCSService
{
    private StorageClient $storage;
    private Bucket $bucket;

    public function __construct()
    {
        $this->storage = new StorageClient([
            'projectId'   => config('filesystems.disks.gcs.project_id'),
            'keyFilePath' => config('filesystems.disks.gcs.key_file_path'),
        ]);

        $this->bucket = $this->storage->bucket(
            config('filesystems.disks.gcs.bucket')
        );
    }

    public function getSignedUrl(string $path, int $minutes = 60): string
    {
        $object = $this->bucket->object($path);

        return $object->signedUrl(
            new \DateTime("+{$minutes} minutes"),
            ['method' => 'GET']
        );
    }

    // Resumable Upload — Large File
    public function createResumableUpload(
        string $path,
        string $contentType
    ): string {
        $object = $this->bucket->object($path);

        return $object->beginSignedUploadSession([
            'metadata' => ['contentType' => $contentType],
        ]);
    }
}
Enter fullscreen mode Exit fullscreen mode

GCP Pub/Sub — Laravel Queue:

php
// composer require google/cloud-pubsub
// composer require kainaat/laravel-google-pubsub

// config/queue.php
'pubsub' => [
    'driver'     => 'pubsub',
    'project_id' => env('GOOGLE_CLOUD_PROJECT_ID'),
    'queue'      => env('PUBSUB_QUEUE', 'laravel-queue'),
    'subscriber' => env('PUBSUB_SUBSCRIBER', 'laravel-subscriber'),
],

// Pub/Sub Push Endpoint — Cloud Run এ Webhook
class PubSubController extends Controller
{
    public function handle(Request $request): JsonResponse
    {
        // Pub/Sub Message Decode করুন
        $payload = $request->json('message.data');
        $data    = json_decode(base64_decode($payload), true);

        $jobClass = $data['job'];
        $payload  = $data['payload'];

        // Job চালান
        dispatch(new $jobClass($payload));

        return response()->json(['status' => 'ok']);
    }
}
Enter fullscreen mode Exit fullscreen mode

GCP BigQuery — Analytics:

php
// composer require google/cloud-bigquery

// app/Services/BigQueryService.php
class BigQueryService
{
    private BigQueryClient $bigQuery;
    private string $dataset;

    public function __construct()
    {
        $this->bigQuery = new BigQueryClient([
            'projectId' => config('services.bigquery.project_id'),
        ]);
        $this->dataset  = config('services.bigquery.dataset');
    }

    // Event Log করুন BigQuery তে
    public function logEvent(string $eventName, array $data): void
    {
        $table = $this->bigQuery
                      ->dataset($this->dataset)
                      ->table('events');

        $table->insertRows([[
            'data' => array_merge($data, [
                'event_name'  => $eventName,
                'occurred_at' => now()->toIso8601String(),
                'session_id'  => session()->getId(),
                'user_id'     => auth()->id(),
                'ip_address'  => request()->ip(),
            ]),
        ]]);
    }

    // Sales Analytics Query
    public function getMonthlySalesReport(int $year): array
    {
        $query = "
            SELECT
                FORMAT_DATE('%Y-%m', DATE(created_at)) as month,
                COUNT(*) as total_orders,
                SUM(total) as revenue,
                AVG(total) as avg_order_value,
                COUNT(DISTINCT user_id) as unique_customers
            FROM `{$this->dataset}.orders`
            WHERE
                EXTRACT(YEAR FROM created_at) = @year
                AND status = 'completed'
            GROUP BY month
            ORDER BY month
        ";

        $queryJobConfig = $this->bigQuery->query($query)->parameters([
            'year' => $year,
        ]);

        $queryResults = $this->bigQuery->runQuery($queryJobConfig);

        return iterator_to_array($queryResults->rows());
    }
}
Enter fullscreen mode Exit fullscreen mode

🔷 PART 3 — Azure (Microsoft Azure)
Laravel এর জন্য গুরুত্বপূর্ণ Azure Services:

Azure VM          → Virtual Machine
Azure App Service → Managed Web App Hosting
Azure Database    → Managed MySQL/PostgreSQL
Blob Storage      → File Storage (S3 এর মতো)
Azure Cache       → Managed Redis
Service Bus       → Message Queue
Azure CDN         → CDN
Azure Functions   → Serverless
AKS               → Managed Kubernetes
Key Vault         → Secret Management
Active Directory  → Enterprise Auth (OAuth2/SAML)
Enter fullscreen mode Exit fullscreen mode

Azure Blob Storage — Laravel:

bash
composer require matthewbdaly/laravel-azure-storage
php// .env
AZURE_STORAGE_NAME=myappstorageaccount
AZURE_STORAGE_KEY=abc123==
AZURE_STORAGE_CONTAINER=production
AZURE_STORAGE_URL=https://myappstorageaccount.blob.core.windows.net

// config/filesystems.php
'azure' => [
    'driver'    => 'azure',
    'name'      => env('AZURE_STORAGE_NAME'),
    'key'       => env('AZURE_STORAGE_KEY'),
    'container' => env('AZURE_STORAGE_CONTAINER'),
    'url'       => env('AZURE_STORAGE_URL'),
    'prefix'    => null,
],

// Azure SAS Token — Temporary Access
class AzureBlobService
{
    public function generateSasToken(string $blobPath): string
    {
        $accountName = config('filesystems.disks.azure.name');
        $accountKey  = config('filesystems.disks.azure.key');
        $container   = config('filesystems.disks.azure.container');

        $expiry      = now()->addHour()->toRfc1123String();
        $resource    = "/{$accountName}/{$container}/{$blobPath}";

        // SAS Token Generate করুন
        $canonicalizedResource = "/blob{$resource}";
        $stringToSign = implode("\n", [
            'r',                    // Read Permission
            '',                     // Start time
            $expiry,               // Expiry
            $canonicalizedResource,
            '',                    // Identifier
            '',                    // IP
            'https',               // Protocol
            '2020-04-08',         // Version
            'b',                   // Blob
            '',                    // Snapshot
            '',
            '',
            '',
        ]);

        $signature = base64_encode(
            hash_hmac('sha256', $stringToSign, base64_decode($accountKey), true)
        );

        return http_build_query([
            'sv'  => '2020-04-08',
            'sr'  => 'b',
            'se'  => $expiry,
            'sp'  => 'r',
            'spr' => 'https',
            'sig' => $signature,
        ]);
    }
}
Enter fullscreen mode Exit fullscreen mode

Azure Key Vault — Secret Management:

php
// Secrets Code এ না রেখে Key Vault এ রাখুন
// composer require azure/azure-sdk-for-php

// app/Providers/AzureKeyVaultServiceProvider.php
class AzureKeyVaultServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        if (app()->environment('production')) {
            $this->loadSecretsFromKeyVault();
        }
    }

    private function loadSecretsFromKeyVault(): void
    {
        $credential = new DefaultAzureCredential();
        $client     = new SecretClient(
            env('AZURE_KEY_VAULT_URL'),
            $credential
        );

        // Secret গুলো Key Vault থেকে নিন
        $secrets = [
            'DB_PASSWORD',
            'REDIS_PASSWORD',
            'JWT_SECRET',
            'AWS_SECRET_ACCESS_KEY',
        ];

        foreach ($secrets as $secretName) {
            try {
                $secret = $client->getSecret($secretName);
                config([strtolower($secretName) => $secret->getValue()]);
            } catch (\Exception $e) {
                Log::error("Key Vault secret load failed: {$secretName}");
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

🏗️ PART 4 — Multi-Cloud Architecture
Real Laravel Project — Cloud Services একসাথে:

php
// app/Services/CloudStorageRouter.php
// Different Files Different Cloud এ — Cost Optimize

class CloudStorageRouter
{
    // Primary: AWS S3
    // Backup: GCP Storage
    // CDN: CloudFront

    public function upload(UploadedFile $file, string $type): array
    {
        $driver = match($type) {
            'product_image'  => 's3',      // AWS S3 + CloudFront
            'user_document'  => 'azure',   // Azure Blob (GDPR Compliance)
            'analytics_data' => 'gcs',     // GCP (BigQuery এর কাছে)
            'backup'         => 's3',      // AWS
            default          => 's3',
        };

        $path = Storage::disk($driver)->putFile(
            $type . '/' . date('Y/m'),
            $file,
            'public'
        );

        return [
            'path'   => $path,
            'url'    => Storage::disk($driver)->url($path),
            'driver' => $driver,
        ];
    }
}

// app/Services/InfrastructureHealthService.php
class InfrastructureHealthService
{
    public function checkAll(): array
    {
        return [
            'aws'   => $this->checkAWS(),
            'gcp'   => $this->checkGCP(),
            'azure' => $this->checkAzure(),
        ];
    }

    private function checkAWS(): array
    {
        $checks = [];

        // S3 Check
        try {
            Storage::disk('s3')->exists('health-check.txt');
            $checks['s3'] = 'healthy';
        } catch (\Exception $e) {
            $checks['s3'] = 'unhealthy: ' . $e->getMessage();
        }

        // SQS Check
        try {
            Queue::connection('sqs')->size('default');
            $checks['sqs'] = 'healthy';
        } catch (\Exception $e) {
            $checks['sqs'] = 'unhealthy';
        }

        return $checks;
    }

    private function checkGCP(): array
    {
        try {
            Storage::disk('gcs')->exists('health-check.txt');
            return ['storage' => 'healthy'];
        } catch (\Exception $e) {
            return ['storage' => 'unhealthy'];
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

🚀 PART 5 — Cloud Deployment Pipeline

yaml
# .github/workflows/cloud-deploy.yml
name: Multi-Cloud Deploy

on:
  push:
    branches: [main]

jobs:
  # ─────────────────────────
  # AWS ECS Deploy
  # ─────────────────────────
  deploy-aws:
    name: 🟡 Deploy to AWS
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id:     ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region:            ap-southeast-1

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build & Push to ECR
        env:
          ECR_REGISTRY:   ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: myapp-laravel
          IMAGE_TAG:       ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

      - name: Deploy to ECS
        run: |
          aws ecs update-service \
            --cluster myapp-production \
            --service laravel-service \
            --force-new-deployment

  # ─────────────────────────
  # GCP Cloud Run Deploy
  # ─────────────────────────
  deploy-gcp:
    name: 🔵 Deploy to GCP
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
      - uses: actions/checkout@v4

      - name: Authenticate to GCP
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Deploy to Cloud Run
        uses: google-github-actions/deploy-cloudrun@v2
        with:
          service: laravel-app
          region:  asia-southeast1
          source:  .
          env_vars: |
            APP_ENV=production
          secrets: |
            DB_PASSWORD=db-password:latest
Enter fullscreen mode Exit fullscreen mode

💰 PART 6 — Cloud Cost Optimization

php
// app/Console/Commands/CloudCostOptimizer.php
class CloudCostOptimizer extends Command
{
    protected $signature   = 'cloud:optimize-costs';
    protected $description = 'Cloud Cost Optimize করুন';

    public function handle(): void
    {
        $this->info('💰 Cost Optimization চেক করছি...');

        // ১. Unused S3 Objects পরিষ্কার করুন
        $this->cleanupS3();

        // ২. Old Log Files Delete করুন
        $this->cleanupLogs();

        // ৩. Unused Cache Clear করুন
        $this->cleanupCache();
    }

    private function cleanupS3(): void
    {
        // ৯০ দিনের পুরনো Temp Files Delete করুন
        $oldFiles = Storage::disk('s3')
                           ->allFiles('temp');

        $deleted = 0;
        foreach ($oldFiles as $file) {
            $lastModified = Storage::disk('s3')
                                   ->lastModified($file);

            if ($lastModified < now()->subDays(90)->timestamp) {
                Storage::disk('s3')->delete($file);
                $deleted++;
            }
        }

        $this->line("  ✅ S3: {$deleted}টা পুরনো File মুছে ফেলা হয়েছে");
    }

    private function cleanupLogs(): void
    {
        // CloudWatch Logs — ৩০ দিনের পুরনো Logs
        $logs = new CloudWatchLogsClient([
            'region'  => 'ap-southeast-1',
            'version' => 'latest',
        ]);

        $this->line("  ✅ Log Retention Policy Set করা হয়েছে");
    }
}
Enter fullscreen mode Exit fullscreen mode

📝 সংক্ষেপে মনে রাখুন

AWS সবচেয়ে বেশি ব্যবহৃত এবং সার্ভিস সবচেয়ে বেশি। Laravel এর জন্য S3 (Storage), SQS (Queue), RDS (Database), ElastiCache (Redis), SES (Email) — এই পাঁচটা শিখলেই ৯০% কাজ হবে।

GCP AI/ML এর জন্য সেরা এবং Cloud Run দিয়ে Laravel Deploy সবচেয়ে সহজ। BigQuery দিয়ে Large-scale Analytics করা যায়।

Azure Enterprise Environment এ সেরা, বিশেষত Microsoft Stack (Active Directory, .NET) এর সাথে।

শেখার ক্রম — প্রথমে AWS S3 + SES শিখুন → তারপর SQS + ElastiCache → পরে RDS → তারপর ECS/Lambda। AWS Free Tier দিয়ে শুরু করুন।

Q:11- Monitoring (Prometheus, Grafana)

Answer:

👁️ Monitoring কেন দরকার?

Monitoring ়া:
"Server Down হয়ে গেছে!"
 Customer Phone করল
 রপর নল 😱
  ঘন পর Fix হল
 Revenue 

Monitoring সহ:
 CPU 90% হল  Alert 
 আপনি নল  আগ Fix করল
 Customer ি ঝল  

Monitoring এর তর:
├── Infrastructure   CPU, Memory, Disk, Network
├── Application      Response Time, Error Rate, Throughput
├── Business         Orders/min, Revenue, Active Users
└── User Experience  Page Load, Crash Rate
Enter fullscreen mode Exit fullscreen mode

🏗️ PART 1 — Monitoring Stack Overview

পুরো Monitoring Stack:

[Laravel App] ──Metrics──→ [Prometheus] ──→ [Grafana Dashboard]
[Nginx]       ──Logs────→  [Loki]       ──→ [Grafana Dashboard]
[MySQL]       ──Traces──→  [Tempo]      ──→ [Grafana Dashboard]
[Redis]            ↑
[Server]      ──Alerts──→  [AlertManager] → [Slack/Email/PagerDuty]

Prometheus  → Metrics Collect করে (Numbers)
Grafana     → সুন্দর Dashboard বানায়
Loki        → Logs Collect করে (Text)
Tempo       → Distributed Tracing
AlertManager→ Alert পাঠায়
Enter fullscreen mode Exit fullscreen mode

📊 PART 2 — Prometheus Setup
Prometheus কী?

Prometheus হলো Time-series Database।
প্রতি ১৫ সেকেন্ডে আপনার App এ গিয়ে
Metrics নিয়ে আসে (Scraping)।

Metrics এর ধরন:
├── Counter   → শুধু বাড়ে (Total Requests, Total Errors)
├── Gauge     → বাড়ে/কমে (Active Users, Memory Usage)
├── Histogram → Distribution (Response Time Distribution)
└── Summary   → Percentile (p95, p99 Response Time)
Enter fullscreen mode Exit fullscreen mode

Docker Compose — Full Monitoring Stack:

yaml
# docker-compose.monitoring.yml
version: '3.8'

services:
  # ─────────────────────────────────────
  # Prometheus — Metrics Collection
  # ─────────────────────────────────────
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./monitoring/prometheus:/etc/prometheus
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--storage.tsdb.retention.time=30d'  # ৩০ দিন Data রাখুন
      - '--web.console.libraries=/usr/share/prometheus/console_libraries'
      - '--web.enable-lifecycle'
    networks:
      - monitoring

  # ─────────────────────────────────────
  # Grafana — Visualization Dashboard
  # ─────────────────────────────────────
  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      GF_SECURITY_ADMIN_USER:     admin
      GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD}
      GF_INSTALL_PLUGINS: >-
        grafana-clock-panel,
        grafana-simple-json-datasource,
        grafana-piechart-panel
    volumes:
      - grafana_data:/var/lib/grafana
      - ./monitoring/grafana/provisioning:/etc/grafana/provisioning
      - ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards
    networks:
      - monitoring

  # ─────────────────────────────────────
  # AlertManager — Alert Routing
  # ─────────────────────────────────────
  alertmanager:
    image: prom/alertmanager:latest
    container_name: alertmanager
    restart: unless-stopped
    ports:
      - "9093:9093"
    volumes:
      - ./monitoring/alertmanager:/etc/alertmanager
    command:
      - '--config.file=/etc/alertmanager/alertmanager.yml'
      - '--storage.path=/alertmanager'
    networks:
      - monitoring

  # ─────────────────────────────────────
  # Loki — Log Aggregation
  # ─────────────────────────────────────
  loki:
    image: grafana/loki:latest
    container_name: loki
    restart: unless-stopped
    ports:
      - "3100:3100"
    volumes:
      - ./monitoring/loki:/etc/loki
      - loki_data:/loki
    command: -config.file=/etc/loki/loki.yml
    networks:
      - monitoring

  # ─────────────────────────────────────
  # Promtail — Log Shipper (Loki এ পাঠায়)
  # ─────────────────────────────────────
  promtail:
    image: grafana/promtail:latest
    container_name: promtail
    restart: unless-stopped
    volumes:
      - ./monitoring/promtail:/etc/promtail
      - /var/log:/var/log:ro          # System Logs
      - ./storage/logs:/app/logs:ro   # Laravel Logs
    command: -config.file=/etc/promtail/promtail.yml
    networks:
      - monitoring

  # ─────────────────────────────────────
  # Node Exporter — Server Metrics
  # ─────────────────────────────────────
  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    restart: unless-stopped
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    networks:
      - monitoring

  # ─────────────────────────────────────
  # MySQL Exporter — Database Metrics
  # ─────────────────────────────────────
  mysql-exporter:
    image: prom/mysqld-exporter:latest
    container_name: mysql-exporter
    restart: unless-stopped
    ports:
      - "9104:9104"
    environment:
      DATA_SOURCE_NAME: "${DB_USERNAME}:${DB_PASSWORD}@tcp(mysql:3306)/"
    networks:
      - monitoring

  # ─────────────────────────────────────
  # Redis Exporter — Redis Metrics
  # ─────────────────────────────────────
  redis-exporter:
    image: oliver006/redis_exporter:latest
    container_name: redis-exporter
    restart: unless-stopped
    ports:
      - "9121:9121"
    environment:
      REDIS_ADDR: "redis:6379"
      REDIS_PASSWORD: "${REDIS_PASSWORD}"
    networks:
      - monitoring

  # ─────────────────────────────────────
  # Nginx Exporter — Web Server Metrics
  # ─────────────────────────────────────
  nginx-exporter:
    image: nginx/nginx-prometheus-exporter:latest
    container_name: nginx-exporter
    restart: unless-stopped
    ports:
      - "9113:9113"
    command:
      - '-nginx.scrape-uri=http://nginx/nginx-status'
    networks:
      - monitoring

networks:
  monitoring:
    driver: bridge

volumes:
  prometheus_data:
  grafana_data:
  loki_data:
Enter fullscreen mode Exit fullscreen mode

Prometheus Configuration:

yaml
# monitoring/prometheus/prometheus.yml
global:
  scrape_interval:     15s  # প্রতি ১৫ সেকেন্ডে Metrics নিন
  evaluation_interval: 15s  # Rules প্রতি ১৫ সেকেন্ডে Check করুন
  external_labels:
    environment: 'production'
    app:         'myapp'

# Alert Rules Load করুন
rule_files:
  - "alerts/*.yml"

# AlertManager
alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

# Metrics Collect করুন এখান থেকে
scrape_configs:
  # Laravel Application Metrics
  - job_name: 'laravel'
    static_configs:
      - targets: ['app:9000']
    metrics_path: '/metrics'
    scrape_interval: 10s

  # Server Metrics
  - job_name: 'node'
    static_configs:
      - targets: ['node-exporter:9100']

  # MySQL Metrics
  - job_name: 'mysql'
    static_configs:
      - targets: ['mysql-exporter:9104']

  # Redis Metrics
  - job_name: 'redis'
    static_configs:
      - targets: ['redis-exporter:9121']

  # Nginx Metrics
  - job_name: 'nginx'
    static_configs:
      - targets: ['nginx-exporter:9113']

  # Prometheus Itself
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']
Enter fullscreen mode Exit fullscreen mode

📈 PART 3 — Laravel Metrics Endpoint
Prometheus PHP Client Install:

bash
composer require promphp/prometheus_client_php
Enter fullscreen mode Exit fullscreen mode
php
// app/Services/MetricsService.php
class MetricsService
{
    private CollectorRegistry $registry;
    private RenderTextFormat  $renderer;

    // Metric Objects
    private Counter   $httpRequestsTotal;
    private Histogram $httpRequestDuration;
    private Counter   $httpRequestErrors;
    private Gauge     $activeUsers;
    private Counter   $ordersTotal;
    private Counter   $revenueTotal;
    private Gauge     $queueSize;
    private Histogram $dbQueryDuration;
    private Counter   $cacheHits;
    private Counter   $cacheMisses;

    public function __construct()
    {
        // Redis এ Metrics Store করুন
        $redis           = new \Prometheus\Storage\Redis([
            'host'     => config('database.redis.default.host'),
            'port'     => config('database.redis.default.port'),
            'password' => config('database.redis.default.password'),
            'timeout'  => 0.1,
            'database' => 3, // আলাদা DB — Metrics এর জন্য
        ]);

        $this->registry  = new CollectorRegistry($redis);
        $this->renderer  = new RenderTextFormat();

        $this->initializeMetrics();
    }

    private function initializeMetrics(): void
    {
        // HTTP Request Counter
        $this->httpRequestsTotal = $this->registry->registerCounter(
            'laravel',
            'http_requests_total',
            'Total HTTP Requests',
            ['method', 'route', 'status']
        );

        // HTTP Response Time (Histogram)
        $this->httpRequestDuration = $this->registry->registerHistogram(
            'laravel',
            'http_request_duration_seconds',
            'HTTP Request Duration in Seconds',
            ['method', 'route'],
            [0.01, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0] // Buckets
        );

        // Error Counter
        $this->httpRequestErrors = $this->registry->registerCounter(
            'laravel',
            'http_request_errors_total',
            'Total HTTP Request Errors',
            ['method', 'route', 'error_type']
        );

        // Active Users (Gauge — বাড়ে/কমে)
        $this->activeUsers = $this->registry->registerGauge(
            'laravel',
            'active_users_total',
            'Currently Active Users'
        );

        // Business Metrics — Orders
        $this->ordersTotal = $this->registry->registerCounter(
            'laravel',
            'orders_total',
            'Total Orders Created',
            ['status', 'payment_method']
        );

        // Business Metrics — Revenue
        $this->revenueTotal = $this->registry->registerCounter(
            'laravel',
            'revenue_total_bdt',
            'Total Revenue in BDT',
            ['payment_method']
        );

        // Queue Size (Gauge)
        $this->queueSize = $this->registry->registerGauge(
            'laravel',
            'queue_size',
            'Queue Job Count',
            ['queue_name', 'status']
        );

        // Database Query Duration
        $this->dbQueryDuration = $this->registry->registerHistogram(
            'laravel',
            'db_query_duration_seconds',
            'Database Query Duration',
            ['query_type'],
            [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0]
        );

        // Cache Metrics
        $this->cacheHits = $this->registry->registerCounter(
            'laravel',
            'cache_hits_total',
            'Total Cache Hits',
            ['store']
        );

        $this->cacheMisses = $this->registry->registerCounter(
            'laravel',
            'cache_misses_total',
            'Total Cache Misses',
            ['store']
        );
    }

    // ─────────────────────────────────────
    // HTTP Metrics Record করুন
    // ─────────────────────────────────────
    public function recordHttpRequest(
        string $method,
        string $route,
        int    $status,
        float  $duration
    ): void {
        $labels = [$method, $route, (string) $status];

        // Request Count বাড়ান
        $this->httpRequestsTotal->incByLabels($labels);

        // Response Time Record করুন
        $this->httpRequestDuration->observe(
            $duration,
            [$method, $route]
        );

        // Error হলে Error Counter বাড়ান
        if ($status >= 500) {
            $this->httpRequestErrors->incByLabels([
                $method,
                $route,
                'server_error'
            ]);
        } elseif ($status >= 400) {
            $this->httpRequestErrors->incByLabels([
                $method,
                $route,
                'client_error'
            ]);
        }
    }

    // ─────────────────────────────────────
    // Business Metrics Record করুন
    // ─────────────────────────────────────
    public function recordOrder(
        Order  $order,
        string $status = 'created'
    ): void {
        $this->ordersTotal->incByLabels([
            $status,
            $order->payment_method ?? 'unknown'
        ]);

        if ($status === 'completed') {
            $this->revenueTotal->incBy(
                $order->total,
                [$order->payment_method ?? 'unknown']
            );
        }
    }

    // ─────────────────────────────────────
    // Active Users Update করুন
    // ─────────────────────────────────────
    public function updateActiveUsers(): void
    {
        $count = Cache::remember('active_users_count', 60, function () {
            return User::where(
                'last_activity_at', '>',
                now()->subMinutes(15)
            )->count();
        });

        $this->activeUsers->set($count);
    }

    // ─────────────────────────────────────
    // Queue Metrics Update করুন
    // ─────────────────────────────────────
    public function updateQueueMetrics(): void
    {
        $queues = ['default', 'high-priority', 'low-priority', 'emails'];

        foreach ($queues as $queue) {
            // Pending Jobs
            $pending = Queue::size($queue);
            $this->queueSize->set($pending, [$queue, 'pending']);

            // Failed Jobs
            $failed = DB::table('failed_jobs')
                        ->where('queue', $queue)
                        ->count();
            $this->queueSize->set($failed, [$queue, 'failed']);
        }
    }

    // ─────────────────────────────────────
    // Cache Metrics Record করুন
    // ─────────────────────────────────────
    public function recordCacheHit(string $store = 'redis'): void
    {
        $this->cacheHits->incByLabels([$store]);
    }

    public function recordCacheMiss(string $store = 'redis'): void
    {
        $this->cacheMisses->incByLabels([$store]);
    }

    // ─────────────────────────────────────
    // DB Query Duration Record করুন
    // ─────────────────────────────────────
    public function recordDbQuery(string $type, float $duration): void
    {
        $this->dbQueryDuration->observe($duration, [$type]);
    }

    // ─────────────────────────────────────
    // Prometheus Format এ Render করুন
    // ─────────────────────────────────────
    public function render(): string
    {
        return $this->renderer->render(
            $this->registry->getMetricFamilySamples()
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

Metrics Middleware — Auto Record করুন:

php
// app/Http/Middleware/PrometheusMetricsMiddleware.php
class PrometheusMetricsMiddleware
{
    public function __construct(
        private MetricsService $metrics
    ) {}

    public function handle(Request $request, Closure $next): Response
    {
        $startTime = microtime(true);

        $response  = $next($request);

        $duration  = microtime(true) - $startTime;

        // Route Name নিন — অথবা Path দিয়ে Normalize করুন
        $route = $request->route()?->getName()
              ?? $this->normalizePath($request->path());

        $this->metrics->recordHttpRequest(
            $request->method(),
            $route,
            $response->getStatusCode(),
            $duration
        );

        return $response;
    }

    // /api/products/123 → /api/products/{id}
    // অনেক Cardinality কমায়
    private function normalizePath(string $path): string
    {
        return preg_replace([
            '/\/\d+/',           // /123 → /{id}
            '/\/[0-9a-f-]{36}/', // UUID
        ], [
            '/{id}',
            '/{uuid}',
        ], '/' . $path);
    }
}

// app/Http/Middleware/DatabaseQueryMetricsMiddleware.php
class DatabaseQueryMetricsMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        // DB Query Listen করুন
        DB::listen(function (QueryExecuted $query) {
            $type     = $this->getQueryType($query->sql);
            $duration = $query->time / 1000; // ms → seconds

            app(MetricsService::class)->recordDbQuery($type, $duration);

            // Slow Query Log
            if ($query->time > 100) {
                Log::channel('slow_queries')->warning('Slow Query', [
                    'sql'      => $query->sql,
                    'time_ms'  => $query->time,
                    'bindings' => $query->bindings,
                ]);
            }
        });

        return $next($request);
    }

    private function getQueryType(string $sql): string
    {
        $sql = strtolower(trim($sql));

        return match(true) {
            str_starts_with($sql, 'select') => 'select',
            str_starts_with($sql, 'insert') => 'insert',
            str_starts_with($sql, 'update') => 'update',
            str_starts_with($sql, 'delete') => 'delete',
            default                         => 'other',
        };
    }
}
Enter fullscreen mode Exit fullscreen mode

Metrics Endpoint:

php
// routes/api.php
Route::get('/metrics', [MetricsController::class, 'index'])
     ->middleware('metrics.auth'); // Prometheus Only Access

// app/Http/Controllers/MetricsController.php
class MetricsController extends Controller
{
    public function index(MetricsService $metrics): Response
    {
        // Queue Metrics Update করুন
        $metrics->updateQueueMetrics();
        $metrics->updateActiveUsers();

        return response($metrics->render(), 200, [
            'Content-Type' => RenderTextFormat::MIME_TYPE,
        ]);
    }
}

// app/Http/Middleware/MetricsAuthMiddleware.php
class MetricsAuthMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        // Prometheus এর IP Allow করুন
        $allowedIps = explode(',', config('monitoring.prometheus_ips'));

        if (!in_array($request->ip(), $allowedIps)) {

            // Bearer Token Check
            $token = $request->bearerToken();

            if ($token !== config('monitoring.metrics_token')) {
                abort(403, 'Unauthorized');
            }
        }

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

🚨 PART 4 — Alert Rules

yaml
# monitoring/prometheus/alerts/laravel.yml
groups:
  # ─────────────────────────────────────
  # Application Alerts
  # ─────────────────────────────────────
  - name: laravel_application
    rules:
      # High Error Rate
      - alert: HighErrorRate
        expr: |
          (
            rate(laravel_http_requests_total{status=~"5.."}[5m])
            /
            rate(laravel_http_requests_total[5m])
          ) > 0.05
        for: 2m
        labels:
          severity: critical
          team:     backend
        annotations:
          summary:     "High Error Rate Detected!"
          description: >
            Error Rate {{ $value | humanizePercentage }}
            (threshold: 5%)
          runbook_url: "https://wiki.myapp.com/runbooks/high-error-rate"

      # Slow Response Time
      - alert: SlowResponseTime
        expr: |
          histogram_quantile(
            0.95,
            rate(laravel_http_request_duration_seconds_bucket[5m])
          ) > 2
        for: 5m
        labels:
          severity: warning
          team:     backend
        annotations:
          summary:     "Slow Response Time!"
          description: "p95 Response Time {{ $value }}s (threshold: 2s)"

      # High Request Rate — Traffic Spike
      - alert: HighRequestRate
        expr: rate(laravel_http_requests_total[1m]) > 1000
        for: 2m
        labels:
          severity: warning
        annotations:
          summary:     "Unusual Traffic Spike!"
          description: "{{ $value }} req/s (threshold: 1000)"

      # Queue Backlog
      - alert: QueueBacklog
        expr: laravel_queue_size{status="pending"} > 500
        for: 5m
        labels:
          severity: warning
          team:     backend
        annotations:
          summary:     "Queue Backlog Building Up!"
          description: >
            Queue '{{ $labels.queue_name }}' has
            {{ $value }} pending jobs

      # Failed Jobs
      - alert: FailedJobs
        expr: laravel_queue_size{status="failed"} > 10
        for: 2m
        labels:
          severity: critical
        annotations:
          summary:     "Too Many Failed Jobs!"
          description: "{{ $value }} failed jobs in queue"

      # Cache Hit Rate Low
      - alert: LowCacheHitRate
        expr: |
          (
            rate(laravel_cache_hits_total[5m])
            /
            (
              rate(laravel_cache_hits_total[5m])
              + rate(laravel_cache_misses_total[5m])
            )
          ) < 0.7
        for: 10m
        labels:
          severity: warning
        annotations:
          summary:     "Low Cache Hit Rate!"
          description: "Cache Hit Rate {{ $value | humanizePercentage }}"

  # ─────────────────────────────────────
  # Infrastructure Alerts
  # ─────────────────────────────────────
  - name: infrastructure
    rules:
      # High CPU
      - alert: HighCPUUsage
        expr: |
          100 - (
            avg by(instance)(
              rate(node_cpu_seconds_total{mode="idle"}[5m])
            ) * 100
          ) > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary:     "High CPU Usage!"
          description: "CPU: {{ $value }}% on {{ $labels.instance }}"

      # High Memory
      - alert: HighMemoryUsage
        expr: |
          (
            1 - (
              node_memory_MemAvailable_bytes
              / node_memory_MemTotal_bytes
            )
          ) * 100 > 85
        for: 5m
        labels:
          severity: warning
        annotations:
          summary:     "High Memory Usage!"
          description: "Memory: {{ $value }}% used"

      # Disk Almost Full
      - alert: DiskAlmostFull
        expr: |
          (
            1 - (
              node_filesystem_avail_bytes{fstype!="tmpfs"}
              / node_filesystem_size_bytes{fstype!="tmpfs"}
            )
          ) * 100 > 80
        for: 5m
        labels:
          severity: critical
        annotations:
          summary:     "Disk Almost Full!"
          description: "Disk {{ $value }}% used on {{ $labels.mountpoint }}"

  # ─────────────────────────────────────
  # Database Alerts
  # ─────────────────────────────────────
  - name: database
    rules:
      # Too Many Connections
      - alert: MySQLTooManyConnections
        expr: mysql_global_status_threads_connected > 150
        for: 2m
        labels:
          severity: warning
        annotations:
          summary:     "MySQL Too Many Connections!"
          description: "{{ $value }} connections (max: 200)"

      # Slow Queries
      - alert: MySQLSlowQueries
        expr: rate(mysql_global_status_slow_queries[5m]) > 5
        for: 5m
        labels:
          severity: warning
        annotations:
          summary:     "Too Many Slow Queries!"
          description: "{{ $value }} slow queries/sec"

      # Redis Memory High
      - alert: RedisHighMemory
        expr: |
          redis_memory_used_bytes
          / redis_memory_max_bytes * 100 > 80
        for: 5m
        labels:
          severity: warning
        annotations:
          summary:     "Redis Memory Almost Full!"
          description: "Redis Memory: {{ $value }}% used"

  # ─────────────────────────────────────
  # Business Alerts
  # ─────────────────────────────────────
  - name: business
    rules:
      # No Orders — Business Problem!
      - alert: NoOrdersReceived
        expr: rate(laravel_orders_total[30m]) == 0
        for: 30m
        labels:
          severity: critical
          team:     business
        annotations:
          summary:     "কোনো Order আসছে না!"
          description: "৩০ মিনিটে কোনো Order আসেনি। Payment Gateway Check করুন।"

      # Revenue Drop
      - alert: RevenueDropped
        expr: |
          (
            rate(laravel_revenue_total_bdt[1h])
            / rate(laravel_revenue_total_bdt[1h] offset 1d)
          ) < 0.5
        for: 30m
        labels:
          severity: warning
          team:     business
        annotations:
          summary:     "Revenue ৫০% কমে গেছে!"
          description: "আজকের Revenue গতকালের তুলনায় অনেক কম।"
Enter fullscreen mode Exit fullscreen mode

AlertManager Configuration:

yaml
# monitoring/alertmanager/alertmanager.yml
global:
  smtp_from:      'alerts@myapp.com'
  smtp_smarthost: 'email-smtp.ap-southeast-1.amazonaws.com:587'
  smtp_auth_username: ${SES_USERNAME}
  smtp_auth_password: ${SES_PASSWORD}

# Alert Routing — কোন Alert কোথায় যাবে
route:
  group_by:       ['alertname', 'team']
  group_wait:     30s      # একসাথে Group করে পাঠান
  group_interval: 5m       # একই Group এর Alert ৫ মিনিট পরপর
  repeat_interval: 4h      # একই Alert ৪ ঘন্টা পরপর Repeat

  receiver: 'default'

  routes:
    # Critical → Slack + SMS + Email
    - match:
        severity: critical
      receiver: critical_alerts
      repeat_interval: 1h

    # Business Alerts → Business Team
    - match:
        team: business
      receiver: business_team

    # Warning → Slack Only
    - match:
        severity: warning
      receiver: warning_alerts
      repeat_interval: 12h

# Alert Receivers
receivers:
  # Critical Alerts
  - name: critical_alerts
    slack_configs:
      - api_url: ${SLACK_CRITICAL_WEBHOOK}
        channel: '#alerts-critical'
        title:   '🚨 Critical Alert!'
        text: |
          *{{ .GroupLabels.alertname }}*
          {{ range .Alerts }}
           {{ .Annotations.summary }}
          {{ .Annotations.description }}
          {{ end }}
        color: '#FF0000'
    email_configs:
      - to: 'oncall@myapp.com'
        subject: '🚨 [Critical] {{ .GroupLabels.alertname }}'

  # Warning Alerts
  - name: warning_alerts
    slack_configs:
      - api_url: ${SLACK_WARNING_WEBHOOK}
        channel: '#alerts-warning'
        title:   '⚠️ Warning!'
        text:    '{{ .CommonAnnotations.summary }}'
        color:   '#FFA500'

  # Business Alerts
  - name: business_team
    slack_configs:
      - api_url: ${SLACK_BUSINESS_WEBHOOK}
        channel: '#business-alerts'
        title:   '📊 Business Alert!'
        text: |
          *{{ .CommonLabels.alertname }}*
          {{ .CommonAnnotations.description }}

  # Default
  - name: default
    slack_configs:
      - api_url: ${SLACK_DEFAULT_WEBHOOK}
        channel: '#monitoring'
Enter fullscreen mode Exit fullscreen mode

📉 PART 5 — Grafana Dashboard
Grafana Datasource Config:

yaml
# monitoring/grafana/provisioning/datasources/datasources.yml
apiVersion: 1

datasources:
  # Prometheus
  - name:      Prometheus
    type:      prometheus
    url:       http://prometheus:9090
    isDefault: true
    jsonData:
      httpMethod:           POST
      exemplarTraceIdDestinations:
        - name: traceID
          datasourceUid: tempo

  # Loki — Logs
  - name: Loki
    type: loki
    url:  http://loki:3100
    jsonData:
      derivedFields:
        - datasourceUid: tempo
          matcherRegex:  '"trace_id":"(\w+)"'
          name:          TraceID
          url:           '$${__value.raw}'
Enter fullscreen mode Exit fullscreen mode

Grafana Dashboard JSON — Laravel Overview:

json
{
  "title": "Laravel Application Dashboard",
  "panels": [
    {
      "title": "Requests per Second",
      "type": "stat",
      "targets": [{
        "expr": "rate(laravel_http_requests_total[1m])",
        "legendFormat": "req/s"
      }],
      "fieldConfig": {
        "defaults": {
          "unit": "reqps",
          "thresholds": {
            "steps": [
              {"color": "green", "value": 0},
              {"color": "yellow", "value": 500},
              {"color": "red", "value": 1000}
            ]
          }
        }
      }
    },
    {
      "title": "p95 Response Time",
      "type": "graph",
      "targets": [{
        "expr": "histogram_quantile(0.95, rate(laravel_http_request_duration_seconds_bucket[5m]))",
        "legendFormat": "p95"
      }, {
        "expr": "histogram_quantile(0.99, rate(laravel_http_request_duration_seconds_bucket[5m]))",
        "legendFormat": "p99"
      }]
    },
    {
      "title": "Error Rate",
      "type": "graph",
      "targets": [{
        "expr": "rate(laravel_http_requests_total{status=~'5..'}[5m]) / rate(laravel_http_requests_total[5m]) * 100",
        "legendFormat": "Error Rate %"
      }],
      "alert": {
        "conditions": [{
          "evaluator": {"params": [5], "type": "gt"},
          "query": {"params": ["A", "5m", "now"]}
        }]
      }
    },
    {
      "title": "Orders per Minute",
      "type": "stat",
      "targets": [{
        "expr": "rate(laravel_orders_total[1m]) * 60",
        "legendFormat": "orders/min"
      }],
      "fieldConfig": {
        "defaults": {
          "unit": "short",
          "color": {"mode": "thresholds"}
        }
      }
    },
    {
      "title": "Revenue (Last Hour)",
      "type": "stat",
      "targets": [{
        "expr": "increase(laravel_revenue_total_bdt[1h])",
        "legendFormat": "BDT"
      }]
    },
    {
      "title": "Queue Size",
      "type": "bargauge",
      "targets": [{
        "expr": "laravel_queue_size",
        "legendFormat": "{{ queue_name }} - {{ status }}"
      }]
    },
    {
      "title": "Cache Hit Rate",
      "type": "gauge",
      "targets": [{
        "expr": "rate(laravel_cache_hits_total[5m]) / (rate(laravel_cache_hits_total[5m]) + rate(laravel_cache_misses_total[5m])) * 100",
        "legendFormat": "Hit Rate %"
      }],
      "fieldConfig": {
        "defaults": {
          "min": 0, "max": 100,
          "thresholds": {
            "steps": [
              {"color": "red",    "value": 0},
              {"color": "yellow", "value": 60},
              {"color": "green",  "value": 80}
            ]
          }
        }
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

📋 PART 6 — Loki Log Aggregation
Laravel Log Configuration:

php
// config/logging.php
'channels' => [
    // Production — Loki Compatible JSON Format
    'production' => [
        'driver'    => 'stack',
        'channels'  => ['json', 'slack_critical'],
        'ignore_exceptions' => false,
    ],

    // JSON Format — Loki এ Parse করা সহজ
    'json' => [
        'driver'    => 'daily',
        'path'      => storage_path('logs/laravel.log'),
        'level'     => 'debug',
        'days'      => 14,
        'formatter' => \Monolog\Formatter\JsonFormatter::class,
    ],

    // Critical Error → Slack
    'slack_critical' => [
        'driver'   => 'slack',
        'url'      => env('LOG_SLACK_WEBHOOK_URL'),
        'username' => 'Laravel Log',
        'emoji'    => ':boom:',
        'level'    => 'critical',
    ],
],

// app/Logging/CustomLogger.php
class CustomLogger
{
    public function __invoke(array $config): Logger
    {
        $logger    = new Logger('laravel');
        $handler   = new StreamHandler(storage_path('logs/laravel.log'));
        $formatter = new JsonFormatter();

        // Extra Context যোগ করুন — Loki তে Query করা সহজ হবে
        $logger->pushProcessor(function (array $record) {
            $record['extra'] = array_merge($record['extra'] ?? [], [
                'request_id'  => request()->header('X-Request-ID'),
                'user_id'     => auth()->id(),
                'ip'          => request()->ip(),
                'url'         => request()->fullUrl(),
                'method'      => request()->method(),
                'environment' => app()->environment(),
                'version'     => config('app.version'),
            ]);

            return $record;
        });

        $handler->setFormatter($formatter);
        $logger->pushHandler($handler);

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

Promtail Config — Log Shipper:

yaml
# monitoring/promtail/promtail.yml
server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

clients:
  - url: http://loki:3100/loki/api/v1/push

scrape_configs:
  # Laravel Logs
  - job_name: laravel
    static_configs:
      - targets:
          - localhost
        labels:
          job:       laravel
          app:       myapp
          __path__:  /app/logs/*.log

    pipeline_stages:
      # JSON Parse করুন
      - json:
          expressions:
            level:      level
            message:    message
            request_id: extra.request_id
            user_id:    extra.user_id
            url:        extra.url
            duration:   extra.duration_ms

      # Labels Set করুন  দ্রুত Query এর জন্য
      - labels:
          level:
          request_id:

      # Slow Query আলাদা করুন
      - match:
          selector: '{job="laravel"}'
          stages:
            - regex:
                expression: 'Slow Query.*time=(?P<query_time>\d+)ms'
            - labels:
                query_type: slow

  # Nginx Logs
  - job_name: nginx
    static_configs:
      - targets:
          - localhost
        labels:
          job:      nginx
          __path__: /var/log/nginx/*.log

    pipeline_stages:
      - regex:
          expression: >-
            (?P<remote_addr>\S+) .* \[(?P<time_local>[^\]]+)\]
            "(?P<method>\S+) (?P<request_uri>\S+) \S+"
            (?P<status>\d+) (?P<bytes_sent>\d+)
      - labels:
          method:
          status:
Enter fullscreen mode Exit fullscreen mode

🎯 PART 7 — Complete Monitoring Artisan Commands

php
// app/Console/Commands/MonitoringCheckCommand.php
class MonitoringCheckCommand extends Command
{
    protected $signature   = 'monitoring:check';
    protected $description = 'System Health Check করুন';

    public function handle(MetricsService $metrics): void
    {
        $this->info('🔍 System Health Check শুরু...');
        $allGood = true;

        // ১. Database Check
        try {
            $start     = microtime(true);
            DB::select('SELECT 1');
            $dbTime    = round((microtime(true) - $start) * 1000, 2);
            $dbStatus  = $dbTime < 100 ? '✅' : '⚠️';
            $this->line("{$dbStatus} Database: {$dbTime}ms");

            if ($dbTime > 100) $allGood = false;
        } catch (\Exception $e) {
            $this->error('❌ Database: FAILED - ' . $e->getMessage());
            $allGood = false;
        }

        // ২. Redis Check
        try {
            $start      = microtime(true);
            Redis::ping();
            $redisTime  = round((microtime(true) - $start) * 1000, 2);
            $this->line("✅ Redis: {$redisTime}ms");
        } catch (\Exception $e) {
            $this->error('❌ Redis: FAILED');
            $allGood = false;
        }

        // ৩. Queue Check
        $pendingJobs = Queue::size('default');
        $failedJobs  = DB::table('failed_jobs')->count();
        $queueStatus = $failedJobs > 10 ? '⚠️' : '✅';
        $this->line("{$queueStatus} Queue: {$pendingJobs} pending, {$failedJobs} failed");

        if ($failedJobs > 10) $allGood = false;

        // ৪. Storage Check
        $diskFree    = disk_free_space('/');
        $diskTotal   = disk_total_space('/');
        $diskUsed    = round((1 - $diskFree / $diskTotal) * 100, 1);
        $diskStatus  = $diskUsed > 80 ? '⚠️' : '✅';
        $this->line("{$diskStatus} Disk: {$diskUsed}% used");

        if ($diskUsed > 80) $allGood = false;

        // ৫. Cache Hit Rate
        $hits        = (int) Redis::get('metrics:cache:hits') ?: 1;
        $misses      = (int) Redis::get('metrics:cache:misses') ?: 0;
        $hitRate     = round($hits / ($hits + $misses) * 100, 1);
        $cacheStatus = $hitRate < 70 ? '⚠️' : '✅';
        $this->line("{$cacheStatus} Cache Hit Rate: {$hitRate}%");

        // ৬. Overall Status
        $this->newLine();
        if ($allGood) {
            $this->info('✅ সব ঠিক আছে!');
        } else {
            $this->warn('⚠️ কিছু সমস্যা আছে। Check করুন।');
        }

        // Metrics Update করুন
        $metrics->updateQueueMetrics();
        $metrics->updateActiveUsers();
    }
}

// Schedule করুন
// app/Console/Kernel.php
protected function schedule(Schedule $schedule): void
{
    $schedule->command('monitoring:check')
             ->everyFiveMinutes()
             ->runInBackground()
             ->withoutOverlapping();
}
Enter fullscreen mode Exit fullscreen mode

📝 সংক্ষেপে মনে রাখুন

Prometheus — আপনার App থেকে প্রতি ১৫ সেকেন্ডে Metrics নেয় এবং Store করে। Counter, Gauge, Histogram — তিন ধরনের Metric শিখুন।

Grafana — Prometheus এর Data দিয়ে সুন্দর Dashboard বানায়। Alert Rule দিয়ে Slack/Email এ Notification পাঠায়।

Loki — Prometheus এর মতোই কিন্তু Logs এর জন্য। Laravel এর JSON Log Loki তে পাঠান, Grafana তে Query করুন।

AlertManager — Alert গুলো Route করে — Critical → Slack + Email, Warning → Slack Only।

Laravel Integration — Middleware দিয়ে HTTP Metrics, DB Listener দিয়ে Query Metrics, Schedule দিয়ে Business Metrics Auto Record করুন।

শেখার ক্রম — প্রথমে Docker Compose দিয়ে Stack চালু করুন → Laravel Metrics Endpoint বানান → Grafana Dashboard তৈরি করুন → Alert Rules যোগ করুন।

Top comments (0)