Servicio HTTP de ejemplo con dos capacidades: genera números aleatorios y crea animales
aleatorios que persisten en MongoDB. En el par de demos simula una API externa que consume la
aplicación de microservicios: el microservicio fibonacci usa /random para construir su secuencia.
- Qué es
- Arquitectura
- Tech Stack
- Getting Started
- API
- Errores
- MongoDB
- Testing
- Docker
- Relación con DemoMicroservices
- License
DemoService no forma parte de la arquitectura de microservicios. Se levanta aparte y se comporta como
un servicio de terceros: expone una API pequeña y estable que la aplicación DemoMicroservices consume
por HTTP. Además, la parte de animales muestra persistencia real con MongoDB.
La estructura está separada por capas: domain contiene el modelo, los puertos y las reglas, con la
entidad en domain/entity y los value objects en domain/vo; application los casos de uso y sus
resultados en application/dto; e infrastructure los controladores, los DTOs HTTP en
infrastructure/controllers/dto y los adaptadores. Los controladores dependen de los casos de uso de
application, no del dominio.
| Capa | Contenido |
|---|---|
domain/entity |
Animal (identidad UUID) |
domain/vo |
ValueRandom, AnimalCatalog |
domain/animal, domain/random |
Puertos, servicios de dominio y InvalidArgumentException en domain/exception |
application/animal, application/random |
Casos de uso y servicios de aplicación |
application/dto |
AnimalResult, RandomNumberResult |
infrastructure/controllers |
Controladores y ApiExceptionHandler |
infrastructure/controllers/dto |
AnimalResponse, RandomNumberResponse |
infrastructure/persistence |
AnimalDocument, MongoAnimalRepository |
infrastructure/random |
RandomNumberGeneratorDefault |
infrastructure/config |
BeanConfiguration |
com.cedaniel200.service
├── domain/ # núcleo: no depende de application ni de infrastructure
│ ├── entity/ # Animal: entidad con id UUID
│ ├── vo/ # ValueRandom y AnimalCatalog
│ ├── animal/ # puertos y servicios de animales
│ ├── random/ # puerto del generador aleatorio
│ └── exception/ # InvalidArgumentException
├── application/ # casos de uso: solo depende de domain
│ ├── animal/ # CreateAnimalUseCase, GetAnimalsUseCase y su servicio
│ ├── random/ # GetRandomNumberUseCase, servicio e InvalidBoundException
│ └── dto/ # AnimalResult y RandomNumberResult
└── infrastructure/ # adaptadores: depende de application y domain
├── config/ # BeanConfiguration (composition root)
├── controllers/ # AnimalController, RandomController y ApiExceptionHandler
│ └── dto/ # AnimalResponse y RandomNumberResponse
├── persistence/ # AnimalDocument y MongoAnimalRepository
└── random/ # RandomNumberGeneratorDefault
Las reglas se verifican automáticamente con ArchUnit en ArchitectureTest.
La dirección de dependencias es:
infrastructure ──> application ──> domain
La identidad de Animal se genera en el dominio con UUID; el adaptador de Mongo la usa como _id.
Los controladores no importan clases del dominio: reciben resultados de la aplicación y los convierten en DTOs HTTP de infraestructura.
| Tecnología | Versión |
|---|---|
| Java | 21 |
| Spring Boot | 4.0.9 |
| Gradle | 9.6.1 (wrapper incluido) |
| Spring Web | gestionada por Spring Boot |
| Spring Data MongoDB | gestionada por Spring Boot |
| MongoDB | 7.0 (contenedor) |
| JUnit 5 + Testcontainers | gestionadas por Spring Boot |
| ArchUnit | 1.5.1 (reglas de arquitectura) |
| Docker | imagen multi-stage |
- Java 21
- Docker y Docker Compose (para MongoDB y la imagen)
./gradlew build
./gradlew bootRunEl servicio queda en http://localhost:9000. /random funciona sin MongoDB; /animal y /animals
necesitan una instancia accesible en mongodb://localhost:27017/demo o la URI que indiques en
MONGODB_URI.
La forma más sencilla de tener todo levantado es Docker Compose:
docker compose up --buildDevuelve un entero entre 0 y bound, ambos inclusive.
curl "http://localhost:9000/random?bound=10"{"value": 7}| Caso | Respuesta |
|---|---|
bound válido |
200 con {"value": N} |
bound negativo |
400 Bad Request |
bound ausente |
400 Bad Request |
Crea un animal aleatorio y lo guarda en MongoDB.
curl -X POST "http://localhost:9000/animal"{"id": "0f8c2a7e-4b1d-4c3a-9e2f-1a2b3c4d5e6f", "type": "Perro", "name": "Luis"}Responde 201 Created.
Devuelve los animales guardados.
curl "http://localhost:9000/animals"[
{"id": "0f8c2a7e-4b1d-4c3a-9e2f-1a2b3c4d5e6f", "type": "Perro", "name": "Luis"},
{"id": "7e3b1d9c-2a4f-4e8b-8c1d-3f5a6b7c8d9e", "type": "Gato", "name": "Lola"}
]Los errores se devuelven con el formato estándar application/problem+json de Spring (ProblemDetail).
El @RestControllerAdvice centraliza la traducción de excepciones a códigos HTTP:
| Situación | Estado | detail |
|---|---|---|
bound negativo |
400 |
bound debe ser mayor o igual que 0 |
bound ausente |
400 |
Falta el parámetro obligatorio: bound |
Valor inválido en un VO (Animal, ValueRandom, AnimalCatalog) |
400 |
Mensaje de validación |
| MongoDB no disponible | 503 |
No se pudo acceder a la base de datos |
Ejemplo:
{
"type": "about:blank",
"title": "Solicitud inválida",
"status": 400,
"detail": "bound debe ser mayor o igual que 0",
"instance": "/random"
}La conexión se configura con spring.mongodb.uri. Spring Boot toma el valor de la variable estándar
SPRING_MONGODB_URI:
spring.mongodb.uri=mongodb://localhost:27017/demoCon Docker Compose, el servicio apunta a mongodb://mongo:27017/demo dentro de la red de Compose. No
hay autenticación: es una configuración de demo local.
./gradlew testSon 51 tests: VOs, entidad, catálogo, generador, casos de uso, los tres endpoints con @WebMvcTest,
la integración del repositorio con Testcontainers y 7 reglas de arquitectura con ArchUnit. El
test de integración necesita Docker.
docker compose up --buildLevanta mongo y demo-service, espera a que Mongo esté sano y publica el servicio en el puerto
9000. La imagen se construye en dos etapas y el proceso corre con el usuario appuser (UID 1001), no
como root.
Para construir y ejecutar solo el servicio:
docker build -t demo-service .
docker run --rm -p 9000:9000 -e SPRING_MONGODB_URI=mongodb://host.docker.internal:27017/demo demo-serviceDemoService simula un servicio externo a la aplicación de microservicios:
DemoMicroservices
└── fibonacci-microservice ──HTTP──> DemoService (/random?bound=N)
El microservicio fibonacci obtiene un número de DemoService y lo usa como punto de partida de su
secuencia. Los endpoints de animales son una capacidad independiente del par y necesitan MongoDB; el
flujo de microservicios solo usa /random.