DEV Community

Devanshu Biswas
Devanshu Biswas

Posted on

Day 17: Extracting the Inventory service from my Spring Boot monolith

For sixteen days OrderHub was one Spring Boot jar. One pom.xml, one src/, one process. It was validated, cached, rate-limited, wrapped in circuit breakers and retries, and made safe to retry with idempotency keys. It was a genuinely solid monolith — and that was the point. You earn the right to split by first having something worth splitting.

Today Phase 3 begins, and the monolith becomes a system. Day 17 is the first cut: extract the Inventory capability into its own service.

Why break up something that works?

A single deployable is wonderful until it isn't. Every change redeploys everything. The whole app scales as one unit even if only one corner is hot. One bad dependency or memory leak takes the lot down. And every team edits the same codebase.

Splitting into services buys independent deploys, independent scaling, fault isolation, and clear ownership — each service right-sized, carrying only the dependencies it actually needs. The honest cost: you trade an in-process method call for a network hop, and simple debugging for distributed complexity. So you don't split by default. You split along the cleanest, highest-value seam. For OrderHub, that's inventory.

Split along a bounded context, not along layers

The mistake would be to split by layer — all controllers here, all repositories there. Instead you split by bounded context: a slice of the domain with its own language and its own data. Orders speaks of customers, line items and order status. Inventory speaks of SKUs and how many units are on hand. Different models, so different services.

The rule that keeps it clean: no shared domain classes and no shared database. Inventory's StockItem owes nothing to order-service's Order. When the two contexts need to exchange information, they do it across an explicit API — never by reaching into each other's tables.

A monorepo with Maven modules

Separate services don't have to mean separate repos on day one. I turned the project into a Maven reactor: a root pom.xml with packaging=pom and a <modules> list that builds no jar itself but aggregates the children. Its parent is still spring-boot-starter-parent, so Boot's dependency management and the repackage plugin flow down to every module, and shared settings like the Java version are declared once and inherited.

order-hub-from-zero/
├─ pom.xml            reactor (aggregates + inherits)
├─ order-service/     the original app, :8080
└─ inventory-service/ NEW second Spring Boot app, :8081
Enter fullscreen mode Exit fullscreen mode

One clone, one mvn verify, versions locked in lockstep — the coordination win of a monolith with the deployability of services.

The second app

A microservice is a process, not a package. inventory-service gets its own @SpringBootApplication with a real main(), its own port (8081), and a deliberately leaner classpath: web, validation, actuator. No Redis, no Flyway, no Resilience4j — inventory doesn't use them. It exposes its own REST API (list stock, read a SKU, reserve units) with its own RFC-7807 error edge: unknown SKU → 404, insufficient stock → 409, bad quantity → 400. That API is the contract order-service will bind to next.

Each service owns its data privately. Inventory keeps stock behind a StockRepository port; today the adapter is a simple in-memory map (order-service already teaches the JPA + Postgres stack, so repeating it here would only add noise), and because the service depends on the interface, a real database is a drop-in swap later.

What's still ahead

The LOOK tab on the learning page simulates the piece that comes next: a service registry. Instances register by name, send heartbeats, and get evicted when their lease lapses; a caller looks up the name and gets a live instance — no hardcoded URLs. That's Eureka (Day 19), with OpenFeign for the actual service-to-service call on Day 18 and client-side load balancing on Day 22. Today, order-service still holds its in-process inventory stub; the wire between the two apps is the next few days' work.

Green the whole way

The reactor builds both modules and the tests stay honest: mvn test runs 38 tests — 27 in order-service, 11 in inventory-service (a @SpringBootTest proving the second app boots, a service unit test, and a @WebMvcTest pinning the HTTP contract). No behaviour changed in order-service; it just moved into a module.

Interactive walkthrough (LOOK / BACKEND / FRONTEND): https://dev48v.infy.uk/orderhub.php
Code: https://github.com/dev48v/order-hub-from-zero

Next: OpenFeign, so order-service can actually call inventory over HTTP.

Top comments (0)