μλΉ μ 보, μ½μ€ λ©λ΄, νμ₯ 체ν¬μΈ νλ¦μ κ΄λ¦¬νλ μλΉμ€μ
λλ€.
Miche-Let MSA νκ²½μμ λμνλ©°, Gateway / Eureka / PostgreSQL / Redis / Kafka / reservation-service μ ν¨κ» μ¬μ©λ©λλ€.
restaurant-serviceλ λ€μ 3κ°μ§ λλ©μΈμ λ΄λΉν©λλ€.
-
πͺ Restaurant
- μλΉ λ±λ‘ / λͺ©λ‘ μ‘°ν / κ²μ / μμΈ μ‘°ν
- μλΉ μ΄μ μ 보 κ΄λ¦¬
- μμ½ μ€ν μκ°, νκ· μμ¬ μκ°, μμ μκ°, μ΄μ μν κ΄λ¦¬
-
π½οΈ Course
- μλΉλ³ μ½μ€ λ±λ‘ / μ‘°ν
- μ½μ€ λ©λ΄ ννΈμ μ λ ¬ μμ κ΄λ¦¬
menus[]κΈ°λ°menuCompositionμλ μμ±- μΈλΆ μ¬μ©μμ© μ½μ€ λͺ©λ‘κ³Ό λ΄λΆ μλΉμ€μ© μ½μ€ μμ½ μ‘°ν λΆλ¦¬
-
β Check-in
- μ μ£Ό/κ΄λ¦¬μ 체ν¬μΈ μμ² μ²λ¦¬
- reservation-serviceμ Feign λκΈ° ν΅μ μΌλ‘ μμ½ μν κ²μ¦
- 체ν¬μΈ λ‘κ·Έ μ μ₯
- Kafka
reservation.checked-inμ΄λ²€νΈ consume reservationIdκΈ°μ€ μ€λ³΅ μ΄λ²€νΈ λ°©μ΄
src/main/java/com/michelet/restaurantservice
ββ restaurant
β ββ application
β β ββ command
β β ββ query
β β ββ result
β β ββ service
β ββ domain
β β ββ exception
β β ββ model
β β β ββ vo
β β ββ repository
β ββ infrastructure
β β ββ persistence
β β ββ query
β ββ presentation
β ββ controller.external
β ββ controller.internal
β ββ dto
ββ course
β ββ application
β β ββ command
β β ββ result
β β ββ service
β ββ domain
β β ββ exception
β β ββ model
β β ββ repository
β ββ infrastructure
β β ββ persistence
β ββ presentation
β ββ controller.external
β ββ controller.internal
β ββ dto
ββ checkin
β ββ application
β β ββ command
β β ββ result
β β ββ service
β ββ domain
β β ββ exception
β β ββ model
β β ββ repository
β ββ infrastructure
β β ββ client
β β ββ kafka
β β ββ persistence
β ββ presentation
β ββ controller.external
β ββ dto
ββ global
β ββ config
β ββ exception
ββ health
β ββ application
β ββ presentation
ββ RestaurantServiceApplication
- Java 17
- Spring Boot 3.5.14
- Spring Cloud 2025.0.1
- Spring Data JPA
- QueryDSL
- PostgreSQL
- Redis / Spring Cache
- Kafka
- OpenFeign
- Eureka Client
- Spring Boot Actuator
- Micrometer Prometheus
- Docker
- GitHub Actions
- AWS ECR / ECS
μλΉμ κΈ°λ³Έ μ 보λ₯Ό κ΄λ¦¬ν©λλ€.
- μλΉλͺ
- μ£Όμ
- μ νλ²νΈ
- μ€λͺ
- μμ½ μ€ν μκ°
- νκ· μμ¬ μκ°
- μ΄μ μν
- μμ μκ°
- μ μ£Ό ID
- μλΉ λ±λ‘
- μλΉ λͺ©λ‘/κ²μ μ‘°ν
- μλΉ μμΈ μ‘°ν
- λ΄λΆ μλΉμ€μ© μλΉ λ¨κ±΄ μ‘°ν
- μ μ£Ό ID κΈ°μ€ μλΉ ID μ‘°ν
OPEN
CLOSED
μλΉλ³ μ½μ€μ μ½μ€ λ©λ΄ ꡬμ±μ κ΄λ¦¬ν©λλ€.
- μ½μ€λͺ
- κ°κ²©
- μΈμ νμ
- νλ§€ μν
- λ©λ΄ κ΅¬μ± λ¬Έμμ΄
- ꡬ쑰νλ μ½μ€ λ©λ΄ λͺ©λ‘
- μ½μ€ λ±λ‘
- μΈλΆ μ¬μ©μμ© μ½μ€ λͺ©λ‘ μ‘°ν
- λ΄λΆ μλΉμ€μ© μ½μ€ μμ½ λͺ©λ‘ μ‘°ν
menus[]κΈ°λ°menuCompositionμμ±
LUNCH
DINNER
AVAILABLE
UNAVAILABLE
AMUSE_BOUCHE
APPETIZER
FISH
MAIN
DESSERT
- ν΄λΌμ΄μΈνΈλ
menuCompositionμ μ§μ μ λ¬νμ§ μμ΅λλ€. - μλ²κ°
menus[]μcoursePart,menuName,sortOrderλ₯Ό κΈ°μ€μΌλ‘menuCompositionμ μμ±ν©λλ€. - μ½μ€ λ©λ΄λ
course_id + sort_orderμ‘°ν©μ΄ μ μΌν΄μΌ ν©λλ€.
μ μ£Όμ νμ₯ 체ν¬μΈ μμ²μ μ²λ¦¬ν©λλ€.
OWNER / MASTER 체ν¬μΈ μμ²
β
restaurant-service
β Feign
reservation-service μμ½ κ²μ¦ λ° μν λ³κ²½
β
restaurant-service check-in log μ μ₯
체ν¬μΈ κ°λ₯ μ¬λΆλ reservation-serviceκ° νλ¨ν©λλ€.
- μμ½ μ‘΄μ¬ μ¬λΆ
- μμ½ μλΉ μΌμΉ μ¬λΆ
- μμ½ μν
- 체ν¬μΈ κ°λ₯ μκ°
restaurant-serviceλ μλΉ μ‘΄μ¬ μ¬λΆμ μ μ£Ό κΆνμ νμΈνκ³ , μμ½ μν λ³κ²½μ reservation-serviceμ μμν©λλ€.
p_restaurant_checkin_logμ 체ν¬μΈ μ΄λ ₯μ μ μ₯ν©λλ€.
- restaurantId
- reservationId
- visitDate
- status
- checkedInBy
- checkedInAt
- μ ν리μΌμ΄μ
λ 벨:
reservationIdκΈ°μ€ κΈ°μ‘΄ 체ν¬μΈ λ‘κ·Έ νμΈ - DB λ 벨:
reservation_idunique constraint
λ°λ³΅ μ‘°ν κ°λ₯μ±μ΄ λμ APIμ Redis Cache-Aside ν¨ν΄μ μ μ©ν©λλ€.
| Cache Name | API | Key |
|---|---|---|
restaurantDetail |
GET /api/v1/restaurants/{restaurantId} |
restaurant:detail:{restaurantId} |
restaurantCourses |
GET /api/v1/restaurants/{restaurantId}/courses |
restaurant:courses:{restaurantId} |
μ‘°ν μμ²
β
Redis μΊμ νμΈ
β
cache hit β DB μ‘°ν μμ΄ λ°ν
cache miss β DB μ‘°ν ν Redis μ μ₯
cache:
ttl:
restaurant-detail: ${CACHE_TTL_RESTAURANT_DETAIL:300}
restaurant-courses: ${CACHE_TTL_RESTAURANT_COURSES:300}GenericJackson2JsonRedisSerializerλ₯Ό μ¬μ©ν©λλ€.
record DTO μμ§λ ¬ν λ¬Έμ λ₯Ό λ°©μ§νκΈ° μν΄ ObjectMapperμ νμ
μ 보λ₯Ό ν¬ν¨νλλ‘ μ€μ νκ³ , BasicPolymorphicTypeValidatorλ‘ νμ© ν¨ν€μ§λ₯Ό μ νν©λλ€.
reservation-serviceμμ λ°νν 체ν¬μΈ μλ£ μ΄λ²€νΈλ₯Ό consumeν©λλ€.
reservation.checked-in
restaurant-service-check-in-consumer
{
"eventId": "uuid",
"eventType": "CHECK_IN_COMPLETED",
"reservationId": "uuid",
"restaurantId": "uuid",
"visitDate": "yyyy-MM-dd",
"checkedInBy": "uuid",
"checkedInAt": "yyyy-MM-dd'T'HH:mm:ss",
"eventCreatedAt": "yyyy-MM-dd'T'HH:mm:ss"
}- μ΄λ²€νΈ νμ
μ΄
CHECK_IN_COMPLETEDμΈμ§ κ²μ¦ - νμ νλ λλ½ μ¬λΆ κ²μ¦
reservationIdκΈ°μ€ μ²΄ν¬μΈ λ‘κ·Έ μ‘΄μ¬ μ¬λΆ νμΈ- μ΄λ―Έ μ‘΄μ¬νλ©΄ μ€λ³΅ μ΄λ²€νΈλ‘ νλ¨νκ³ skip
- μ‘΄μ¬νμ§ μμΌλ©΄ μλΉ μ‘΄μ¬ μ¬λΆ νμΈ ν check-in log μ μ₯
- λμ μ²λ¦¬ μ€ unique constraint μΆ©λμ΄ λ°μνλ©΄ μ€λ³΅ μ΄λ²€νΈλ‘ νλ¨νκ³ skip
체ν¬μΈ μμ² μ reservation-service λ΄λΆ APIλ₯Ό νΈμΆν©λλ€.
PATCH /internal/reservations/check-in
- reservationId
- restaurantId
- checkedInBy
- X-User-Id
- X-User-Role
| reservation-service μλ΅ | restaurant-service μ²λ¦¬ |
|---|---|
| 404 | μμ½ μμ |
| 400 | μμ½ μ²΄ν¬μΈ μ²λ¦¬ μ€ν¨ |
| 409 | μμ½ μ²΄ν¬μΈ μΆ©λ |
| κΈ°ν Feign μ€λ₯ | μλͺ»λ reservation-service μλ΅ |
μΈλΆ APIλ Gatewayλ₯Ό ν΅ν΄ μ§μ νλ©°, μΈμ¦ 컨ν μ€νΈλ common-auth λͺ¨λμ ν΅ν΄ μ λ¬λ©λλ€.
USEROWNERMASTER
| κΈ°λ₯ | νμ© Role |
|---|---|
| μλΉ λ±λ‘ | OWNER |
| μλΉ λͺ©λ‘/κ²μ μ‘°ν | USER, OWNER, MASTER |
| μλΉ μμΈ μ‘°ν | USER, OWNER, MASTER |
| μ½μ€ λ±λ‘ | OWNER |
| μ½μ€ λͺ©λ‘ μ‘°ν | USER, OWNER |
| 체ν¬μΈ μ²λ¦¬ | OWNER, MASTER |
Base Path: /api/v1/restaurants
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/restaurants |
μλΉ λ±λ‘ |
| GET | /api/v1/restaurants |
μλΉ λͺ©λ‘/κ²μ μ‘°ν |
| GET | /api/v1/restaurants/{restaurantId} |
μλΉ μμΈ μ‘°ν |
| GET | /api/v1/restaurants/health |
restaurant-service health νμΈ |
| Parameter | Required | Description |
|---|---|---|
keyword |
false | μλΉλͺ κ²μμ΄ |
region |
false | μ£Όμ κΈ°λ° μ§μ κ²μμ΄ |
status |
false | μλΉ μν νν° (OPEN, CLOSED) |
page |
false | νμ΄μ§ λ²νΈ |
size |
false | νμ΄μ§ ν¬κΈ° |
sort |
false | μ λ ¬ κΈ°μ€ |
Base Path: /api/v1/restaurants/{restaurantId}/courses
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/restaurants/{restaurantId}/courses |
μ½μ€ λ±λ‘ |
| GET | /api/v1/restaurants/{restaurantId}/courses |
μΈλΆ μ¬μ©μμ© μ½μ€ λͺ©λ‘ μ‘°ν |
Base Path: /api/v1/restaurants
| Method | Path | Description |
|---|---|---|
| PATCH | /api/v1/restaurants/{restaurantId}/reservations/{reservationId}/check-in |
μμ½ μ²΄ν¬μΈ μ²λ¦¬ |
Base Path: /internal/v1/restaurants
| Method | Path | Description |
|---|---|---|
| GET | /internal/v1/restaurants/{restaurantId} |
λ΄λΆ μλΉ λ¨κ±΄ μ‘°ν |
| GET | /internal/v1/restaurants/owners/{ownerId}/id |
μ μ£Ό ID κΈ°μ€ μλΉ ID μ‘°ν |
Base Path: /internal/v1/restaurants/{restaurantId}/courses
| Method | Path | Description |
|---|---|---|
| GET | /internal/v1/restaurants/{restaurantId}/courses |
λ΄λΆ μ½μ€ μμ½ λͺ©λ‘ μ‘°ν |
{
"name": "MicheLet Dining",
"address": "μμΈνΉλ³μ κ°λ¨κ΅¬ ν
ν€λλ‘ 123",
"phone": "02-1234-5678",
"description": "νμΈλ€μ΄λ λ μ€ν λ",
"reservationOpenAt": "10:00:00",
"avgMealDurationMin": 90,
"status": "OPEN",
"businessHours": "MON-FRI 11:00-20:00 / SAT,SUN CLOSED"
}{
"name": "Dinner Course",
"price": 150000,
"sessionType": "DINNER",
"status": "AVAILABLE",
"menus": [
{
"coursePart": "AMUSE_BOUCHE",
"menuName": "νμ° νλ₯΄νλ₯΄",
"sortOrder": 1
},
{
"coursePart": "APPETIZER",
"menuName": "μ μ² μλ¬λ",
"sortOrder": 2
},
{
"coursePart": "FISH",
"menuName": "μ μ² μμ ꡬμ΄",
"sortOrder": 3
},
{
"coursePart": "MAIN",
"menuName": "μκ°λΉ",
"sortOrder": 4
},
{
"coursePart": "DESSERT",
"menuName": "λ°λλΌ λ¬΄μ€",
"sortOrder": 5
}
]
}ν μ€νΈλ μλ μμμ ν¬ν¨ν©λλ€.
- β Restaurant Query Service Test
- β Restaurant Controller Test
- β Restaurant Internal Controller Test
- β Course Command Service Test
- β Course Query Service Test
- β Course Controller Test
- β Course Internal Controller Test
- β CheckInCompletedEventHandler Test
- β CheckInCompletedEventConsumer Test
- β Redis Cache Test
- β QueryDSL Repository Test
./gradlew clean testHTTP Client κΈ°λ° MVP / λ°°ν¬νκ²½ ν μ€νΈ νμΌμ μ 곡ν©λλ€.
src/test/http
ββ checkin-event-outbox-run.http
ββ checkin-event-outbox-setup.http
ββ restaurant-cache.http
ββ restaurant-internal.http
src/test/mvp
ββ owner.http
ββ user.http
ββ owner-checkin.http
ββ prod-restaurant-api-scenario.http
ββ prod-full-checkin-e2e.http
ββ sql
ββ seed_mvp_data.sql
ββ reset_mvp_data.sql
- OWNER νμκ°μ / λ‘κ·ΈμΈ
- μλΉ λ±λ‘
- μ½μ€ λ±λ‘
- USER νμκ°μ / λ‘κ·ΈμΈ
- μλΉ λͺ©λ‘ μ‘°ν
- μλΉ μμΈ μ‘°ν
- μ½μ€ λͺ©λ‘ μ‘°ν
- νμμ¬λ‘― μμ± / μ‘°ν
- λκΈ°μ΄ μ μ₯
- μμ½ μμ±
- 체ν¬μΈ μ²λ¦¬
- μ€λ³΅ 체ν¬μΈ νμΈ
체ν¬μΈμ μμ½ μμ μκ° κΈ°μ€ μ νλ μκ° μμμλ§ κ°λ₯ν©λλ€.
λ°°ν¬νκ²½ ν
μ€νΈμμλ νμ¬ νκ΅ μκ° κΈ°μ€μΌλ‘ slotStartTimeμ μ€μ νκ³ , νμμ¬λ‘― μ‘°ν κ²°κ³Όμμ μ νν μΌμΉνλ timeSlotIdλ₯Ό μ¬μ©ν΄μΌ ν©λλ€.
k6 κΈ°λ° μ‘°ν API λΆνν μ€νΈ μ€ν¬λ¦½νΈλ₯Ό μ 곡ν©λλ€.
load-tests/restaurant
ββ restaurant-read-as-is.js
ββ restaurant-read-to-be-cache.js
- Redis μΊμ μ μ© μ /ν μ‘°ν μ±λ₯ λΉκ΅
- λ°λ³΅ μ‘°ν μ DB select κ°μ μ¬λΆ νμΈ
- cache hit/miss μ§ν νμΈ
- p95 / p99 μλ΅ μκ° νμΈ
Actuatorμ Prometheus endpointλ₯Ό λ ΈμΆν©λλ€.
/actuator/health
/actuator/metrics
/actuator/prometheus
Prometheus / Grafanaμμ μλ μ§νλ₯Ό νμΈν μ μμ΅λλ€.
- HTTP request count
- JVM heap / non-heap memory
- CPU
- HikariCP connection pool
- Redis cache hit/miss
- Kafka consumer μν
μλ μλΉμ€κ° λ¨Όμ μ€νλμ΄ μμ΄μΌ ν©λλ€.
- PostgreSQL
- Redis
- Kafka
- Eureka Server μ ν
- API Gateway μ ν
- reservation-service μ ν
체ν¬μΈ APIλ₯Ό λ‘컬μμ μ€μ λ‘ κ²μ¦νλ €λ©΄ reservation-serviceκ° νμν©λλ€.
.env.example κΈ°μ€μΌλ‘ νμν κ°μ
λλ€.
SPRING_PROFILES_ACTIVE=local
SERVER_PORT=19300
DB_URL=jdbc:postgresql://localhost:5432/michelet_db
DB_USERNAME=your_db_username
DB_PASSWORD=your_db_password
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=michelet_db
POSTGRES_USER=your_db_username
POSTGRES_PASSWORD=your_db_password
REDIS_HOST=localhost
REDIS_PORT=6379
EUREKA_CLIENT_ENABLED=false
EUREKA_DEFAULT_ZONE=http://localhost:8761/eureka/
EUREKA_ENABLED=false
EUREKA_HOST=localhost
EUREKA_PORT=8761
FEIGN_RESERVATION_SERVICE_URL=http://localhost:19500
INTERNAL_AUTH_SECRET=your_internal_auth_secret_at_least_32_bytes
JWT_SECRET=your_jwt_secret_at_least_32_bytes
KAFKA_BOOTSTRAP_SERVERS=localhost:9092
KAFKA_CONSUMER_GROUP_ID=restaurant-service-check-in-consumer
KAFKA_TOPIC_RESERVATION_CHECK_IN_COMPLETED=reservation.checked-in
KAFKA_LISTENER_AUTO_STARTUP=true
CACHE_TTL_RESTAURANT_DETAIL=300
CACHE_TTL_RESTAURANT_COURSES=300| λ³μλͺ | μ€λͺ |
|---|---|
SPRING_PROFILES_ACTIVE |
νμ± profile (local, docker, prod) |
SERVER_PORT |
restaurant-service μ€ν ν¬νΈ |
DB_URL |
local/docker profile DB URL |
DB_USERNAME |
local/docker profile DB μ¬μ©μλͺ |
DB_PASSWORD |
local/docker profile DB λΉλ°λ²νΈ |
POSTGRES_HOST |
prod profile PostgreSQL host |
POSTGRES_PORT |
prod profile PostgreSQL port |
POSTGRES_DB |
prod profile PostgreSQL database |
POSTGRES_USER |
prod profile PostgreSQL μ¬μ©μλͺ |
POSTGRES_PASSWORD |
prod profile PostgreSQL λΉλ°λ²νΈ |
REDIS_HOST |
Redis host |
REDIS_PORT |
Redis port |
EUREKA_CLIENT_ENABLED |
local/docker profile Eureka client νμ±ν μ¬λΆ |
EUREKA_DEFAULT_ZONE |
local/docker profile Eureka server URL |
EUREKA_ENABLED |
prod profile Eureka client νμ±ν μ¬λΆ |
EUREKA_HOST |
prod profile Eureka host |
EUREKA_PORT |
prod profile Eureka port |
FEIGN_RESERVATION_SERVICE_URL |
reservation-service μ§μ νΈμΆ URL. λΉμλλ©΄ Eureka name κΈ°λ° νΈμΆ |
INTERNAL_AUTH_SECRET |
λ΄λΆ API μΈμ¦ secret |
JWT_SECRET |
JWT secret |
KAFKA_BOOTSTRAP_SERVERS |
Kafka bootstrap servers |
KAFKA_CONSUMER_GROUP_ID |
Kafka consumer group id |
KAFKA_TOPIC_RESERVATION_CHECK_IN_COMPLETED |
체ν¬μΈ μλ£ μ΄λ²€νΈ topic |
KAFKA_LISTENER_AUTO_STARTUP |
Kafka listener μλ μμ μ¬λΆ |
CACHE_TTL_RESTAURANT_DETAIL |
μλΉ μμΈ μΊμ TTL seconds |
CACHE_TTL_RESTAURANT_COURSES |
μ½μ€ λͺ©λ‘ μΊμ TTL seconds |
- μλΉμ€λͺ
:
restaurant-service - κΈ°λ³Έ server port:
19300 - Redis cache μ¬μ©
- Kafka consumer κΈ°λ³Έ μ€μ
- Actuator / Prometheus endpoint λ ΈμΆ
- local profile DB μ°κ²°
- Redis host κΈ°λ³Έκ°:
localhost - JPA ddl-auto:
update - SQL λ‘κ·Έ μΆλ ₯
- Eureka client κΈ°λ³Έ λΉνμ±ν
- prod profile DB URLμ
POSTGRES_HOST,POSTGRES_PORT,POSTGRES_DBλ‘ μ‘°ν© - HikariCP pool size νκ²½λ³μ κΈ°λ° μ€μ
- Redis / Kafka / Eureka / Feign / Cache TTL νκ²½λ³μ κΈ°λ° μ€μ
- Prometheus metrics λ ΈμΆ
./gradlew bootRunSPRING_PROFILES_ACTIVE=local ./gradlew bootRunλλ IntelliJμμ RestaurantServiceApplicationμ μ€νν©λλ€.
docker build -t restaurant-service:local .docker run --rm -p 19300:19300 \
--env-file .env.example \
restaurant-service:localDockerfileμ multi-stage buildλ₯Ό μ¬μ©ν©λλ€.
Gradle JDK 17 builder stage
β
bootJar μμ±
β
Eclipse Temurin JRE 17 runtime stage
β
app.jar μ€ν
GitHub Actions κΈ°λ° CI/CDλ₯Ό μ 곡ν©λλ€.
Pull Request / Push
β
JDK 17 μ€μ
β
Gradle test
β
Docker image build
β
ECR push
- μ΄λ―Έμ§ νκ·Έλ commit SHA μ 7μ리λ₯Ό μ¬μ©ν©λλ€.
- AWS μΈμ¦μ OIDC Role κΈ°λ°μΌλ‘ μ²λ¦¬ν©λλ€.
CI-PROD μ±κ³΅
β
ECR image μ‘΄μ¬ νμΈ
β
νμ¬ ECS Task Definition μ‘°ν
β
μ Task Definition revision μμ±
β
ECS Service update
β
services-stable λκΈ°
λ°°ν¬ μ container environmentμ DB, Redis, Kafka, Eureka, cache TTL κ°μ μ£Όμ ν©λλ€.
restaurant-serviceλ μλΉ/μ½μ€/체ν¬μΈ λ‘κ·Έλ₯Ό μμ ν©λλ€.
μμ½ μνμ source of truthλ reservation-serviceκ° μμ ν©λλ€.
체ν¬μΈ μμ²μ restaurant-serviceμμ λ°μ§λ§, μμ½ μν κ²μ¦κ³Ό μν λ³κ²½μ reservation-serviceμ μμν©λλ€.
μ¦μ μλ΅μ΄ νμν 체ν¬μΈ κ²μ¦μ Feign λκΈ° ν΅μ μΌλ‘ μ²λ¦¬ν©λλ€.
체ν¬μΈ μλ£ μ΄λ²€νΈ κΈ°λ° λ‘κ·Έ μ μ₯μ Kafka λΉλκΈ° consumeμΌλ‘ μ²λ¦¬ν©λλ€.
μ¦μ κ²μ¦: Feign
μ΄λ²€νΈ μ²λ¦¬: Kafka
μ€λ³΅ λ°©μ΄: reservationId κΈ°μ€ idempotency
μλΉ μμΈ / μ½μ€ λͺ©λ‘ μ‘°νλ λ°λ³΅ μ‘°ν κ°λ₯μ±μ΄ λκΈ° λλ¬Έμ Redis Cache-Aside ν¨ν΄μ μ μ©ν©λλ€.
- cache hit: DB μ κ·Ό μμ΄ μλ΅
- cache miss: DB μ‘°ν ν Redis μ μ₯
record DTO μμ§λ ¬ν μ΄μλ₯Ό λ°©μ§νκΈ° μν΄ Redis value serializerμ νμ μ 보 μ€μ μ λͺ μν©λλ€.
κ³΅ν΅ BaseEntityλ₯Ό μ¬μ©νλ©°, μ£Όμ μ‘°νμμλ μμ λμ§ μμ λ°μ΄ν° κΈ°μ€μΌλ‘ μ²λ¦¬ν©λλ€.
νλμ μμ½μ λν΄ μ²΄ν¬μΈ λ‘κ·Έλ ν λ²λ§ μ μ₯λμ΄μΌ ν©λλ€.
uk_checkin_log_reservation_id
Kafka μ¬μ λ¬μ΄λ λμ consume μν©μμλ reservationId κΈ°μ€μΌλ‘ μ€λ³΅ μ μ₯μ λ°©μ΄ν©λλ€.
.
ββ build.gradle
ββ settings.gradle
ββ Dockerfile
ββ .env.example
ββ load-tests
β ββ restaurant
ββ src
β ββ main
β β ββ java
β β ββ resources
β ββ test
β ββ http
β ββ java
β ββ mvp
β ββ resources
ββ gradle
μ΄ μλΉμ€λ λ€μμ μ± μμ§λλ€.
- πͺ μλΉ κΈ°λ³Έ μ 보 κ΄λ¦¬
- π½οΈ μλΉ μ½μ€ λ° μ½μ€ λ©λ΄ κ΄λ¦¬
- π μ¬μ©μμ© μλΉ/μ½μ€ μ‘°ν
- π λ΄λΆ μλΉμ€μ© μλΉ/μ½μ€ μ‘°ν
- β μμ½ μ°λ 체ν¬μΈ μ²λ¦¬
- π© Kafka 체ν¬μΈ μλ£ μ΄λ²€νΈ consume
- π§±
reservationIdκΈ°μ€ μ²΄ν¬μΈ λ‘κ·Έ μ€λ³΅ λ°©μ΄ - π Redis Cache-Aside κΈ°λ° μ‘°ν μ±λ₯ κ°μ
- π Prometheus/Grafana κΈ°λ° λͺ¨λν°λ§ μ§ν λ ΈμΆ
- π§ͺ HTTP Client κΈ°λ° λ°°ν¬νκ²½ API μλλ¦¬μ€ κ²μ¦