Designing a Router Framework for Decoupling Modules in Large‑Scale Projects

The article analyzes the challenges of tightly coupled modules in rapidly growing projects, proposes a Router model that encapsulates and distributes inter‑module calls, discusses its core concepts, implementation options, and extensions such as interceptors, and shares practical lessons learned after two years of use.

Tongcheng Travel Technology Center
Tongcheng Travel Technology Center
Tongcheng Travel Technology Center
Designing a Router Framework for Decoupling Modules in Large‑Scale Projects

Background

As the business entered a rapid growth phase, the project structure became large and complex, causing iteration costs and compile times to increase dramatically. Modular execution after project splitting is a good solution, but tangled inter‑project references make it hard to proceed, which is the focus of this article.

Problem Statement

The main issue is chaotic coupling and references between projects. Theoretically each project should be independent, but in the same business system complete isolation is impossible, so we must address coupling from a technical perspective.

Router is introduced as a model to solve these coupling and dependency problems.

What Is a Router?

A Router is a model with "collection" and "distribution" concepts that handles direct dependencies and calls between modules (projects). It works by defining conventions and rules for parsing and then dispatching requests.

Development

The key characteristics identified are collection and distribution. A quick pseudocode example demonstrates the idea:

public static void jump(String rule){
    if(rule){
        gotoBusinessActivity();
        return;
    }
    if(rule){
        invokeBusinessFunction();
        return;
    }
}

While this approach works, several problems appear as the business grows:

Expansion

The class becomes a massive "big soup" of code.

Maintenance Cost

Changes in one project may affect others.

Complex if‑else logic is hard to maintain.

Distribution logic is hard‑coded, making fixes difficult.

Projects cannot be truly separated.

Extensibility

The current distribution is essentially "hand‑crafted" rather than a real dispatch mechanism.

Thus, extracting a unified process for distribution is the real challenge.

Core Router Modules

The Router consists of two core modules: Rule Processor and Executor.

Rule

Two essential elements are "address" and "package", corresponding to "target" and "data" in code. The rule format is simplified as:

protocol://path?data

For this project the concrete rule looks like:

tctclient://project/module?key=value

The difficulty lies in mapping the path string to the actual handling class.

Three mapping solutions are discussed: if‑else direct logic.

Using a Map for association.

Using XML configuration.

XML is chosen for its zero‑code coupling and dynamic update capability, while Map offers better performance by avoiding I/O.

Executor

The Executor receives the parsed intent and data and forwards them to a designated receiver via an interface, allowing business modules to implement their own handling logic.

Interceptor

When special generic logic must run before a rule takes effect, an interceptor layer is added. Interceptors can be multiple, all must pass before the Executor runs, and they can be implemented recursively.

Extension and Tooling

After two years of using the Router framework, it meets most business needs, but two issues remain:

XML loading incurs I/O overhead.

Long string rules like jump("tctclient://hotel/list") cannot be checked at compile time.

To address these, a Gradle plugin with Freemarker is used to generate helper classes at compile time, turning XML into a Map and converting long strings into enum constants, e.g., jump(Bridge.HOTEL_LIST).

Conclusion

The article reflects on the author's thought process when designing the Router, emphasizing that no single model fits all scenarios. Simple if‑else works for small scale, but as the system grows, refactoring becomes necessary. A framework must align with business needs to be effective, and thoughtful design outweighs mere coding.

Key takeaways include the importance of extracting core and non‑core lifecycles, and the value of thinking before coding.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javasoftware architecturemodularizationdependency managementGradleRouter
Tongcheng Travel Technology Center
Written by

Tongcheng Travel Technology Center

Pursue excellence, start again with Tongcheng! More technical insights to help you along your journey and make development enjoyable.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.