Fra monolit til mikrotjenester
Afgør, hvad der skal opdeles, og hvordan tjenestegrænserne skal trækkes.
Fra monolit til mikrotjenester er en gratis PHP Academy-lektion på CoddyKit. Dette er lektion 1 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i PHP Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. PHP Academy-kurset indeholder 4 lektioner i alt.
Fra monolit til mikrotjenester
Mikrotjenester er et organisatorisk og driftsmæssigt sats, ikke en standardløsning. Når du opdeler en PHP-monolit, bytter du enkelhed i processen ud med netværkskald, distribuerede data og kompleksitet i udrulningen. Hvis det gøres af de forkerte grunde, bliver alting langsommere.
Denne lektion handler om den sværeste del: fastlæggelse af tjenestegrænser. Få samlingerne rigtigt, så er resten rørføring; få dem forkert, så bygger du en distribueret monolit — alle omkostningerne uden nogen af fordelene.
Hvorfor (og hvorfor ikke) opdele
Gode grunde til at opdele:
- Uafhængig udrulning og ejerskab hos teamet.
- Uafhængig skalering af belastede delsystemer.
- Isolering af teknologi og kørselsmiljø samt begrænsning af fejl.
Dårlige grunde: "koden er rodet" (refaktorér monolitten først) eller jagten på en trend. Hvis to tjenester altid skal udrulles sammen eller dele en databasetabel, er de én tjeneste med to roller.
Afgrænsede kontekster
Domain-Driven Design giver det skarpeste værktøj til grænser: den afgrænsede kontekst. Inden for en kontekst har begreber én præcis betydning. "Kunde" i Fakturering (et fakturamål) adskiller sig fra "Kunde" i Support (forfatteren til en sag).
Hver afgrænset kontekst er en oplagt kandidat til at blive en tjeneste. Grænserne følger forretningssproget og den måde, organisationen faktisk kommunikerer på — Conway's Law i praksis.
Høj sammenhæng, lav kobling
En god tjenestegrænse holder ting, der ændres sammen, indenfor og skubber ting, der ændres uafhængigt, udenfor. Vurder en foreslået opdeling ud fra:
- Hvor mange funktioner kræver, at du berører to tjenester på samme tid? (Det bør være få.)
- Hvor omfattende er kaldemønstret mellem dem? (Det bør være grovkornet.)
Hvis implementeringen af én funktion konstant går på tværs af en grænse, er grænsen placeret forkert.
<?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";Én database pr. tjeneste
Den ufravigelige regel: Hver tjeneste ejer sine data, og ingen anden tjeneste må røre dens tabeller. Delte databaser genskaber den kobling, du opdelte systemet for at slippe væk fra — en skemaændring ødelægger tjenester, der ikke har noget med hinanden at gøre.
Det betyder, at forespørgsler på tværs af tjenester, som var en SQL JOIN i monolitten, bliver til API-kald eller replikerede læsemodeller. Det er prisen, og det er hele pointen.
<?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";Mønsteret med gradvis udskiftning
Omskriv aldrig en monolit på én gang. Mønsteret med gradvis udskiftning trækker funktionalitet ud lidt efter lidt: Omdirigér en del af trafikken til en ny tjeneste gennem en facade eller proxy, udbyg den, og fjern først den gamle kodevej, når den nye fuldt ud dækker den.
En omvendt proxy (eller API-gateway) placeres foran systemet og afgør for hver rute, om den skal gå til den ældre monolit eller den nye tjeneste. Migreringen foregår én funktion ad gangen, og hver ændring kan altid leveres.
<?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";Udtræk af et modul
En pragmatisk rækkefølge for udtræk:
- Find et modul med få indgående afhængigheder og tydeligt dataejerskab.
- Pak først dets kald i samme proces ind bag en grænseflade i monolitten.
- Flyt de data, det ejer, til sit eget skema eller sin egen database.
- Erstat grænsefladens implementering med en netværksklient.
- Skift trafikken over via facaden, og slet den gamle kode.
Hvis du først pakker kaldende ind bag en grænseflade i monolitten, reducerer du risikoen ved netværkstrinnet.
<?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'; }
}Konsistens i distribuerede data
Når dataene er opdelt, mister du ACID-transaktioner på tværs af tjenester. Accepter eventuel konsistens: Tjenester udgiver hændelser om deres egne data, og andre bygger lokale læsemodeller ud fra disse hændelser.
Ordretjenesten forespørger ikke Kunder ved hver forespørgsel — den vedligeholder en lille projektion (kun de felter, den har brug for), som opdateres af CustomerUpdated-hændelser. Det fjerner en afhængighed under kørsel og et ekstra latenstrin.
<?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']]);
}Driftsvirkeligheden
Mikrotjenester flytter kompleksiteten fra koden til driften. Før du opdeler, har du brug for:
- Centraliseret logning og distribueret sporing (korrelations-id'er på tværs af hop).
- CI/CD pr. tjeneste og uafhængige, versionsstyrede udrulninger.
- Sundhedstjek, tidsgrænser, nye forsøg og kredsløbsafbrydere ved hvert kald.
- Kontrakttests, så en leverandør ikke i stilhed kan ødelægge en forbruger.
Hvis dit team ikke kan drive én tjeneste godt, bliver ti tjenester ti gange værre.
Tjenester i passende størrelse
"Mikro" er misvisende — dimensionér tjenester efter forretningsfunktion og teamejerskab, ikke efter antal kodelinjer. Hvis de er for finkornede (nanotjenester), breder en enkelt funktion sig ud i en storm af netværkskald; hvis de er for grovkornede, er du tilbage ved en monolit.
En sund tommelfingerregel er, at én tjeneste skal kunne ejes af ét team, udrulles selvstændigt og opfylde sine centrale anvendelsestilfælde uden en synkron kæde gennem mange andre tjenester.
Alternativet med en modulær monolit
Overvej den modulære monolit, før du går distribueret: én udrulningsenhed, men med en intern opdeling i moduler med tydelige grænser og egne skemaer, som kun kommunikerer gennem offentliggjorte grænseflader — ingen adgang til tabeller på tværs af moduler.
Du får rene grænser og nem refaktorering uden omkostningerne ved distribuerede systemer. Når et modul virkelig har brug for uafhængig skalering eller ejerskab, er det allerede udformet, så det kan trækkes ud via mønsteret med gradvis udskiftning. For de fleste teams er dette det rigtige første skridt.
<?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.
}Hurtigt tjek
Genkend en dårlig opdeling.
Opsummering
Sådan fastlægger du gode tjenestegrænser:
- Opdel for at opnå udrulning, skalering og ejerskab — ikke fordi koden er rodet.
- Tilpas grænserne til afgrænsede kontekster; stræb efter høj sammenhæng og lav kobling.
- Én database pr. tjeneste — ingen delte tabeller; erstat JOINs med API'er eller læsemodeller.
- Migrér trinvist med mønsteret med gradvis udskiftning.
- Accepter eventuel konsistens, og investér i drift, før du skalerer ud.
Næste emne: hvordan tjenesterne faktisk kommunikerer — REST og gRPC.
Lær PHP med en AI-underviser — gratis
Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.
- Kurser
- 49
- Lektioner
- 195
Ofte stillede spørgsmål
Er lektionen “Fra monolit til mikrotjenester” gratis?
Ja — hele teksten til “Fra monolit til mikrotjenester” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af PHP Academy-kurset, skal du opgradere til CoddyKit PRO. PHP Academy-kurset indeholder 4 lektioner i alt.
Hvad lærer jeg i “Fra monolit til mikrotjenester”?
Afgør, hvad der skal opdeles, og hvordan tjenestegrænserne skal trækkes. Du øver dig i PHP Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.
Skal jeg have erfaring for at begynde på PHP Academy?
Der kræves ingen tidligere erfaring. PHP Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 1 af 4.
Hvor lang tid tager lektionen “Fra monolit til mikrotjenester”?
De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.
Kan jeg skrive og køre kode i denne PHP Academy-lektion?
Ja. Alle PHP Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.
Alle lektioner i dette kursus
- Fra monolit til mikrotjenester
- Tjenestekommunikation: REST og gRPC
- API-gateways og tjenesteopdagelse
- Robusthed: Circuit Breakers og retries