Introducing Navi: An Open‑Source Component Routing Framework for Scalable Backend Systems

Navi is an open‑source component‑routing framework that separates routing logic from business code using matcher annotations, allowing developers to define reusable handlers for different client types and versions, thereby reducing code complexity, improving extensibility, and simplifying backend services while supporting future extensions and community contributions.

iQIYI Technical Product Team
iQIYI Technical Product Team
iQIYI Technical Product Team
Introducing Navi: An Open‑Source Component Routing Framework for Scalable Backend Systems

Navi (short for Navigation) is an open‑source framework that provides component‑level routing capabilities. It was born from the iQIYI membership backend team’s exploration of system extensibility and component‑based design, and later generalized for public use. The project repository is http://github.com/iqiyi/navi .

The framework addresses common problems in complex business systems: high code complexity, poor extensibility, tangled core and branch logic, and low code reuse. By separating routing logic from business logic, Navi enables developers to keep the main functionality clean while handling diverse client scenarios (e.g., Android, H5) through dedicated components.

How to use Navi – a step‑by‑step example

Below is a typical order‑creation service before applying Navi. The code mixes business logic with multiple client‑type branches:

public class OrderService {
    public OrderCreateResult createOrder(OrderCreateRequest req) {
        Order order = Order.create(req);
        if (req.getClientType().equals(ANDROID)) {
            // Do for the common android client.
            if (Version.create("1.0.0", "2.0.0").within(req.getClientVersion())) {
                // Do for android client v1.
            } else if (Version.create("2.0.0", "3.0.0").within(req.getClientVersion())) {
                // DO for android client v2.
            } else {
                // Do for other versions android client
            }
        }
        if (req.getClientType().equals(H5)) {
            // Do for H5.
        }
        orderRepository.save(order);
        return OrderCreateResponse.create(order, req);
    }
}

Refactoring the branching logic into a separate method improves readability but does not solve extensibility, because every new client requirement still forces modifications to existing code.

With Navi, the same functionality is expressed by defining handler components annotated with matcher annotations. The routing selector automatically picks the appropriate handler based on request attributes, leaving the core service untouched:

interface OrderCreateHandler {
    void handle(Order order, OrderCreateRequest request);
}

@EqualMatcher(property = "clientType", value = "android")
@VersionMatcher(range = "[1.0.0,2.0.0)")
@Component
class AndroidV1Handler implements OrderCreateHandler {
    public void handle(Order order, OrderCreateRequest request) { }
}

@EqualMatcher(property = "clientType", value = "android")
@VersionMatcher(range = "[2.0.0,3.0.0)")
@Component
class AndroidV2Handler implements OrderCreateHandler {
    public void handle(Order order, OrderCreateRequest request) { }
}

@EqualMatcher(property = "clientType", value = "android")
@VersionMatcher(range = "[3.0.0,*)")
@Component
class AndroidLatestHandler implements OrderCreateHandler {
    public void handle(Order order, OrderCreateRequest request) { }
}

@EqualMatcher(property = "clientType", value = "h5")
@Component
class H5Handler implements OrderCreateHandler {
    public void handle(Order order, OrderCreateRequest request) { }
}

public class OrderService {
    @AutoWired
    private Selector selector; // SpringBasedSelector

    public OrderCreateResult createOrder(OrderCreateRequest req) {
        Order order = Order.create(req);
        OrderCreateHandler handler = selector.select(req, OrderCreateHandler.class);
        if (handler != null) {
            handler.handle(order, req);
        }
        orderRepository.save(order);
        return OrderCreateResponse.create(order, req);
    }
}

Developers can also create composite matcher annotations to simplify usage. For example, a @ClientRequestMapping annotation combines @EqualMatcher and @VersionMatcher:

@Retention(RUNTIME)
@EqualMatcher(property = "name")
@VersionMatcher(property = "clientVersion")
@interface ClientRequestMapping {
    @AliasFor(annotation = EqualMatcher.class, attribute = "value")
    String name();
    @AliasFor(annotation = VersionMatcher.class, attribute = "range")
    String clientVersionRange();
}

Applying the composite annotation makes handler definitions more concise:

@ClientRequestMapping(name = "hulk", clientVersionRange = "[1.0.0,2.0.0)")
class AliasAllHandler implements Handler { }

Implementation principle

When selector.select(req, OrderCreateHandler.class) is invoked for the first time, the selector loads all candidate components of type OrderCreateHandler from the Spring ApplicationContext. Each candidate’s matcher annotations are parsed into MatcherDefinition objects and cached.

The selector then evaluates the definitions against the request attributes. A REJECT result stops the current candidate; otherwise matching continues. By default the selector returns the first non‑rejected handler, but other strategies (e.g., highest match score, multiple candidates) are possible.

Future directions

Navi plans to add more built‑in matchers, additional component‑selection modes, configuration options (files, DSL), AOP‑based transparent usage, hot‑loading, and performance improvements.

The article concludes with a call for developers to try Navi, submit PRs, and discuss complex system architecture. A recruitment notice for iQIYI’s backend and big‑data teams is also included.

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 Architectureopen‑sourcedependency-injectioncomponent routing
iQIYI Technical Product Team
Written by

iQIYI Technical Product Team

The technical product team of iQIYI

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.