Microservices in PHP: Advanced Architecture and Practice

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Microservices in PHP: From Theory to Production



The transition from a monolith to a microservices architecture in PHP is not just a technological upgrade, but a fundamental shift in a developer's mindset. At an advanced level, we face real challenges: distributed transactions, data consistency, network latency, and debugging complexity. In this article, we will break down key patterns and practical techniques that will help build a reliable and scalable system in PHP.



1. Communication Between Services: Synchronous vs Asynchronous



Choosing a communication protocol is the first thing an architect encounters. In the PHP ecosystem, there are two main approaches.



Synchronous Communication via REST/gRPC

Ideal for operations requiring an immediate response (e.g., checking a user's balance). However, it creates tight dependencies: if service B goes down, service A will also fail. It is recommended to use the Circuit Breaker pattern.



// Example of Circuit Breaker in PHP using the Guzzle libraryuse GuzzleHttp\\Client;use GuzzleHttp\\Exception\\ConnectException;

class CircuitBreaker { private int $failureCount = 0; private int $threshold = 3; private int $timeout = 30; // seconds private bool $open = false; private int $lastFailureTime = 0;

public function call(string $url): ?array { if ($this->isOpen()) { return null; // Fast short-circuit }

try { $client = new Client(['timeout' => 2.0]); $response = $client->get($url); $this->reset(); return json_decode($response->getBody(), true); } catch (ConnectException $e) { $this->recordFailure(); return null; } }

private function isOpen(): bool { if (!$this->open) return false; if (time() - $this->lastFailureTime > $this->timeout) { $this->open = false; // Half-open state return false; } return true; }

private function recordFailure(): void { $this->failureCount++; $this->lastFailureTime = time(); if ($this->failureCount >= $this->threshold) { $this->open = true; } }

private function reset(): void { $this->failureCount = 0; $this->open = false; }}


Asynchronous Communication via Queues (RabbitMQ, Kafka)

For operations that do not require an immediate response (sending emails, generating reports). Use the php-amqplib library.



// Publishing an event to RabbitMQuse PhpAmqpLib\\Connection\\AMQPStreamConnection;use PhpAmqpLib\\Message\\AMQPMessage;

$connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');$channel = $connection->channel();

$channel->queue_declare('order_created', false, true, false, false);

$data = json_encode([ 'order_id' => 12345, 'user_id' => 678, 'total' => 1500.00]);$msg = new AMQPMessage($data, ['delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT]);$channel->basic_publish($msg, '', 'order_created');

$channel->close();$connection->close();


2. Data Management: CQRS and Event Sourcing



In microservices, each service should own its data. But what to do when you need to combine data from different services for a report? The CQRS (Command Query Responsibility Segregation) pattern comes to the rescue.



Implementing Simple CQRS in PHP

We separate commands (write) and queries (read). For writing, we use Doctrine ORM; for reading, we use direct SQL queries to the read model.



// Command: creating an orderclass CreateOrderCommand {    public function __construct(        public readonly int $userId,        public readonly array $items,        public readonly float $total    ) {}}

// Handler

Blogs

Book Recommendations