WebExpress 0.0.10-alpha – Class Annotation vs. Method Annotation: A Clear Comparison
In many REST frameworks, the same question eventually arises: Where should I annotate my HTTP methods? Directly on the class or on the specific method that handles the request? Both approaches work, both are valid, and both lead to completely different architectural characteristics. That’s exactly why it’s worth making this decision consciously instead of leaving it to chance or framework conventions. In this article, I compare two models that have become established in REST systems: the linear, class-based model and the non-linear, method-based model. I also show how modern Java frameworks like Spring Boot solve this question and why. In the end, I want to find a solution that feels right for WebExpress, performs well, and can be adapted dynamically.
In the linear approach, the HTTP method is defined directly on the class. The class represents exactly one endpoint and exactly one HTTP verb. The implementation contains a single method that processes the request, often with a fixed name like getData(). This model is extremely well structured: one class corresponds to one endpoint and one verb, without branches, alternatives, or overloads. The big advantage lies in its simplicity. The dispatcher knows when loading the class which method is responsible and can operate deterministically without reflection. Routing is trivial, a simple if-branch is enough to call the correct method. This model achieves maximum performance because there is no dynamic lookup, no matching, and no ambiguity. However, you are bound to fixed method names, and flexibility is significantly limited. This approach is especially suitable for systems optimized for speed, simplicity, and deterministic execution. It resembles minimalist microframeworks or embedded systems where every additional abstraction introduces unnecessary latency.
The non-linear approach is quite different, where the annotation sits directly on the method. Here, a class can contain multiple GET methods, multiple POST methods, different routes, different DTOs, and different signatures. Method names are freely selectable, such as getUser(), getUserById(), or searchUsers(). A class can handle GET, POST, PUT, and DELETE simultaneously, which allows for tremendous flexibility. However, the dispatcher must decide at runtime which method fits. This requires reflection, as methods must be scanned, attributes evaluated, and parameters matched. While this opens up many possibilities, it also leads to potential ambiguities when multiple methods could theoretically match. In such cases, the system needs priorities or clear error handling. The strength of this model lies in its expressiveness: different parameter lists, different routes, different query parameters, different body types, different constraints, different return types, and true overloads. This diversity makes the model powerful but also more complex.
Modern Java frameworks like Spring Boot, Quarkus, Micronaut, or Helidon rely almost exclusively on the method-based model. Each HTTP operation is annotated directly on a method, using annotations like @GetMapping, @PostMapping, or @Path. The reason is simple: large APIs are rarely linear. They consist of many variations of an endpoint, different DTOs, versioning, query parameters, and routes. The method-based model allows for this diversity, and the frameworks provide the necessary routing logic. However, these frameworks pay the same price that you would pay in your own system: reflection or annotation scanning, dynamic routing, matching logic, ambiguity handling, and more complex error cases. Frameworks like Micronaut or Quarkus partially compensate for this through build-time processing, but the core principle remains: flexibility comes at the cost of complexity. The class-based approach, on the other hand, is more reminiscent of very lean microframeworks or systems that deliberately rely on static routes to minimize latency. It is ideal for environments where performance and deterministic behavior are more important than maximum expressiveness.
WebExpress deliberately follows the approach that endpoints are always represented by classes. Each endpoint has a unique route that is directly assigned to the class. The individual HTTP verbs are then distributed to methods within this class and annotated accordingly, such as with GET. This keeps the structure clear, the assignment unambiguous, and the internal architecture easy to understand. At the same time, this model allows for a clean separation between routing, logic, and structure without fully adopting the complexity of the method-based approach. WebExpress consciously positions itself between both worlds: it uses the simplicity of the class-based model without giving up the flexibility of modern method mappings.
In the end, there are two models, each optimized for different goals. Class-based annotation is fast, deterministic, and simple but inflexible. Method-based annotation is flexible, modular, and expressive but more complex and slower. The decision therefore does not depend on the framework but on the architectural philosophy. If you want maximum performance and clear, linear endpoints, class annotation is a good choice. If you need flexible APIs with many variants, routes, and DTOs, you should go with method annotation. Both models have their place, and the key is to consciously choose the one that fits your platform.
And this is exactly where I want to invite you: If you’re interested in contributing to a modern, modular, and lightweight framework, join WebExpress. Share your ideas, help improve existing components, or develop new features. Every form of contribution helps advance WebExpress and maybe even shape the architecture of the next generation.

Comments
Post a Comment