This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Monday, 29 December 2025
Mikropalveluista on tullut "vakavan järjestelmän" oletusarkkitehtuuri.
Jos haluat kuulostaa kypsältä, puhut palveluverkoista, tapahtumabusseista, jaetusta jäljityksestä ja "riippumattomuudesta". Jos haluat näyttää yrittäjävalmialta, piirrät laatikoita, kunnes kaavio näyttää jonkun heittämältä spagettikulholta.
Suurin osa junioreista ei kuitenkaan saa korjausta:
Mikropalvelut eivät ole sovellusarkkitehtuurin päivitys, vaan organisaatioskaalautumisstrategia.
Jos sinulla ei ole jo niiden ratkaisemaa organisaatiokipua, niiden varhainen hyväksyminen ei tee sinusta tulevaisuudenkestävää, vaan se tekee sinusta nykyhetken rikkinäisen.
Tämä ei ole teoreettinen argumentti, vaan insinöörien vaihtokauppa, ja kuten kaikki sovitukset, se käy järkeen vain silloin, kun ymmärtää maksamansa veron ja sen, mitä saa vastineeksi.
Olen ollut yhtä syyllinen kuin kuka tahansa "mikropalveluiden tekemiseen" ilman, että olen sisäistänyt täysin niiden aiheuttamia organisaatiokuluja. Sanastoon on helppo tarttua ja unohtaa, että todellinen haaste on monimutkaisuuden hallinta eri tiimeissä ja järjestelmissä.
Lopulta kyse on ohjelmistotekniikan vanhimmasta mallista: KISS - Pidä se yksinkertaisena, typerys.
Mikropalveluita myydään oletusarkkitehtuurina, koska kerronta on viettelevää:
Tämä kehystys on takaperin.
Mikropalveluissa ei ole kyse ensisijaisesti koodirakenteesta, vaan siitä, että kuka voi muuttaa mitäkin, puhumatta kenen kanssa, ja kuinka usein.
Jos sinulla ei ole:
Sitten et ratkaise organisaatio-ongelmaa, vaan ostat sen.
Arkkitehtuurissa on viime kädessä kyse ihmisten mahdollistamisesta, ei laatikoiden liikuttamisesta.
Olet nähnyt tämän kaavion.

Tämä ei ole "esimerkkiarkkitehtuuri kopioitavaksi", vaan varoitusetiketti.
Kurssit opettavat usein mikropalveluita kaavioina, koska kaaviot ovat helpompia kuin opetetaan operatiivista vastuuta.
Tärkein korjaus on tämä: tuo kaavio ei ole kohdetila. kustannuspinta.
Jokainen laatikko on sitoumus:
Nuolet näyttävät vaikuttavilta ja kätkevät todellisuuden.
Koska jokainen nuoli on:
Jos arkkitehtuuri vaatii tätä ensimmäisenä päivänä, et rakenna tuotetta vaan alustaa.
Kuten kaikki arkkitehtoniset päätökset, mikropalveluissa on Monimutkaisuusvero (ks. Koodilla leikkiminen: monimutkaisuusveroErona on se, että veroyhdisteet ulottuvat jokaisen palvelurajan, jokaisen käyttöönoton ja jokaisen vikatilan yli.
Kustannukset ovat harvoin yksiselitteisiä, yleensä ne naamioidaan "parhaaksi käytännöksi".
Jokainen palvelu haluaa:
Jos joukkue ei pysty tähän mukavasti, sillä ei ole mikropalveluita. jaettu monoliitti, jossa on ylimääräisiä askeleita.
Monoliitissa puhelu on funktiopuhelu.
Mikropalveluissa "puhelu" on neuvoteltu aselepo seuraavien välillä:
Virhetilat moninkertaistuvat:
Vikoja ei vioiteta enää. järjestelmä ilmoittaa.
Tässä on hinta, josta ei juuri koskaan puhuta mikropalveluiden ajamisessa: Serialisointi- ja deserialisaatio (SerDe) yläpuolella.
Jokainen palvelun raja tarkoittaa:
// Monolith: direct object reference
var user = _userService.GetUser(userId); // < 1μs
var tier = user.Tier; // Memory access
// Microservices: serialize → network → deserialize
var json = JsonSerializer.Serialize(user); // ~50μs for a complex object
var bytes = Encoding.UTF8.GetBytes(json); // ~10μs
// ... send over network ...
var responseJson = await response.Content.ReadAsStringAsync(); // ~20μs
var user = JsonSerializer.Deserialize<User>(responseJson); // ~70μs
var tier = user.Tier; // Finally
Kutakin puhelua kohden se on noin 150 μs puhdasta prosessoria ennen kuin verkko edes sekaantuu siihen.
Kerro se nyt puheluketjussa:
Viiden palvelun ketjussa kulutat ~1,5 milliä vain SerDeen Jos käytät XML:ää, Protocol Buffersia, jossa on heijastus tai tehottomia sarjakuvia, kerro se 2-10x:llä.
Kyllä, tätä voi vähentää tekniikoilla, kuten JSON-lähdesukupolvi verkossa:
[JsonSerializable(typeof(User))]
internal partial class UserJsonContext : JsonSerializerContext { }
// ~30-40% faster than reflection-based serialization
var json = JsonSerializer.Serialize(user, UserJsonContext.Default.User);
Mutta teet yhä sarjatuotantoa, olet juuri tehnyt verosta hieman halvemman. Monoliitissa vero on nolla.
Profiloin kerran osoitehakujärjestelmän tällä arkkitehtuurilla (kaikki Kubernetesin konteissa):
ASP.NET API → Go Service (fan-out) → ASP.NET Search Service → Elasticsearch
Go-palvelu käsitteli fanitusta useisiin tietolähteisiin ja aggregoituihin tuloksiin. Jokainen pyyntö tarkoitti useita sarjahumuja konttirajojen yli.
Tyypillisen osoitehaun erittely:
Kolmelle takatolpalle:
Käytimme lähes yhtä paljon aikaa SerDeen kuin varsinaiseen etsintään.
Kustannukset eivät olleet itse Go-palvelussa, vaan käyttökotelossa oli järkeä. palvelurajatJokainen konttiraja tarkoitti sarjanumeroa → verkkoa → deserialize.
Monoliitissa, joka tekee samaa fan-out-logiikkaa:
Kustannukset ovat seuraavat:
Monoliitissa tämä hinta on nolla. Objekti on jo muistissa.
Tässä on yleinen myyntipuhe: "Microservices parantaa suorituskykyä."
Tämä on takaperin.
Mikropalvelut ovat hitaammin Tarkennetaan, mitä tarkoitamme:
Läpäisykyky (pyynnöt sekunnissa CPU-ydintä kohti):
Kallistuvuus (valmius käsitellä enemmän kokonaispyyntöjä lisäämällä resursseja):
Mikropalvelut sallivat sinun mittakaava itsenäisesti. Jos varastopalvelusi tarvitsee 10x resursseja, mutta käyttäjäpalvelusi ei, voit skaalata ne erikseen. kun sitä tarvitsee.
Mutta se ei ole suoritusoptimointia, vaan skaalautumisstrategiaJa siihen liittyy vero.
Väite "mikropalveluiden mittakaavan parantaminen" oli järkevämpi vuonna 2010, kun palvelimilla oli 4-8 ydintä.
Nykyaikaisissa palvelimissa on 32–128 ydintä. Yksi kone voi suorittaa tuhansia samanaikaisia toimintoja tehokkaasti.
Jos monoliittisi on rakennettu nykyaikaisten rinnakkaisten toteutusmallien avulla, se pystyy hoitamaan massiivisen läpimenon yhdellä käyttökerralla. Väliaikainen täytäntöönpanokoordinaattoreiden toiminta, voit:
// Process thousands of concurrent operations in a monolith
services.AddEphemeralWorkCoordinator<TranslationRequest>(
async (request, ct) => await TranslateAsync(request, ct),
new EphemeralOptions { MaxConcurrency = Environment.ProcessorCount * 4 });
// Bounded, observable, efficient concurrent processing
// No network overhead, no SerDe, no container orchestration
// Can handle 10k+ req/sec on a single machine
Tästä saat:
Kun tee tarve skaalautua yhden koneen ulkopuolelle, voit:
Olet käyttänyt monoliittia useita kertoja.
Asian ydin: Älä jaa "suorituskyvyn" mikropalveluihin. Jakaudu kun Tiettyjen aineosien riippumaton skaalautuminen perustelee toiminnan yleiskustannuksia.
Mikropalvelut eivät vähennä monimutkaisuutta, vaan jakavat sen uudelleen.
"Yksinkertaisessa palvelussa" tarvitaan edelleen kontekstia:
Ihmiset aliarvioivat tämän, koska kaaviot peittävät sen.
Jokaisesta rajasta tulee sopimus.
Jokaisesta sopimuksesta tulee:
Voit vältellä tätä jonkin aikaa "käynnistellä kaikkea yhdessä".
Se on huipennus: jos kaikki otetaan käyttöön yhdessä, rakennetaan monoliitti, joka on vain huonompi.
Aikainen käyttö on aina sama:
Mikään näistä laivoista ei tuota arvoa.
Useimmat tiimit tekevät sen ennen kuin ovat todistaneet tuotteen ansaitsevan monimutkaisuuden.
Tämä on keskeinen virhe: maksat seremoniaveron ennen kuin olet ansainnut arvonKuten päivittäiset standupit, jotka tuhlaavat aikaa ilman yhdenmukaistamistaMikropalveluista voi tulla rituaaliarkkitehtuuria: vaikuttavaa katseltavaa, kallista ylläpidettävää ja irrallaan todellisesta ongelmasta, jota ratkot.
Yhdessä nämä kustannukset eivät katoa - ne kasautuvat, ja ne yhdistelevät sitä, tarvitsiko organisaatio ylipäätään mikropalveluita vai ei.
Arkkitehtuuri auttaa ihmisryhmiä muuttamaan ohjelmistoja turvallisesti.
En halua tehdä vaikutusta muihin insinööreihin.
Ei tyydyttämään kaaviota.
Ei oikeuttamaan alustajoukkuetta, jota sinulla ei ole.
Joukkueen koolla ja käyttöönottokadenssilla on enemmän merkitystä kuin kuvioluettelolla.
Conway's Law ei ole valinnainen, vaan fysiikka.
Arkkitehtuurisi heijastaa viestintärakennettasi, pidit siitä tai et.
Mikropalvelut eivät luo autonomiaa, vaan vaativat sitä.
Useimpien järjestelmien pitäisi alkaa monoliittina, mutta ei huolimattomana.
A modulaarinen monoliitti on
Se tarkoittaa:
Kyse ei ole siitä, että pysymme yksinäisinä ikuisesti. ansaitse rajat ennen kuin teet niistä etäisiä.
Modulaarinen monoliitti pakottaa sinut opettelemaan taitoja, joita mikropalvelut esittävät ratkovansa:
Jos et pysty rakentamaan puhdasta modulaarista monoliittia, mikropalvelut eivät pelasta sinua. Ne vain levittävät sotkua.
Oikean monoliittisen muotoilun avulla lykkää mikropalvelupäätöstä Lukitsematta itseäsi sisään.
Yksi parhaista esimerkeistä on Outbox-kuvio - keino saavuttaa mahdollisimman johdonmukainen ja epäyhtenäinen käsittely Sisällä Monoliitti, jossa on puhdas tie jakeluun myöhemmin.
Sen sijaan, että:
// Tightly coupled synchronous code
public async Task PlaceOrder(Order order)
{
await _orderRepo.Save(order);
await _emailService.SendConfirmation(order); // Blocks on external service
await _inventoryService.Reserve(order.Items); // Blocks on another service
}
Sinä kirjoitat:
// Outbox pattern: write events to a table
public async Task PlaceOrder(Order order)
{
await using var transaction = await _db.Database.BeginTransactionAsync();
// Save order
await _orderRepo.Save(order);
// Write events to outbox table (same transaction)
_db.OutboxMessages.Add(new OutboxMessage
{
EventType = "OrderPlaced",
Payload = JsonSerializer.Serialize(order),
CreatedAt = DateTime.UtcNow
});
await _db.SaveChangesAsync();
await transaction.CommitAsync();
// Background worker picks up outbox events and publishes them
}
Mitä tämä antaa sinulle:
Erytropoietiini SegmentCommercen otoshanke osoittaa tämän tuotantomallin:
// Mostlylucid.SegmentCommerce/Services/Queue/PostgresOutbox.cs
public class PostgresOutbox
{
public async Task PublishAsync<T>(string eventType, T payload)
{
var message = new OutboxMessage
{
EventType = eventType,
Payload = JsonSerializer.Serialize(payload),
CreatedAt = DateTime.UtcNow
};
_db.OutboxMessages.Add(message);
// Caller commits transaction
}
}
// Background service
public class OutboxProcessor : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var pending = await _db.OutboxMessages
.Where(m => !m.Processed)
.OrderBy(m => m.CreatedAt)
.Take(100)
.ToListAsync();
foreach (var message in pending)
{
await ProcessMessage(message);
message.Processed = true;
}
await _db.SaveChangesAsync();
await Task.Delay(1000, stoppingToken);
}
}
}
Tämä tapahtuu yhdessä sovelluksessa tänään. Kun sinun täytyy skaalata:
Olet rakentanut mikropalveluvalmiin infrastruktuurin maksamatta jakeluveroa.
Mikään näistä ei vaadi jakelua, vaan ne kaikki helpottavat jakelua myöhemmin.
Mikropalveluissa on järkeä, kun rajoitukset ovat todellisia, eivät kunnianhimoisia.
Kriittinen kysymys ei ole: "Pitäisikö meidän käyttää mikropalveluita?" ovatko hyödyt toimintaveroa suuremmat?
Kuten valita väliltä pienet paikalliset mallit ja raja-alueet LLM:t, Tässä on kyse siitä, että ymmärretään, mihin monimutkaisuus kuuluu ja mihin epäonnistumistiloihin sinulla on varaa.
Jos syysi sisältää sanat May, lopulta, tai TulevaisuusvarmaSe ei luultavasti ole syy.
Tämä muuttuu, kun monoliitit jaetaan palveluihin.
Monolith:
public async Task<Order> PlaceOrder(OrderRequest request)
{
var user = await _userService.GetUser(request.UserId);
var inventory = await _inventoryService.CheckStock(request.Items);
var price = _pricingService.Calculate(request.Items, user.Tier);
var order = new Order { /* ... */ };
await _orderRepository.Save(order);
await _emailService.SendConfirmation(order);
return order;
}
Kokonaislatenssi: ~50ms (käynnissä olevat puhelut + 2 DB-kyselyä)
Mikropalvelut:
public async Task<Order> PlaceOrder(OrderRequest request)
{
// HTTP call to User Service (network + TLS + serialization)
var user = await _httpClient.GetAsync<User>($"http://user-service/users/{request.UserId}");
// HTTP call to Inventory Service
var inventory = await _httpClient.PostAsync<StockResult>(
"http://inventory-service/check", request.Items);
// HTTP call to Pricing Service
var price = await _httpClient.PostAsync<PriceResult>(
"http://pricing-service/calculate",
new { items = request.Items, tier = user.Tier });
// HTTP call to Order Service
var order = await _httpClient.PostAsync<Order>(
"http://order-service/orders", request);
// Event published to message bus
await _messageBus.Publish(new OrderPlaced { OrderId = order.Id });
return order;
}
Kokonaislatenssi: ~400 ms (jopa optimistinen 50–80 ms per HTTP-puhelu todellisissa ympäristöissä + viestibussi julkaistaan ~20 ms)
Voittosi:
Se, mitä maksoit:
Monolith-virheiden käsittely:
try
{
var order = await PlaceOrder(request);
return Ok(order);
}
catch (InsufficientStockException)
{
return BadRequest(new { error = "Out of stock" });
}
catch (Exception ex)
{
_logger.LogError(ex, "Order placement failed");
return StatusCode(500);
}
Mikropalveluiden virheiden käsittely:
try
{
// Call User Service
var user = await _httpClient.GetAsync<User>($"http://user-service/users/{request.UserId}");
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
return NotFound(new { error = "User not found" });
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.ServiceUnavailable)
{
// Retry with exponential backoff?
// Circuit breaker opened?
// Fail fast or degrade gracefully?
_logger.LogWarning("User service unavailable, retrying...");
await Task.Delay(TimeSpan.FromMilliseconds(100));
// ... retry logic ...
}
catch (TaskCanceledException ex)
{
// Timeout - was the request processed? Do we retry?
_logger.LogError("User service timeout");
return StatusCode(503, new { error = "Service temporarily unavailable" });
}
try
{
var inventory = await _httpClient.PostAsync<StockResult>(
"http://inventory-service/check", request.Items);
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.BadRequest)
{
// Business error from remote service
var errorDetails = await ex.Content.ReadAsAsync<ErrorResponse>();
return BadRequest(new { error = errorDetails.Message });
}
catch (HttpRequestException ex)
{
// Which service failed? Network issue? Service down?
// Do we have a fallback? Cached data? Fail fast?
_logger.LogError(ex, "Inventory service failed");
// Maybe try a backup instance?
// ... more retry logic ...
}
// And repeat for every service call...
Jokaisessa verkkopuhelussa esitellään:
Monolithin käyttöönotto:
# Build
dotnet publish -c Release
# Run migrations
dotnet ef database update
# Deploy
docker push myapp:v1.2.3
kubectl set image deployment/myapp myapp=myapp:v1.2.3
# Rollback if needed
kubectl rollout undo deployment/myapp
Mikropalveluiden käyttöönotto:
Muutat järjestysrakennetta. Order sisältää UserTier suoraan (epänormaali suorituskyvylle).
# 1. Deploy Pricing Service v2 (understands both old and new Order format)
kubectl set image deployment/pricing-service pricing-service=pricing:v2.0.0
# 2. Wait for rollout and monitor
kubectl rollout status deployment/pricing-service
# Check metrics - is v2 working with old order format?
# 3. Deploy Order Service v2 (starts sending new format)
kubectl set image deployment/order-service order-service=order:v2.0.0
# 4. Monitor for errors
# If Pricing v2 has a bug, you can't just rollback Order service
# You have to rollback both in reverse order
# 5. Deploy User Service v2 (stops including tier in response)
# But wait - old Order Service instances might still be running!
# Need zero-downtime rollout or maintain backward compat
# 6. Eventually clean up old code paths in Pricing Service v3
# (6 months later because you're scared to remove backward compat)
Mikropalveluiden myötä jokainen muutos ylittää useita palveluita. Sopimuskehitys edellyttää:
Mikään tästä ei ole mahdotonta, mutta se vaatii kuria, työkaluja ja kokemusta siitä, että suurin osa joukkueista ansaitsee vain vuosien tuskan jälkeen.
Monolithin vikailmoitus: "Järjestys epäonnistuu premium-käyttäjien kohdalla"
// Set breakpoint in PlaceOrder
// Step through each call
// User tier is null - ah, there's the bug
// Fix, test, deploy
Mikropalveluiden vikailmoitus: "Järjestys epäonnistuu premium-käyttäjien kohdalla"
# 1. Check logs - which service failed?
kubectl logs -l app=order-service --tail=100
# 2. Oh, it's calling Pricing service. Check those logs
kubectl logs -l app=pricing-service --tail=100
# 3. Grep for correlation ID across all services
stern -l app=order-service,app=pricing-service,app=user-service \
| grep "correlation-id-xyz"
# 4. Reconstruct the call chain from distributed traces
# Open Jaeger/Zipkin, find the trace
# User service returned 200 OK
# Pricing service returned 400 Bad Request
# Error: "user.tier is required"
# 5. Check User service - did it send tier?
# Look at schema version - ah, User v1.2 stopped sending tier
# When did that deploy? Was Pricing service updated?
# 6. Check API contracts
# Pricing expects tier, User stopped sending it
# Who approved this change?
# 7. Fix requires coordinating two teams
# Can't just deploy a fix - need contract negotiation
Tämä on todellisuutta: vikoja, jotka olivat 5 minuutin korjauksia, tulee usean joukkueen tutkimuksia.
Jos tämä kuulostaa äärimmäisyyksiltä, se on hyvä. Mikropalvelut ovat äärimmäistä tekniikkaa.
Mikropalvelut ovat yksisuuntainen ovi.
Turvallinen reitti näyttää tylsältä:
Jos et pysty vetämään rajaa monoliittiin (todennäköisillä riippuvuuksilla), et voi poistaa sitä turvallisesti.
Hanki saumat ensin paikalliseen kuntoon.
Lukeminen on helpompaa:
Lue mallit ensin, jos tarvitset asumuseroa.
Synkroniset palvelupuhelut luovat riippuvuusketjuja.
Async-viestit luovat:
Aloita erottelemalla tapahtumat ja faktat toisistaan, ei viipaloimalla päätetapahtumia.
Ei isoa pamausta.
Ei: "Kirjoitamme sen kunnolla."
Leikkaa raja pois, mittaa se, omista se ja jatka eteenpäin.
Jos et pysty selittämään, miksi palvelu on olemassa itsenäisesti, sitä ei pitäisi olla lainkaan.
Mikropalvelut eivät ole lähtökohta vaan lopputulos.
Monimutkaisuus on ansaittava. Hajautetut järjestelmät eivät ole virkamerkki vaan velkainstrumentti.
Vaihtokaupan yhtälö on yksinkertainen:
Microservices Value = (Team Autonomy + Independent Scaling + Failure Isolation)
- (Latency Tax + SerDe Tax + Operational Overhead + Coordination Cost)
Useimmille joukkueille - erityisesti alkuvaiheen tai yhden joukkueen tuotteille - oikea puoli hallitsee. Maksat valtavat kustannukset hyödyistä, joita et vielä tarvitse.
Mutta kun olet:
Sitten vasen puoli alkaa voittaa.
Kysy nämä kysymykset järjestyksessä:
Voiko yksi joukkue yhä omistaa tämän?
→ Kyllä: pysy yksin. Ei: harkitse jakoa.
Estävätkö joukkueet toistensa vapautumissyklit?
→ Kyllä: mikropalveluista voi olla apua. Ei: koordinaatio toimii.
Onko meillä toiminnallista voimaa käyttää hajautettuja järjestelmiä?
→ Ei: rakenna se ensin. Kyllä: etene varovasti.
Voimmeko jäljittää, debuggata ja lähettää useita palveluja ilman sankaritekoja?
→ Ei: et ole valmis. Kyllä: saatat olla.
Olemmeko ensin todistaneet koodin rajat?
→ Ei: korjaa monoliitin rakenne. Kyllä: irrottaminen on turvallisempaa.
Jos et pääse kysymyksen 3 ohi luottavaisin mielin, et ole valmis mikropalveluihin - ja se on hyvä. Tylsä arkkitehtuuri on kilpailuetu.
Menestyksekkäimmät tuotteet rakennettiin ensin monoliitteiksi: GitHub, Shopify, Stack Overflow, Basecamp. Ne kehittyivät jaetuiksi järjestelmiksi vain, kun organisaation kipu sitä vaati.
Tehtäväsi ei ole rakentaa vaikuttavinta arkkitehtuuria, vaan tuottaa arvoa ja samalla minimoida sattumanvaraista monimutkaisuutta.
Eikä yksikään asiakas ole koskaan maksanut ylimääräistä, koska järjestelmäsi käytti Kafkaa.
Joskus se tarkoittaa mikropalveluita, yleensä hyvin jäsenneltyä monoliittia ja kuria pitää se sellaisena.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.