Dependency Inversion 2.0: “Enhanced Eventdriven Service Design” with the magic4j-cdi-utils

Have you ever done the design of a layered application – usually representing a certain functional domain or microservice – consisting of the usual Web-, Façade-, Businesslogic- and Data-Access-Layers? Did that annoy you because of the continuously rising quantity of pure delegation methods in the Façade- and Businesslogic-Layer that produce nothing but “hot air” (and of course work overhead)? Then the CDI-based “Enhanced Eventdriven Service-Design” (“EESD”) is for you!


Note: Although this article explains some basics about CDI-Events, it is not a tutorial for newbies. It is presumed, that you already have basic knowledge about CDI and its Event mechanism.

EESD-Quickstart for Java EE Designers/Developers and Architects

The main achievement of EESD – provided by the “magic4j-cdi-utils” library – is, that it enhances the CDI-Event mechanism by injecting the result of an Event-Observer method back into the Event-Producer – while still keeping both the Event-Producer and the Observer completely decoupled. This seemingly small feature nevertheless opens up the door to a significantly more lightweight application design: In a traditional layered application design that conforms to the Dependency Inversion Principle (DIP), you needed interface methods in API-Modules of each layer as the abstraction from higher-level- to lower-level-modules (see diagram 1 below).

traditional domainlayers with Dependency Inversion Principle

Traditional Application layers with consideration of the “Dependency Inversion Principle”

In EESD, CDI-Events replace these API-methods as the abstraction. An Interceptor provided by the magic4j-cdi-utils delivers the ability to inject return values into the Event-Producer. CRUD-Methods which do not need additional business logic are ideal EESD-Usecases: Just imagine that you can now trigger a DAO-method from the web-layer without the need to go through the Facade- and Businesslogic-Layers. Instead of bloating the intermediate interfaces and implementation classes with useless methods, you only involve those modules that really contribute to the solution.

In order to round up this quick intro, here are example code snippets that show you the implementation of EESD with magic4j. If you are familiar with CDI and its Event-mechanism, you will probably already be able to look through its easy usage. The central part of magic4j-cdi-utils is the “EventResultProviderInterceptor” that manages the provision of the result value to the Event-Producer. Be aware, that there is no need to fire back a second CDI-Event to the original Event-Producer!

@EventResultReceiver
@RequestScoped
@Path("v1/orders")
public class OrdersResource {

    @Inject
    private JaxbXmlConverter jaxbXmlConverter;

    @Inject
    @RetrieveOrder
    private Event retrieveOrderByIdEvent;

    @RetrieveOrder
    private String retrievedOrderAsXml;

    /**
     * Retrieves representation of an instance of
     * me.stephanbauer.eventdriven.microservice.web.OrdersResource
     *
     * @param orderId
     * @return an instance of
     * me.stephanbauer.eventdriven.microservice.core.OrderDto
     */
    @GET
    @Produces("application/xml")
    @Path("{orderId}/")
    public OrderDto getOrderById(@PathParam("orderId") Long orderId) {
        retrieveOrderByIdEvent.fire(orderId);
        OrderDto orderDto = this.jaxbXmlConverter.unmarshallXml(retrievedOrderAsXml, OrderDto.class);
        return orderDto;
    }

 Listing1: The REST-Service produces the @RetrieveOrderById Event.

Please look at the following Annotations here:

  • The @EventResultReceiver Annotation at class level: This is needed so that the magic4j-Interceptor can find the desired result value injection point quicker, as it only needs to search through beans that have this particular annotation.

  • The custom @RetrieveOrderEvent annotation, which is a Usecase-specific CDI-Qualifier, that the developer needs to provide. In the Event-Producer, there are two places, where this Annotation is needed: The Event itself and the instance variable, into which the return value should get injected.

Please also note, that the Event-Producer must be an @RequestScoped CDI-Bean. I’ll explain this in a minute.

Below you find the code snippet for the Observer-Bean.

@RequestScoped
@EventResultReceiver
public class OrderDaoImpl {

    @Inject
    @MapOrderEntityToDto
    private Event mapOrderEntityToDtoEvent;

    @MapOrderEntityToDto
    private String orderDtoAsXml;

    @EventResultProvider
    public String retrieveOrderById(@Observes @RetrieveOrder Long orderId) {
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setId(orderId);
        orderEntity.setOrderNumber("123");
        orderEntity.setAmount(BigDecimal.TEN);
        mapOrderEntityToDtoEvent.fire(orderEntity);
        return this.orderDtoAsXml;
    }
}

Listing 2: The Bean with the Observer-Method for the @RetrieveOrder Event.

Please look at the following annotations here:

  • The Observer-Method (the one with the @Observes-Annotation) is annotated with @EventResultProvider, which is the Interceptor-Annotation for activating the magic4j-EventResultProviderInterceptor. This interceptor actually performs the return value injection into the Event-Producer by looking for a bean with the corresponding return value injection point (see Listing 1).

  • This Bean is also annotated with @EventResultReceiver, because in this example, the retrieveOrderById()-method also uses EESD for triggering the mapping from the OrderEntity instance to the OrderDto-instance. You may wonder, why this method returns the OrderDto as a String. This is because of the fact, that I didn’t want to introduce additional dependencies upon the DTO-Module in the DAO-Module just because of EESD. You’ll find more details on this in the next chapter.
  • Please be also aware, that this Observer-Method does not return void, as it is done by usual Observer-Methods. Instead, it returns the value to be injected into the Event-Producer. The interceptor is the one, who receives this return value and performs the injection.

The call sequence of the retrieveOrderById(orderId) example

As already mentioned above, there are 3 Beans and 2 Events involved in this example:

  • The Restful Webservice “OrdersResource”
  • The OrderDaoImpl Bean
  • The DtoMapperImpl Bean for mapping from Entity to DTO

The following Activity-Diagram should clear up the call sequence:

EESD-RetrieveOrderActivity

Activity-Diagram of the retrieveOrderById()-example

The six EESD-key design considerations

1. Independent components without vertical hierarchy

In EESD, the layers lose their aspect of being at a fix, certain level in the hierarchy of layers, where each layer has to be called by its superior layer in every request.

    • Instead, each layer is considered as an independent component, to which no other component needs a compile-time dependency. You can consider all components being on the same hierarchy level, it’s just that each component has its well-defined and cleanly separated responsibility.

    • Thus, the name “layer” is not suitable for EESD anymore. Each component can fire events that get serviced in any of the other components. The Event-Producer doesn’t (need to) know which component provides the corresponding Observer-Method.

2. Predictable execution order through synchronous event delivery

Due to the synchronous nature of the CDI-Event delivery (default behavior), the execution sequence is exactly predefined. In fact, the method that fires an Event receives back the flow control only after the event has been completely processed by the Observer-Method (and the result has been injected into it by magic4j). Furthermore, it is also possible to nest Events in that sense, that you can fire another Event from within an Observer-Method. See the above Activity-Diagram for a concrete call sequence with 3 components and 2 Events:

3. Observer-Method-Result Injection

By applying the EventResultProviderInterceptor and some more annotations from the “magic4j-cdi-utils” library, the result from the observer-logic gets automatically injected into the Event-Producer-Bean. This leads to a situation where you can effectively mimic a normal call to a non-void method without any coupling between the caller and the callee. In other words, you call a method indirectly and the assignment of the result variable happens under the covers of magic4j.

4. @RequestSoped Event-Producer-Beans for threadsafe instance variable value injection

Each Event-Producer Bean that is meant to receive a return a value from an Observer, must be an @RequestScoped CDI-Bean. The main reason for this is, that magic4j can provide the return value only to an instance variable of the Event-Producer. If the scope was for example @ApplicationScoped, then mutable instance variables lead to thread-unsafety. As of magic4j version 1.0, each Bean with an Observer-Method that is meant to return a value to the Event-Producer, must also be @RequestScoped.

5. Effects of skipping the Facade

The Façade-Component is no longer needed for EESD-Usecases. Thus the two main responsibilities of the traditional Façade get split up into other components:

  • The DTO-to-Entity-Mapping (and vice-versa) gets separated out into a dedicated mapping component without any other responsibility.

  • The second traditional Façade-responsibility is the transaction handling.

5.1 Modificated DTO-Mapping for keeping dependencies clean

In order to keep the module dependencies to the DTO-API- and the Entity-API-Module clean, the DTO-To-Entity-Mapper-Component must provide map-Methods that return the target-Instance as a serialized XML- or JSON-String. The reason for this is, that by dissolving the Façade-Component we lost exactly that one component that was the only one to have dependencies to both modules. Nevertheless, we still want the Web-Component to only depend on the DTOs while the Business-Services and the DAOs should still only depend on the Entities.

The Mapping from the Entity to the DTO must now be initiated by the DAO- or BusinessService-Component. In order to avoid a dependency to the DTO-module there, the mapper cannot return the DTO itself, but instead a serialized XML- or JSON-String representation of it.

The same is valid vice versa for the Web-Component when it needs to transform the DTO to an Entity. But this is no problem for the DTOs because they need to be XML/JSON-serializable anyway so that JAX-RS can send them over the wire. The only overhead is, that you need to make the entities XML/JSON-serializable as well.

5.2 Shifting the transaction boundary into the Weblayer

In the absence of the Façade-Layer, the transaction boundary gets shifted into the Web-Layer. Therefore, there are at least the following two alternatives:

  • If you have EJBs and want them to take care of the Transaction-Boundary, then you can define the JAX-RS-Resource class as a Stateless Session EJB: But EJBs currently cannot send CDI-Events, so the EJB must delegate the Request to an @RequestScoped-CDI-Bean, which in turn represents the starting point of the Event-driven call flow.

  • If you don’t want or cannot have EJBs, you can either start a UserTransaction manually or you integrate a CDI-Extension like Apache Deltaspike and let it demarcate the Transaction-Boundary for you. In that case, the JAX-RS Resource-Bean can itself be an @RequestScoped CDI-Bean and so you can get along with only one bean if you like.

Testing

Although there are frameworks like Apache Delta Spike, that make it possible to unittest CDI-injections in a standard-unittest-vm, testing EESD-interactions outside of a JEE container currently seems to be impossible, because @RequestScoped Beans do not seem to be supported in Standard-VMs (see the list of supported features of Weld 2.3.3 Final)

Note: Although the author is not a fan of the arquilian framework, this currently seems to be the only way to write tests for EESD. Please leave a comment if you know about alternative test possibilities. Anyway I have of course done integration testing including load testing in order to ensure, that return values don’t get mixed up because of multi-threading issues.

Final recommendations for EESD

The question arises, if you should completely set aside the API-Modules, especially also for those cases, where each layer does contribute to the solution. To be honest, I think that in such cases, you should still prefer the usual programming model, because it’s easier to read.

For CRUD-Operations where no additional code in the business logic component is needed, you receive the full benefits of EESD.

Even if you need some business logic, you can still skip the facade. The Event fired from the Web-Component gets observed in the business logic component and from there, the DAO-Method should be called via a normal API-interface method.

To generalize this: If you need to communicate between 2 components that were direct neighbours in the traditional application layer concept, then you should stick to the traditional direct method calls, because it keeps your code more readable. As soon as you can skip one layer, you should use EESD. I want to discourage you from introducing additional dependencies compared to the traditional layering, e.g. the Weblayer should not get a compile dependency to the business logic API or the DAO-API.

Thus the best approach for readability on the one hand and code compactness on the other hand should be to use both design strategies in parallel and use the best of both worlds where appropriate.

Downloading magic4j-cdi-utils

I have released the magic4j-cdi-utils under the Apache License 2.0 to the central maven repository. Thus you simply need to add the following maven dependency:

            <dependency>
                <groupId>org.magic4j</groupId>
                <artifactId>magic4j-cdi-utils</artifactId>
                <version>1.0.0.0</version>
            </dependency>

-Stephan Bauer

Leave a Reply

Your email address will not be published. Required fields are marked *