← Back

Breaking circular module dependencies

Spring ModulithKafkaJava

As the backend grew, an automated architecture test started flagging circular dependencies between domains that worked fine at runtime but broke the core promise of the system - that domains talk to each other through events, not direct calls.

I traced the tightest cycle to a synchronous cleanup step and moved it into an event listener that only runs after the triggering transaction commits, turning a two-way dependency into a one-way one.

A couple of deeper cycles needed a stronger fix: introducing interfaces that let an upstream domain define a contract for something it needs, without ever depending on the domain that provides it.

Along the way, I found the system was silently duplicating audit log entries because an event was being published to the message broker even though something in the same process already handled it.