Purpose – Executes daily batch jobs (Spring Batch) and guarantees exact‑once publication of domain events using the Transactional Outbox pattern for the OrderHub platform.
This service processes scheduled orders every midnight (KST) and emits ORDER_SCHEDULED events without losing messages, even in the face of crashes or retries.
-
Scheduled Order Collection – Persists snapshots of every order in
scheduled_orderstable (via Kafka listener). -
Nightly Processing – At
00:00(cron0 0 0 * * *) a Spring Batch job:- Reads yesterday’s
PENDINGscheduled orders (excluding cancelled). - Marks them as
PROCESSING. - Writes an
inventory-deductevent request to the Outbox table.
- Reads yesterday’s
-
Transactional Outbox –
OutboxEventrows are saved in the same DB transaction as batch writes, ensuring atomicity. A separate generic publisher (not shown) polls and pushes to Kafka.
| Layer | Technology |
|---|---|
| Runtime | Java 17, Spring Boot 3 |
| Batch | Spring Batch (chunk‑oriented) |
| Persistence | Spring Data JPA, MySQL 8 |
| Messaging | Apache Kafka (Outbox pattern) |
| Build | Gradle 8, Docker Compose |
graph TD
subgraph Nightly Job (00:00)
R[ItemReader: SELECT scheduled_orders<br/>where date=yesterday & status=PENDING]
P[ItemProcessor:<br/>order.markAsProcessed()]
W[ItemWriter:<br/>save orders + insert OutboxEvent]
end
R --> P --> W
- Reader – JPQL pulls yesterday’s orders using
BETWEEN&ProcessStatus.PENDINGfilter. - Processor – Sets
processStatus = PROCESSING(business marker). - Writer – Persists changes and builds an
InventoryDeductRequest, wrapped as anOutboxEventwitheventType = ORDER_SCHEDULED.
classDiagram
class OutboxEvent {
+Long id
+AggregateType aggregateType
+String aggregateId
+EventType eventType
+String payload
+EventStatus status
}
class ScheduledOrder {
+Long id
+Long originalOrderId
+Long storeId
+OrderStatus status
+ProcessStatus processStatus
+Instant scheduledAt
}
ScheduledOrder --> "*" ScheduledOrderItem
| Topic | Payload | Trigger |
|---|---|---|
order-created |
OrderEventRequest |
Order service |
order-updated |
OrderEventRequest |
Order service |
Outbox → inventory-deduct |
InventoryDeductRequest |
Scheduler batch job |
Outbox publisher polls rows where status = PENDING, sends to Kafka, then sets status = PUBLISHED + publishedAt.
git clone https://github.com/orderhub/scheduler-service.git
cd scheduler-service
# start infra
docker compose up -d mysql kafka zookeeper
# run application (incl. @Scheduled cron)
./gradlew bootRunCheck logs around midnight or change cron to */30 * * * * * for quick tests.
- Unit – JUnit 5 for domain & processor logic.
- Integration – Testcontainers for MySQL+Kafka, verifying outbox insertion and message consumption.
Built with Jib → pushed to registry → deployed via Helm chart. Cron schedule adjustable via SPRING_BATCH_CRON env.