A client or service must implement common behaviors like authentication, caching, logging, exception handling, and validation.
How can common behaviors like authentication, caching, logging, exception handling, and validation be executed without having to modify the client or service code?
Services often require common behaviors like authentication, caching, logging, exception handling, and validation. One could code these behaviors directly within the client or service, but this inevitably leads to long service methods, code duplication, and maintenance problems. Developers may instead extract common behaviors into small specialized classes that are instantiated and invoked from within the service. However, a "hard dependency" between the containing entity (i.e., client or service) and the generic behaviors will arise. Another common approach is to pull the cross-cutting behaviors out into their own services. A client's "order request" might, for example, first be directed to an authentication service, then to a logging service, and finally to the target order service. Unfortunately, this is a rather complex architecture that increases latency and the potential for partial failures.
Encapsulate cross-cutting behaviors within individual classes. Load these classes into pipelines that are managed by client or service frameworks.
Service Interceptors are common behaviors that are loaded into pipelines which exist within the address space of the client or service. These pipelines are created and managed by a framework like Apache's CXF, Microsoft's .NET, JAX-WS, JAX-RS, or Spring. Developers can often leverage standard interceptors that are a part of the framework. Most frameworks, for example, provide interceptors that support behaviors like authentication and schema validation. Developers can also create custom interceptors to encapsulate and consolidate the logic for other generic behaviors. One might, for example, create distinct interceptors for logging, exception handling, and distributed (memory) cache management. Since common behaviors like these are consolidated into distinct classes, maintainability is promoted, and duplicate code is minimized. This approach also makes it easier to reuse these behaviors across multiple clients or services.