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
Los microservicios se han convertido en la arquitectura predeterminada del "sistema serio".
Si quieres sonar maduro, hablas de mallas de servicio, autobuses de eventos, rastreo distribuido y "desplegabilidad independiente". Si quieres lucir listo para la empresa, dibujas cajas hasta que el diagrama parece un tazón de espaguetis que alguien lanzó a una pizarra blanca.
Pero aquí está el correctivo de la mayoría de los jóvenes no se les da:
Los microservicios no son una actualización de la arquitectura de aplicaciones, sino una estrategia de escalamiento organizacional.
Si no tienes ya el dolor organizativo que resuelven, adoptarlos temprano no te hace a prueba de futuro. Te hace presente roto.
Esto no es un argumento teórico. Es un intercambio de ingeniería, y como todos los intercambios, sólo tiene sentido cuando entiendes el impuesto que estás pagando y lo que estás recibiendo a cambio.
He sido tan culpable como cualquiera de "hacer microservicios" sin internalizar completamente los gastos generales de organización que implican. Es fácil quedar atrapado en el vocabulario y olvidar que el verdadero desafío es gestionar la complejidad entre equipos y sistemas.
Al final se reduce al patrón más antiguo en ingeniería de software: KISS - mantenlo sencillo, estúpido.
Los microservicios se venden como arquitectura predeterminada porque la narrativa es seductora:
Este encuadre está al revés.
Los microservicios no son principalmente sobre la estructura de código. quién puede cambiar qué, sin hablar con quién, y con qué frecuencia.
Si no tienes:
...entonces no estás resolviendo un problema de organización.
La arquitectura se trata en última instancia de habilitar a la gente, no mover cajas alrededor.
Has visto este diagrama.

Esta no es una "arquitectura de ejemplo para copiar", es una etiqueta de advertencia.
Los cursos a menudo enseñan microservicios como diagramas porque los diagramas son más fáciles que enseñar responsabilidad operacional.
El reframing importante es este: que el diagrama no es un estado objetivo. Es un superficie de costes.
Cada caja es un compromiso:
Las flechas se ven impresionantes, también esconden la realidad.
Porque cada flecha es:
Si una arquitectura requiere esto en el primer día, usted no está construyendo un producto. Usted está construyendo una plataforma.
Como todas las decisiones arquitectónicas, los microservicios vienen con un impuesto sobre la complejidad (véase Jugar con el código: impuesto de complejidad. La diferencia es que este impuesto se compone a través de cada límite de servicio, cada despliegue, y cada modo de fallo.
Los costos rara vez son explícitos, por lo general se disfrazan de "mejores prácticas".
Cada servicio quiere:
Si un equipo no puede hacer esto cómodamente, no tiene microservicios. un monolito distribuido con pasos adicionales.
En un monolito, una llamada es una llamada de función.
En los microservicios, una "llamada" es una tregua negociada entre:
Los modos de fallo se multiplican:
Ya no depuras bichos. Estados del sistema.
Este es un costo que casi nunca se discute en la defensa de microservicios: gastos generales de serialización y desarealización (SerDe).
Cada límite de servicio significa:
// 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
Por llamada, eso es ~150μs de carga de CPU pura antes de que la red se involucre.
Ahora multiplíquelo a través de una cadena de llamadas:
En una cadena de 5 servicios, usted está gastando ~1.5ms sólo en SerDe Si estás usando XML, Protocol Buffers con reflexión, o serializadores ineficientes, multiplíquelo por 2-10x.
Sí, puedes reducir esto con técnicas como Generación de fuentes JSON en .NET:
[JsonSerializable(typeof(User))]
internal partial class UserJsonContext : JsonSerializerContext { }
// ~30-40% faster than reflection-based serialization
var json = JsonSerializer.Serialize(user, UserJsonContext.Default.User);
Pero sigues haciendo serialización, acabas de hacer el impuesto un poco más barato, en un monolito, el impuesto es cero.
Una vez perfilé un sistema de búsqueda de direcciones con esta arquitectura (todos en contenedores de Kubernetes):
ASP.NET API → Go Service (fan-out) → ASP.NET Search Service → Elasticsearch
El servicio Go manejó el fan-out a múltiples fuentes de datos y resultados agregados. Cada solicitud significó múltiples saltos de serialización a través de los límites del contenedor.
El desglose de una búsqueda de direcciones típica:
Para un ventilador de 3-backend:
Pasamos casi tanto tiempo en SerDe como en la búsqueda real.
El costo no era el servicio Go en sí mismo - fan-out tenía sentido para el caso de uso. límites de servicio. Cada borde de contenedor significa serializar → red → deserializar.
En un monolito haciendo la misma lógica de fan-out:
Este costo:
En un monolito, este costo es cero. El objeto ya está en memoria.
He aquí un argumento de ventas común: "Los microservicios mejoran el rendimiento".
Esto es al revés.
Los microservicios son más lento Vamos a ser precisos sobre lo que queremos decir:
Producción (solicitudes por segundo por núcleo de CPU):
Escalabilidad (capacidad para atender más solicitudes totales mediante la adición de recursos):
Los microservicios le permiten escala independientementeSi tu servicio de inventario necesita recursos de 10x pero tu servicio de usuario no, puedes escalarlos por separado. cuando lo necesitas.
Pero no es una optimización de rendimiento. estrategia de escalamientoY viene con un impuesto.
El argumento "microservicios escala mejor" tuvo más sentido en 2010 cuando los servidores tenían 4-8 núcleos.
Los servidores modernos tienen 32-128 núcleos. Una sola máquina puede ejecutar miles de operaciones simultáneas de manera eficiente.
Si su monolito está construido con modernos patrones de ejecución concurrente, puede manejar el rendimiento masivo en una sola implementación. Por ejemplo, usando patrones como Coordinadores de ejecución efímera, puedes:
// 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
Esto le da:
Cuando tú @info: tooltip necesidad de escalar más allá de una máquina, usted puede:
Sigues ejecutando un monolito, lo has desplegado varias veces.
El punto: No se divida en microservicios para "rendimiento". escalado independiente de componentes específicos justifica los gastos generales de funcionamiento.
Los microservicios no reducen la complejidad, sino que lo redistribuyen.
El "servicio simple" todavía necesita contexto:
La gente subestima esto porque los diagramas lo ocultan.
Cada límite se convierte en un contrato.
Cada contrato se convierte en:
Usted puede esquivar esto por un tiempo con "sólo desplegar todo juntos".
Ese es el chiste: si despliegas todo juntos, construyes un monolito, sólo uno peor.
El gasto temprano es siempre el mismo:
Ninguna de estas naves valor de producto.
Y la mayoría de los equipos lo hacen antes de demostrar que el producto merece la complejidad.
Este es el error central: Usted paga el impuesto de la ceremonia antes de haber ganado el valorComo standups diarios que pierden tiempo sin ofrecer alineación, los microservicios pueden convertirse en arquitectura ritual: impresionante de ver, caro de mantener y desconectado del problema real que estás resolviendo.
Tomados en conjunto, estos costos no desaparecen - se agravan. Y se agravan si la organización necesita o no microservicios en primer lugar.
La arquitectura existe para ayudar a grupos de humanos a cambiar el software de forma segura.
No para impresionar a otros ingenieros.
No satisfacer un diagrama.
No para justificar un equipo de plataformas que no tienes.
El tamaño del equipo y la cadencia de despliegue importan más que el catálogo de patrones.
La ley de Conway no es opcional, es física.
Tu arquitectura reflejará tu estructura de comunicación te guste o no.
Los microservicios no crean autonomía, lo requieren.
La mayoría de los sistemas deberían empezar como monolito, pero no como descuidado.
A monolito modular es:
Significa:
El punto no es permanecer monolítico para siempre. ganar límites antes de hacerlos remotos.
Un monolito modular te obliga a aprender las habilidades que los microservicios pretenden resolver:
Si no puedes construir un monolito modular limpio, los microservicios no te salvarán.
El diseño monolito correcto le permite aplazar la decisión sobre los microservicios sin encerrarte.
Uno de los mejores ejemplos es el patrón de salida - una manera de lograr la consistencia eventual y el procesamiento sincronizado interior un monolito, con un camino limpio a la distribución más tarde.
En lugar de:
// 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
}
Escribes:
// 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
}
Lo que esto te da:
Los Proyecto de muestra de SegmentCommerce demuestra este patrón en la producción:
// 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);
}
}
}
Esto se ejecuta en una sola aplicación hoy. Cuando necesitas escalar:
Usted ha construido infraestructura lista para microservicios sin pagar el impuesto de distribución.
Ninguno de estos requisitos requiere distribución. Todos ellos hacen la distribución más fácil más adelante.
Los microservicios tienen sentido cuando las limitaciones son reales, no aspirativas.
La pregunta crítica no es "¿deberíamos usar microservicios?" ¿Los beneficios superan el impuesto operativo?
Como elegir entre pequeños modelos locales y LLMs fronterizos, se trata de entender dónde pertenece la complejidad y qué modos de fallo puede permitirse.
Si tu razón contiene las palabras puede, con el tiempo, o a prueba de futuro, probablemente no sea una razón.
Esto es lo que cambia cuando divides un monolito en servicios.
Monolito:
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;
}
Latencia total: ~50ms (llamadas en proceso + 2 consultas DB)
Microservicios:
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;
}
Latencia total: ~400ms (incluso optimista 50–80ms por llamada HTTP en entornos reales + bus de mensajes publicar ~20ms)
Lo que ganaste:
Lo que pagaste:
Manejo de errores de Monolito:
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);
}
Manejo de errores de microservicios:
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...
Cada llamada de red introduce:
Despliegue de Monolito:
# 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
Despliegue de microservicios:
Estás cambiando la estructura del pedido. Order incluye UserTier directamente (desnormalizado para el rendimiento).
# 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)
Con los microservicios, cada cambio cruza múltiples servicios. La evolución del contrato requiere:
Nada de esto es imposible, pero requiere disciplina, herramientas y experiencia que la mayoría de los equipos sólo ganan después de años de dolor.
Informe de fallo de Monolith: "La colocación de pedidos falla para los usuarios premium"
// Set breakpoint in PlaceOrder
// Step through each call
// User tier is null - ah, there's the bug
// Fix, test, deploy
Informe de fallo de Microservices: "La colocación de pedidos falla para los usuarios premium"
# 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
Esta es la realidad: Los errores que fueron correcciones de 5 minutos se convierten en investigaciones multi-equipo.
Si esto suena extremo, bueno, los microservicios son ingeniería extrema.
Los microservicios son una puerta de un solo sentido.
El camino seguro parece aburrido:
Si no puedes dibujar el límite dentro del monolito (con dependencias ejecutables), no puedes extraerlo con seguridad.
Obtener las costuras justo a nivel local primero.
Las lecturas son más fáciles:
Mover modelos leídos hacia fuera primero si usted necesita separación.
Las llamadas de servicio sincronizadas crean cadenas de dependencia.
La mensajería de Async crea:
Comience separando eventos y hechos, no cortando puntos finales.
No es una gran explosión.
No "lo reescribiremos correctamente".
Cortar un límite, medirlo, poseerlo, y seguir adelante.
Si no puedes explicar por qué un servicio existe independientemente, no debería existir en absoluto.
Los microservicios no son un punto de partida, son un resultado.
La complejidad debe ser ganada. Los sistemas distribuidos no son una insignia, son un instrumento de deuda.
La ecuación de compensación es simple:
Microservices Value = (Team Autonomy + Independent Scaling + Failure Isolation)
- (Latency Tax + SerDe Tax + Operational Overhead + Coordination Cost)
Para la mayoría de los equipos - especialmente los productos de primera etapa o de un solo equipo - el lado derecho domina. Usted paga costos enormes por los beneficios que no necesita todavía.
Pero cuando tienes:
...entonces el lado izquierdo comienza a ganar. El impuesto se justifica.
Haga estas preguntas en orden:
¿Puede un equipo seguir siendo dueño de este extremo a extremo?
→ Sí: permanecer monolito. No: considerar la división.
¿Los equipos están bloqueados por los ciclos de liberación del otro?
→ Sí: los microservicios podrían ayudar. No: la coordinación está funcionando.
¿Tenemos el músculo operativo para ejecutar los sistemas distribuidos?
→ No: construirlo primero. Sí: proceder con cuidado.
¿Podemos rastrear, depurar y desplegar múltiples servicios sin heroísmo?
→ No: no estás listo. Sí: podrías estarlo.
¿Hemos probado los límites en código primero?
→ No: fija tu estructura monolítica. Sí: la extracción es más segura.
Si no puedes pasar la pregunta 3 con confianza, no estás listo para microservicios - y eso está bien. La arquitectura aburrida es una ventaja competitiva.
La mayoría de los productos exitosos fueron construidos como monolitos primero: GitHub, Shopify, Stack Overflow, Basecamp. Ellos evolucionaron en sistemas distribuidos sólo cuando el dolor organizacional lo exigía.
Su trabajo no es construir la arquitectura más impresionante. Es entregar valor mientras minimiza la complejidad accidental.
Y ningún cliente ha pagado nunca más porque su sistema usó Kafka.
A veces eso significa microservicios. Por lo general, significa un monolito bien estructurado y la disciplina para mantenerlo de esa manera.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.