PHP Academy · Lektion

Från monolit till mikrotjänster

Avgör vad som ska delas upp och hur tjänstegränserna ska dras.

Lektion 1 av 413 steg

Från monolit till mikrotjänster är en gratis lektion i PHP Academy på CoddyKit. Detta är lektion 1 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för PHP Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i PHP Academy innehåller totalt 4 lektioner.

Från monolit till mikrotjänster

Mikrotjänster är en organisatorisk och operativ satsning, inte ett standardval. Att dela upp en PHP-monolit innebär att enkelhet inom processen byts mot nätverksanrop, distribuerade data och komplexare driftsättning. Om det görs av fel skäl blir allt långsammare.

Den här lektionen handlar om den svåraste delen: att definiera tjänstegränser. Får ni gränserna rätt är resten mest infrastrukturarbete; får ni dem fel bygger ni en distribuerad monolit — alla kostnader, inga fördelar.

Varför (och varför inte) dela upp

Bra skäl att dela upp:

  • Oberoende driftsättning och teamägarskap.
  • Oberoende skalning av belastade delsystem.
  • Isolering av teknik och körmiljö samt begränsning av fel.

Dåliga skäl är "koden är rörig" (refaktorera monoliten först) eller att jaga en trend. Om två tjänster alltid måste driftsättas tillsammans eller dela en databastabell är de en och samma tjänst med två roller.

Bounded Contexts

Domain-Driven Design ger det skarpaste verktyget för gränsdragning: bounded context. Inom en sådan kontext har termerna en enda precis betydelse. "Customer" i Billing (ett fakturamål) skiljer sig från "Customer" i Support (den som skapar ett ärende).

Varje bounded context är en stark kandidat för att bli en tjänst. Gränserna följer verksamhetens språk och hur organisationen faktiskt kommunicerar — Conway's Law i praktiken.

Hög kohesion, låg koppling

En bra tjänstegräns håller sådant som ändras tillsammans inom tjänsten och flyttar ut sådant som ändras oberoende. Utvärdera en föreslagen uppdelning utifrån:

  • Hur många funktioner kräver ändringar i två tjänster samtidigt? (Det bör vara få.)
  • Hur pratsamt är anropsmönstret mellan dem? (Det bör vara grovkornigt.)

Om implementeringen av en funktion ständigt hamnar på båda sidor om en gräns ligger gränsen fel.

<?php
// Chatty boundary smell: N network calls to render one view
foreach ($order->lineItems as $item) {
    $product = $catalogApi->get($item->productId); // one call PER item!
    $names[] = $product['name'];
}

// Coarse-grained: one batch call across the boundary
$ids   = array_map(fn($i) => $i->productId, $order->lineItems);
$names = $catalogApi->getMany($ids); // single round-trip
echo count($names) . " products in one call\n";

En databas per tjänst

Den icke förhandlingsbara regeln är att varje tjänst äger sina data och att ingen annan tjänst får komma åt dess tabeller. Delade databaser återskapar den koppling som ni försökte komma bort från — en schemaändring bryter sönder orelaterade tjänster.

Det innebär att frågor mellan tjänster som var en SQL JOIN i monoliten blir API-anrop eller replikerade läsmodeller. Det är priset, och det är själva poängen.

<?php
// In the monolith: one JOIN across domains
$sql = 'SELECT o.id, c.email
        FROM orders o JOIN customers c ON c.id = o.customer_id';

// After split: Orders service holds only the foreign id;
// it asks the Customers service for the rest (or keeps a local read model).
$order = $orderRepo->find($id);          // local
$customer = $customersApi->get($order->customerId); // network call
echo $order->id . ' / ' . $customer->email . "\n";

Strangler Fig-mönstret

Skriv aldrig om en monolit i ett enda stort steg. Strangler fig-mönstret extraherar stegvis: dirigera en del av trafiken till en ny tjänst via en fasad eller proxy, bygg ut den och avveckla den gamla kodvägen först när den nya helt täcker dess funktion.

En reverse proxy (eller API gateway) ligger framför och avgör per route om anropet ska gå till den gamla monoliten eller den nya tjänsten. Migreringen sker en funktion i taget och kan alltid driftsättas.

<?php
// Facade routing: peel off one capability at a time
function route(string $path): string {
    $migrated = ['/invoices', '/invoices/pdf']; // moved to billing-svc
    foreach ($migrated as $prefix) {
        if (str_starts_with($path, $prefix)) {
            return 'http://billing-svc' . $path;
        }
    }
    return 'http://legacy-monolith' . $path; // everything else, for now
}
echo route('/invoices/pdf'), "\n";
echo route('/users/42'), "\n";

Extrahera en modul

En pragmatisk ordning för extrahering:

  1. Hitta en modul med få inkommande beroenden och tydligt dataägarskap.
  2. Kapsla först in dess anrop inom processen bakom ett gränssnitt i monoliten.
  3. Flytta de data som modulen äger till ett eget schema eller en egen DB.
  4. Ersätt gränssnittsimplementeringen med en nätverksklient.
  5. Styr över trafiken via fasaden och ta bort den gamla koden.

Genom att kapsla in gränssnittet i monoliten först minskar ni risken med nätverkssteget.

<?php
// Step 2: hide the implementation behind a port the monolith calls
interface InvoiceService {
    public function generate(string $orderId): string; // returns invoice id
}

// Today: local class. Tomorrow: HTTP client to billing-svc.
// The monolith's calling code never changes.
final class LocalInvoiceService implements InvoiceService {
    public function generate(string $orderId): string { return 'inv-1'; }
}

Distribuerad datakonsistens

När data har delats upp förlorar ni ACID-transaktioner över tjänstegränser. Acceptera eventual consistency: tjänster publicerar händelser om sina egna data och andra bygger lokala läsmodeller utifrån dessa händelser.

Orders-tjänsten frågar inte Customers vid varje anrop — den behåller en liten projektion (endast de fält den behöver), uppdaterad av händelser av typen CustomerUpdated. Det eliminerar ett körningsberoende och ett extra latenssteg.

<?php
// Orders service keeps a tiny local projection of customer data
function onCustomerUpdated(PDO $db, array $evt): void {
    $db->prepare(
        'INSERT INTO customer_read_model (id, email)
         VALUES (:id, :email)
         ON CONFLICT (id) DO UPDATE SET email = EXCLUDED.email'
    )->execute(['id' => $evt['id'], 'email' => $evt['email']]);
}

Verkligheten i drift

Mikrotjänster flyttar komplexiteten från kod till drift. Innan ni delar upp behöver ni:

  • Centraliserad loggning och distribuerad spårning (korrelations-ID:n över alla hopp).
  • CI/CD per tjänst och oberoende versionsmärkta driftsättningar.
  • Hälsokontroller, timeouts, omförsök och circuit breakers vid varje anrop.
  • Kontraktstester så att en leverantör inte i tysthet kan bryta en konsument.

Om ert team inte kan köra en tjänst på ett bra sätt blir tio tjänster tio gånger värre.

Rätt dimensionering av tjänster

"Mikro" är missvisande — dimensionera tjänster efter verksamhetsförmåga och teamägarskap, inte efter antalet kodrader. Om tjänsterna är för finkorniga (nanoservices) sprider sig en enda funktion till en storm av nätverksanrop; om de är för grovkorniga är ni tillbaka vid en monolit.

En bra tumregel är att en tjänst ska kunna ägas av ett team, driftsättas självständigt och uppfylla sina centrala användningsfall utan en synkron kedja genom många andra tjänster.

Det modulära monolitalternativet

Innan ni går över till distribuerade system bör ni överväga en modulär monolit: en enda driftsättningsenhet, men internt uppdelad i moduler med tydliga gränser och egna scheman, som endast kommunicerar via publicerade gränssnitt — ingen åtkomst till andra modulers tabeller.

Ni får rena gränser och enkel refaktorering utan kostnaden för distribuerade system. När en modul verkligen behöver oberoende skalning eller ägarskap är den redan utformad för att lyftas ut via strangler-mönstret. För de flesta team är detta det rätta första steget.

<?php
// Modules talk only through interfaces, never each other's tables.
namespace App\Billing;          // owns billing_* tables
interface BillingFacade {
    public function invoiceForOrder(string $orderId): string;
}

namespace App\Sales;            // owns sales_* tables
final class Checkout {
    public function __construct(private \App\Billing\BillingFacade $billing) {}
    // Sales never SELECTs from billing_* directly - only via the facade.
}

Snabb kontroll

Att känna igen en dålig uppdelning.

Sammanfattning

Att dra bra tjänstegränser:

  • Dela upp för driftsättning, skalning och ägarskap — inte för att koden är rörig.
  • Anpassa gränserna till bounded contexts och eftersträva hög kohesion och låg koppling.
  • En databas per tjänst — inga delade tabeller; ersätt JOINs med API:er eller läsmodeller.
  • Migrera stegvis med strangler fig-mönstret.
  • Acceptera eventual consistency och investera i drift innan ni skalar ut.

Nästa steg: hur tjänsterna faktiskt kommunicerar — REST och gRPC.

Gratis att börja

Lär dig PHP med en AI-lärare – gratis

Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.

Kurser
49
Lektioner
195

Vanliga frågor

Är lektionen ”Från monolit till mikrotjänster” gratis?

Ja – hela texten till ”Från monolit till mikrotjänster” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i PHP Academy, kan Ni uppgradera till CoddyKit PRO. Kursen i PHP Academy innehåller totalt 4 lektioner.

Vad lär jag mig i ”Från monolit till mikrotjänster”?

Avgör vad som ska delas upp och hur tjänstegränserna ska dras. Ni övar på PHP Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.

Behöver jag någon erfarenhet för att börja lära mig PHP Academy?

Du behöver inga förkunskaper. Utbildningen i PHP Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.

Hur lång tid tar lektionen ”Från monolit till mikrotjänster”?

De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.

Kan jag skriva och köra kod i den här PHP Academy-lektionen?

Ja. Varje PHP Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.

Alla lektioner i den här kursen

  1. Från monolit till mikrotjänster
  2. Tjänstekommunikation: REST och gRPC
  3. API-gateways och tjänsteupptäckt
  4. Feltålighet: kretsbrytare och omförsök
← Tillbaka till PHP Academy