Hasina Razafintsalama

Hasina RAZAFINTSALAMA

← Back to Blog
Architecture

Migrating a Laravel Monolith to Microservices

A step-by-step approach to breaking up a Laravel monolith,without bringing everything down. From strangler fig pattern to service boundaries.

2026-06-10·9 min

Migrating a monolith to microservices is one of the most complex decisions a tech team can make. Done well, it unlocks scalability and team autonomy. Done poorly, it creates a distributed monolith,with all the complexity of microservices and none of the benefits.

Start with the strangler fig pattern

The strangler fig pattern is the safest migration strategy: instead of a big-bang rewrite, you incrementally extract functionality from the monolith into new services. The monolith continues running while you route traffic to the new services piece by piece.

Never attempt a full rewrite of a running monolith. Extract one domain at a time, keep the monolith as fallback, and migrate traffic gradually with feature flags.

Identify domain boundaries first

The hardest part is not the code,it is defining the right service boundaries. Use Domain-Driven Design (DDD) to identify bounded contexts. Each microservice should own its data and expose a clear API contract.

  • Map your domain: identify aggregates and bounded contexts
  • Spot pain points: which modules deploy together? Which slow each other down?
  • Define data ownership: each service must own its own database
  • Document API contracts before writing a single line of service code

Practical steps with Laravel

php
// 1. Extract the domain logic into a standalone service
// Before: OrderController in monolith calls UserService directly
class OrderController extends Controller {
    public function store(Request $request, UserService $users) {
        $user = $users->find($request->user_id); // direct coupling
        // ...
    }
}

// After: OrderService calls UserService via HTTP
class OrderService {
    public function createOrder(int $userId, array $items): Order {
        $user = Http::get(config('services.user.url') . '/users/' . $userId)->json();
        // ...
    }
}

Communication: synchronous vs asynchronous

Not all service communication should be synchronous HTTP. Use synchronous calls for operations requiring an immediate response (user auth, payment). Use async messaging (RabbitMQ, Kafka) for events that do not block the user flow (email notifications, audit logs, analytics).

A microservices migration is a journey measured in months, not weeks. Pace yourself, measure outcomes at each step, and do not hesitate to pause and consolidate before moving to the next service extraction.

Need help with this topic? Microservices Migration

Discover this service