Ace8 is the modernization practice of AceMQ.24/7 support from AceMQ after go-live
Book an Assessment

Guide

Modular monolith: one deployable, hard boundaries.

For many Java teams it is the right stopping point on the road from a big-ball-of-mud monolith. Here is how to build the boundaries so they hold.

AceMQ engineering team4 min readEngineer-reviewed

The short answer

A modular monolith is a single deployable application split into modules that own their data and expose defined interfaces. Boundaries are enforced by tooling, not by good intentions. It keeps one release and one database transaction model, so it beats microservices when the team is small or the domain is still moving.

01Definition

What makes a monolith modular?

A big monolith and a modular one can have the same deployment shape. The difference is what the code is allowed to do. In a modular monolith each module has a public surface, private internals and its own tables. Another module cannot reach in.

That is the same discipline monolith to microservices work needs first, without the network between the parts. Many teams stop here and never need the next step. Others use it as the safe half of a split.

02Boundary rules

Five rules that make a boundary real

If a rule is not checked in the build, it will be broken within a quarter. Write these down, then automate each one.

  • Public API per module. One package or interface that other modules may call. Everything else is private.
  • Data ownership. A module reads and writes its own tables. No joins across module tables, no shared entities.
  • One-way dependencies. Module A may depend on B, or B on A, never both. Cycles mean the boundary is in the wrong place.
  • Events for side effects. When module A needs to tell B something happened, publish an event instead of calling B directly.
  • Tests per module. Each module can start and be tested without loading the whole application.

Planning this for your estate? Get a fixed scope and price first.

03Tooling

How does Spring Modulith enforce the boundaries?

Spring Modulith is a Spring project for building modular applications on Spring Boot 3. By convention, each direct sub-package of your application's main package is a module. Types in that top-level package are the module's public API. Types in nested packages are internal.

From there it gives you four checks you can run in CI:

  • A verification call, ApplicationModules.of(Application.class).verify(), which fails the build on a cyclic dependency or on code that reaches into another module's internal package.
  • Explicit dependency lists with @ApplicationModule(allowedDependencies = ...), so a module can use just the modules you named.
  • Module-scoped integration tests with @ApplicationModuleTest, which load one module and its declared dependencies.
  • Generated module diagrams and documentation from the same model, so the picture cannot drift from the code.

04Decision rule

Modular monolith or microservices?

Pick by constraint, not by fashion. The table lists the constraint that should push you one way or the other.

ConstraintStay modularSplit to services
Team sizeOne team or a few, who talk dailyMany teams that need to ship independently
Release cadenceOne cadence is fineA module needs to release many times a day while others do not
ScalingLoad grows evenlyOne module needs a very different scale or hardware
Data consistencyYou need ACID across modulesYou can accept eventual consistency and sagas
Domain clarityBoundaries are still shiftingBoundaries have held for several releases
Operations skillNo platform team yetPlatform team can run many services

05Next step

When should a module leave the monolith?

06Getting there

How do you find module boundaries in an existing monolith?

  1. 01

    List every database table and note which code reads and writes it. Tables written by one area of code hint at a module. Tables written by many hint at a hidden coupling.

  2. 02

    Group the code by business capability, such as billing or catalog, not by technical layer. A controller-service-repository folder tree is not a module structure.

  3. 03

    Draw the dependency graph from the real imports. Count the cycles. Fewer cycles means a cleaner cut.

  4. 04

    Pick the module with the fewest inbound dependencies and move it behind an interface first.

  5. 05

    Turn on the boundary check in CI for that one module. Expand one module at a time.

07How Ace8 helps

How Ace8 helps with the boundary work

Ace8 is the AI-assisted application modernization practice of AceMQ. We map table ownership and dependency cycles from your code, propose module boundaries, and have engineers review every move. AI-assisted tools handle the mechanical refactoring.

If you are deciding between staying modular and splitting, start with the modernization assessment. It scores each capability against the table above and gives you an order.

FAQ

Frequently asked.

Something else on your mind? Ask an engineer.

A single deployable application divided into modules with public interfaces, private internals and separate data ownership, where boundaries are enforced by the build.

Start here

Know the cost before anything moves.

A fixed-price assessment gives you the inventory, the risk score and a phased plan. Scope and cost are fixed before the migration starts.

Get in touch

Talk to a modernization engineer

Tell us what you run today. An engineer, not a sales script, replies within one business day.

305-204-2607info@ace8.io

66 W. Flagler St. 9th Floor
Miami, FL 33130

What are you moving off? *

Fixed price. Scope and cost before any migration starts.