Si estás preparando una entrevista técnica para un rol de desarrollador .NET con foco en microservicios, estas 24 preguntas cubren los temas que más aparecen en procesos de selección. Las respondo con el nivel de detalle que se espera en una entrevista senior.
Comunicación entre servicios
1. ¿Cuál es la diferencia entre comunicación sincrónica y asincrónica? ¿Cuándo elegirías una sobre la otra?
Sincrónica: el servicio que llama espera la respuesta antes de continuar. El acoplamiento es temporal: si el servicio destino está caído, la operación falla. Ejemplo: REST o gRPC donde el cliente hace la request y bloquea hasta recibir la respuesta.
Asincrónica: el servicio emisor publica un mensaje y sigue su ejecución sin esperar. El receptor procesa cuando puede. Ejemplo: RabbitMQ, Azure Service Bus, Kafka.
¿Cuándo usar cada una?
- Sincrónica → cuando necesitás la respuesta para continuar (consulta de stock antes de confirmar una compra), latencia baja es prioritaria, o es una operación de lectura simple.
- Asincrónica → cuando el procesamiento puede diferirse (envío de email, generación de reportes), necesitás desacoplar servicios para mayor resiliencia, o querés escalar consumidores independientemente.
2. ¿En qué escenarios usarías REST vs gRPC para comunicación entre servicios?
REST es la opción por defecto cuando:
- Los consumidores son variados (apps móviles, browsers, terceros).
- La interoperabilidad importa más que la performance.
- Necesitás que sea fácil de explorar con herramientas como Swagger/Postman.
gRPC brilla cuando:
- La comunicación es interna entre microservicios (backend-to-backend).
- El volumen de datos es alto y el rendimiento es crítico (Protocol Buffers son mucho más compactos que JSON).
- Necesitás streaming bidireccional.
- Querés contratos fuertes y tipados (el
.protoes el contrato).
En .NET 8, Grpc.AspNetCore tiene soporte de primera clase y es muy fácil de configurar.
3. ¿Qué librerías de mensajería .NET has usado en proyectos de producción?
Las más comunes en el ecosistema .NET:
- MassTransit: abstracción sobre RabbitMQ, Azure Service Bus, Amazon SQS. Maneja reintentos, sagas, outbox pattern. Es el estándar de facto en proyectos .NET complejos.
- NServiceBus: más enterprise, con licencia comercial. Muy robusto para flujos complejos.
- Azure Service Bus SDK: cuando el stack es 100% Azure.
- Confluent Kafka .NET Client: para escenarios de event streaming de alto volumen.
- Rebus: alternativa liviana a MassTransit.
Patrones de mensajería
4. ¿Qué es el patrón Outbox y cómo lo implementarías en .NET?
El Outbox pattern resuelve el problema de la doble escritura: ¿cómo garantizás que guardaste en la base de datos y publicaste el evento, sin riesgo de que uno falle y el otro no?
La idea: en la misma transacción de base de datos que actualizás tu entidad, escribís el mensaje en una tabla OutboxMessages. Un proceso separado (poller) lee esa tabla y publica los mensajes al broker. Una vez publicado, marca el mensaje como procesado.
Implementación en .NET:
- MassTransit Outbox: soporte nativo con EF Core. Solo configurás
.AddEntityFrameworkOutbox()y se encarga de todo. - Manual con EF Core + Hosted Service: tabla
OutboxMessages, unIHostedServiceque pollea cada N segundos y publica con tu broker. - CAP (DotNetCore.CAP): librería open source que implementa outbox + inbox patterns con soporte para múltiples bases de datos y brokers.
5. ¿Qué es el patrón Inbox y cómo lo implementarías en .NET?
El Inbox pattern es la contraparte del Outbox, pero del lado del consumidor. Garantiza idempotencia: si el mismo mensaje llega dos veces (por reintentos del broker), solo se procesa una vez.
La idea: cuando llega un mensaje, primero verificás si su ID ya existe en la tabla InboxMessages. Si existe, lo descartás. Si no, lo insertás y procesás en la misma transacción.
En .NET: MassTransit con UseInMemoryInbox() o EF Inbox, y CAP también lo implementa nativamente. Para implementación manual, basta con una tabla con la clave del mensaje y un UNIQUE INDEX.
11. ¿Cuál es la diferencia entre mensajería point-to-point y publish-subscribe?
- Point-to-point (cola): un productor envía un mensaje a una cola. Un solo consumidor lo recibe y procesa. Si hay múltiples consumidores escuchando la misma cola, solo uno recibe cada mensaje. Ideal para distribuir trabajo.
- Publish-Subscribe (topic/exchange): el productor publica en un topic. Todos los suscriptores registrados reciben una copia del mensaje. Ideal para notificar eventos a múltiples servicios interesados.
En RabbitMQ: queue = P2P, fanout/topic exchange = pub/sub. En Azure: Storage Queue = P2P, Service Bus Topic = pub/sub.
12. ¿Cuál es la diferencia entre una queue, un topic y un exchange en RabbitMQ?
- Queue: buffer donde se almacenan los mensajes. Los consumidores consumen de acá.
- Exchange: recibe los mensajes de los productores y los enruta a las queues según su tipo (direct, fanout, topic, headers). Es el router.
- Topic (tipo de exchange): enruta mensajes a queues según un patrón de routing key con wildcards (
*= una palabra,#= cero o más palabras). Ej:pedidos.#capturapedidos.creado,pedidos.cancelado.urgente, etc.
El flujo es: Productor → Exchange → (binding) → Queue → Consumidor.
Resiliencia
6. ¿Cómo configurarías políticas de retry para comunicación REST entre servicios en .NET?
Con Polly (o Microsoft.Extensions.Http.Resilience en .NET 8+):
// .NET 8 - nuevo approach con Microsoft.Extensions.Http.Resilience
builder.Services.AddHttpClient<IProductoService, ProductoService>()
.AddStandardResilienceHandler(options =>
{
options.Retry.MaxRetryAttempts = 3;
options.Retry.Delay = TimeSpan.FromSeconds(1);
options.Retry.BackoffType = DelayBackoffType.Exponential;
});
// Approach clásico con Polly
builder.Services.AddHttpClient<IProductoService, ProductoService>()
.AddPolicyHandler(HttpPolicyExtensions
.HandleTransientHttpError()
.WaitAndRetryAsync(3, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))));
Importante: usá exponential backoff con jitter para evitar que todos los servicios reintenten al mismo tiempo (thundering herd problem).
7. Explicá el patrón Circuit Breaker. ¿Cuándo debería aplicarse?
El Circuit Breaker es como un fusible eléctrico para llamadas entre servicios. Tiene tres estados:
- Closed (normal): las llamadas pasan. Se cuentan los fallos.
- Open (disparado): cuando los fallos superan un umbral, el circuito se abre. Las llamadas fallan inmediatamente sin intentar llegar al servicio (fail fast). Esto evita sobrecargar un servicio ya caído.
- Half-Open: después de un tiempo, se permite una llamada de prueba. Si funciona, vuelve a Closed. Si falla, regresa a Open.
¿Cuándo aplicarlo? Cuando un servicio externo es propenso a fallos temporales y querés evitar que una cascada de errores derribe toda tu arquitectura. Con Polly: .AddCircuitBreaker(). En .NET 8, el StandardResilienceHandler incluye circuit breaker por defecto.
8. ¿Qué patrones de resiliencia en comunicación conocés? Explicá cada uno.
- Retry: reintenta la operación fallida N veces, idealmente con backoff exponencial.
- Circuit Breaker: corta el circuito tras demasiados fallos para proteger al servicio caído.
- Timeout: limita cuánto tiempo espera una llamada. Sin timeout, un hilo puede quedar bloqueado indefinidamente.
- Bulkhead: aísla los recursos usados por cada operación. Si el servicio A se cuelga, no consume todos los threads del pool impactando al servicio B.
- Fallback: cuando falla, retorna un valor por defecto o una respuesta alternativa.
- Hedge (nuevo en .NET 8): lanza múltiples requests en paralelo y usa la primera respuesta exitosa. Reduce la latencia del percentil 99.
9 y 10. ¿Cómo configurarías políticas de resiliencia para clientes gRPC en .NET?
gRPC en .NET usa HttpClient internamente, así que Polly aplica igual. Pero gRPC también tiene su propio mecanismo nativo de retry:
// Retry nativo de gRPC (service config)
var defaultMethodConfig = new MethodConfig
{
Names = { MethodName.Default },
RetryPolicy = new RetryPolicy
{
MaxAttempts = 4,
InitialBackoff = TimeSpan.FromSeconds(1),
MaxBackoff = TimeSpan.FromSeconds(5),
BackoffMultiplier = 1.5,
RetryableStatusCodes = { StatusCode.Unavailable }
}
};
var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
});
Para circuit breaker y políticas más complejas, combiná con Polly como en REST.
Eventos
13. ¿Cuándo usarías thin events vs thick events? ¿Qué patrones aplicás para recuperar datos faltantes?
- Thin event: el evento solo contiene el ID y el tipo. Ej:
{ "tipo": "PedidoCreado", "pedidoId": "abc-123" }. El consumidor debe consultar al servicio origen para obtener los datos completos. - Thick event: el evento incluye todos los datos relevantes. Ej:
{ "tipo": "PedidoCreado", "pedidoId": "abc-123", "monto": 500, "cliente": {...} }. El consumidor es autónomo.
Thin es mejor cuando: los datos cambian frecuentemente, los payloads son grandes, o el consumidor puede no necesitar todos los campos.
Thick es mejor cuando: la autonomía del consumidor es prioritaria, querés evitar llamadas adicionales, o el servicio origen podría no estar disponible.
Patrón para recuperar datos con thin events: Event-Carried State Transfer — el consumidor llama al servicio origen via API para obtener el estado actual. Si el servicio no está disponible, el mensaje se reencola (retry policy).
14. Si los thin events dependen de callbacks a un servicio, ¿cómo evitás que ese servicio se convierta en un cuello de botella?
- Caché local: el consumidor cachea los datos del servicio origen por un tiempo razonable.
- Read replicas: exponer réplicas de lectura del servicio origen.
- CQRS + proyecciones locales: el consumidor mantiene una proyección local de los datos que necesita, sincronizada via eventos.
- Rate limiting del consumidor: controlar cuántas consultas hace por segundo.
- Async enrichment: procesar el evento en dos pasos — primero guardás el evento thin, luego un proceso separado enriquece los datos en background.
Sagas y transacciones distribuidas
15. Explicá el patrón Saga. ¿Cuáles son las dos estrategias de coordinación y cómo las implementarías en .NET?
Una Saga es una secuencia de transacciones locales coordinadas entre múltiples servicios. No hay transacción distribuida clásica (2PC); en cambio, cada servicio hace su transacción local y emite un evento. Si algo falla, se ejecutan pasos compensatorios.
Choreography (Coreografía): no hay coordinador central. Cada servicio escucha eventos y reacciona emitiendo sus propios eventos. Desacoplado, pero difícil de rastrear el flujo completo.
Orchestration (Orquestación): un servicio central (el orquestador) dirige el flujo, le dice a cada servicio qué hacer y maneja los errores. Más fácil de entender y debuggear.
En .NET: MassTransit tiene soporte nativo de Sagas con State Machine (orquestación), usando EF Core o Redis para persistir el estado. Es la implementación más completa del ecosistema.
16. ¿Cómo funcionan los pasos compensatorios en Sagas de Orchestration y Choreography?
En Orchestration: el orquestador sabe exactamente qué pasos se ejecutaron. Si el paso 3 falla, llama explícitamente a las acciones compensatorias del paso 2 y paso 1 en orden inverso. Centralizado y predecible.
En Choreography: cada servicio escucha el evento de fallo y ejecuta su propia compensación. Ej: si PagoFallido se emite, el servicio de inventario escucha ese evento y libera el stock reservado. Más complejo de coordinar porque no hay vista global del estado.
Infraestructura y arquitectura
17. ¿Con qué API Gateways has trabajado? ¿Cómo configurarías routing entre múltiples instancias de un mismo servicio?
Opciones populares en el ecosistema .NET:
- YARP (Yet Another Reverse Proxy): de Microsoft, integración nativa con .NET, altamente configurable.
- Ocelot: API Gateway open source para .NET, fácil de configurar con JSON.
- Kong, NGINX, Traefik: opciones agnósticas al lenguaje.
- Azure API Management / AWS API Gateway: opciones managed cloud.
Load balancing entre instancias: el gateway descubre las instancias via service discovery (Consul, Kubernetes Service DNS) y distribuye con round-robin, least connections, u otras estrategias. En Kubernetes, el Service abstrae esto automáticamente.
18. ¿Qué es service discovery y qué librerías o herramientas has usado en .NET?
Service Discovery es el mecanismo por el cual un servicio encuentra la dirección de red de otro servicio, sin hardcodearla.
- Client-side discovery: el cliente consulta al registro (Consul, Eureka) y elige una instancia.
- Server-side discovery: el cliente hace la request al gateway/load balancer, que resuelve internamente (Kubernetes Services).
En .NET:
- Consul + Steeltoe o Consul.Net: registro y descubrimiento programático.
- Microsoft.Extensions.ServiceDiscovery (.NET 8+): nuevo package oficial con integración a Aspire y Kubernetes DNS.
- Kubernetes: DNS interno (
http://servicio-nombre) es la forma más común en producción.
19. Explicá el patrón Sidecar y describí casos de uso prácticos en microservicios.
El Sidecar es un contenedor auxiliar que corre junto al servicio principal (en el mismo pod en Kubernetes) y agrega funcionalidades sin modificar el código del servicio.
Casos de uso:
- Service Mesh (Envoy/Istio): el sidecar proxy intercepta todo el tráfico de red para implementar mTLS, circuit breaking, observabilidad y retry sin que el servicio sepa nada.
- Log shipping: Fluentd o Filebeat como sidecar leen los logs del contenedor principal y los envían a Elasticsearch.
- Configuración dinámica: un sidecar sincroniza config desde Consul/Vault al filesystem del servicio principal.
- Rotación de secretos: Vault Agent como sidecar renueva tokens y certificados automáticamente.
20. Explicá el patrón Anti-Corruption Layer. ¿Cuándo es más valioso?
El Anti-Corruption Layer (ACL) es una capa de traducción que aísla tu modelo de dominio del modelo de un sistema externo o legacy. En lugar de contaminar tu código con las estructuras del sistema externo, el ACL traduce entre ambos mundos.
Es más valioso cuando:
- Integrás con un sistema legacy con un modelo de datos confuso o inconsistente.
- Consumís una API de terceros cuyo contrato puede cambiar.
- Estás migrando un monolito a microservicios progresivamente (strangler fig pattern).
- El bounded context externo usa terminología diferente al tuyo.
En .NET: el ACL suele ser un servicio o capa de mappers/adapters. AutoMapper puede ayudar, pero muchas veces el mapeo es suficientemente complejo para justificar mappers explícitos.
Migración, hosting y diseño
21. ¿Cómo migrarías una aplicación monolítica ASP.NET a microservicios? ¿Cuáles son los pasos clave?
La estrategia más probada es el Strangler Fig Pattern:
- Identificar bounded contexts: analizá el monolito y encontrá los dominios naturales (pedidos, inventario, usuarios). Domain-Driven Design ayuda acá.
- Extraer un servicio a la vez: empezá por los módulos más independientes y con menos acoplamiento.
- Usar un API Gateway/Proxy: el gateway enruta parte del tráfico al nuevo microservicio y el resto sigue al monolito.
- Separar la base de datos: cada servicio debe tener su propia DB. Si el monolito tiene una DB compartida, la migración de datos es el paso más crítico.
- Introducir mensajería gradualmente: reemplazá llamadas directas internas por eventos asíncronos.
- Monitoreo desde el día 1: distributed tracing (OpenTelemetry), logs centralizados.
22. ¿Cómo decidís entre contenedores y serverless para hostear microservicios?
Contenedores (Docker + Kubernetes):
- Servicios que corren continuamente con tráfico constante.
- Necesitás control total sobre el runtime, puertos, recursos.
- Latencias bajas y predecibles (sin cold start).
- Servicios con estado o conexiones persistentes (WebSockets, gRPC streaming).
Serverless (Azure Functions, AWS Lambda):
- Tráfico muy esporádico o con picos muy variables.
- Funciones pequeñas y bien definidas (procesamiento de eventos, webhooks).
- Querés pagar solo por ejecución, no por tiempo idle.
- No querés gestionar infraestructura.
En la práctica, muchas arquitecturas usan ambos: servicios core en contenedores y tareas auxiliares en serverless.
23. ¿Cuáles son los trade-offs entre monorepo y multi-repo para microservicios?
Monorepo (un repositorio para todos los servicios):
- ✅ Refactoring atómico cross-service, visibilidad total del código, CI/CD simplificado para cambios coordinados.
- ❌ El repo crece mucho, los pipelines de CI pueden volverse lentos sin optimización (build incremental), los equipos pueden pisarse entre sí.
Multi-repo (un repo por servicio):
- ✅ Autonomía de equipos, pipelines independientes, encapsulación real.
- ❌ Coordinar cambios cross-servicio es más complejo, gestión de versiones de contratos/paquetes compartidos, más overhead operativo.
No hay una respuesta correcta universal. Amazon y Google usan monorepos. Netflix usa multi-repo. La clave es la madurez del equipo y las herramientas de CI/CD disponibles.
24. ¿Cómo usarías Domain-Driven Design en .NET para definir los límites de los servicios?
DDD es probablemente la herramienta más poderosa para definir microservicios bien acotados:
- Bounded Contexts: cada contexto delimitado se convierte en un microservicio candidato. Un Pedido en el contexto de Ventas tiene atributos distintos a un Pedido en el contexto de Logística.
- Ubiquitous Language: cada servicio tiene su propio vocabulario. Documentarlo en código (nombres de clases, métodos, eventos) evita la confusión.
- Aggregates: definen los límites de consistencia. Un aggregate se guarda entero en una transacción. Si dos aggregates necesitan actualizarse juntos frecuentemente, quizás deberían estar en el mismo servicio.
- Domain Events: los eventos entre bounded contexts definen las interfaces entre microservicios.
- Context Map: diagramá las relaciones entre bounded contexts (upstream/downstream, ACL, shared kernel) antes de empezar a codear.
En .NET, librerías como Ardalis.Specification, MediatR y el Clean Architecture template de Ardalis son buenos puntos de partida para estructurar el código siguiendo DDD.
Conclusión
Estas 24 preguntas no son trivia — reflejan problemas reales que aparecen cuando querés escalar una arquitectura de microservicios en producción. Conocer los patrones (Outbox, Saga, Circuit Breaker, Sidecar, ACL) y poder relacionarlos con implementaciones concretas en .NET es lo que separa a un candidato promedio de uno sólido.
¿Hay alguna de estas preguntas que te generó dudas? Dejala en los comentarios.