crnk-project / crnk-framework

JSON API library for Java
Apache License 2.0
286 stars 153 forks source link

How to combine Router of Vertx with Crnk's `ReactiveResourceRepositoryBase` #550

Open UkonnRa opened 4 years ago

UkonnRa commented 4 years ago

In the code example, you use daggerModule.addRepository(someRepo); to inject and expose out Repository layer directly. So here are some questions:

  1. Literally, Repostitory layers communicate with Database directly and send the result to Service layer. The design now encourages people to write business code into the Repo layer rather than in the higher service layer. Is it a nice design?

  2. The worse result is, I cannot combine my own RESTful interface with CrnkVertxHandler in Vertx. The other frameworks may use annotation to mark the interfaces, but we use Router in Vertx, and daggerModule.addRepository does not support Router, so which means that the Crnk service should occupy a new port, but not combine with the previous service in the same port.

Any suggestions?

st-h commented 4 years ago

@CasterKKK I was able to use crnk with the Vertx router doing something like this:

public void start(Future<Void> startFuture) throws Exception {

    Set<ReactiveResourceRepository> repositories = new HashSet<>();

    repositories.add(new ProjectRepository());
    repositories.add(new ItemRepository());

    CrnkVertxHandler handler = new CrnkVertxHandler((boot) -> {
      SimpleModule appModule = new SimpleModule("app");
      repositories.forEach(it -> appModule.addRepository(it));
      boot.addModule(appModule);
    });

    HttpServer server = vertx.createHttpServer();

    Router router = Router.router(vertx);

    router.route().handler(routingContext -> {
        HttpServerRequest request = routingContext.request();
        User user  = routingContext.user();

        Flowable.fromArray(request).flatMap(r -> handler.process(r))
          .subscribe((response) -> LOGGER.debug("delivered response {}", response), error -> LOGGER.debug("error occurred", error));

    });

    server.requestHandler(router).listen(8888);

yet, that feels rather cumbersome and I was hoping to find some time somewhen to see if there is a better way.

Regarding our first point, I think you should be able to call your service layer from the repository and keep your business logic in the service layer. Mono offers methods to convert for instance existing CompletableFuture returned by the service layer, however so far I did not check how the native vertx classes like Future are supported. hope that helps

UkonnRa commented 4 years ago

@st-h But you still cannot combine Router with CrnkHandler even if you write like this?

st-h commented 4 years ago

@CasterKKK I noticed I probably have stripped too much from the start method of the verticle in the code I posted and just edited it. Hope it is clearer now. Basically what you do is define a route and from that routes handler you call the process method of the CrnkVertxHandler. That way you should also be able to attach the crnk handler to a sub path of the vertx router. The currently inconvenient thing is that I had to define a Flowable from an array, which always just contains one item, but there should be ways to improve this.

UkonnRa commented 4 years ago

Cool! Problem resolved! May be you can change Flowable to Single, I think it works the same.

remmeier commented 4 years ago

Would be useful to add the router to the dagger-vertx-example as it is common use case. Maybe an implementation of Handler<RoutingContext> should be provided?

Literally, Repostitory layers communicate with Database directly and send the result to Service layer. The design now encourages people to write business code into the Repo layer rather than in the higher service layer. Is it a nice design?

as pointed out by @st-h arbitrary number of additional layers can be added as the application requires it. on the otherside with the resource-oriented nature it is kind of already at a "higher-layer" and the internal crnk engine takes care of many things tradditionally contained in the rest layer. There are minimalistic create, find, save and delete methods, no path mappings, swagger annotations, parameter parsing, Http-specific data structures and other things that one would not like to see in business code. So repositories are also easy to call from backend internal code and testing.

UkonnRa commented 4 years ago

---- Update ---- I have just reviewed the Resource-Oriented Programming and found that I totally misunderstood this architecture. So the right way is that create, update, delete and find* in Repository will be mapped to POST, PATCH, DELETE and GET. And the Repository in Crnk is more likely to Controller in Spring. So all the business logic layer should be injected to here!

So I have to say that, the name Repository is too confusing for Spring players. I still cannot accept this name and prefer Controller or something.

---- Origin Comment ---- @remmeier

  1. If you consider Repository as the restful layer which can communicate with the outside world, than I think its functions should more than CRUD. Because I have many other business interfaces, I want to put all my business Rest API into ONE Java interface.

  2. If you want users to write business method in Repository, then I think the annotations of @GET, @POST etc should be given to expose the business Rest API. If you do that, you will wrap up the Vertx's Router. Although not official, but I think it works.

Overall, JSON:API just designs how to get the resources, but say nothing about the business methods. So I hope you can give me a way to merge the resources and business methods.

PS: @st-h 's idea is enough for work, but I think it can become more beautiful.

remmeier commented 4 years ago

"So I have to say that, the name Repository is too confusing for Spring players. I still cannot accept this name and prefer Controller or something."

I would have put it closer to a spring data repository than to a spring controller? but in general the the inner workings are quite different to either.

"because I have many other business interfaces, I want to put all my business Rest API into ONE Java interface. Overall, JSON:API just designs how to get the resources, but say nothing about the business methods"

Not yet 100% sure whether we thing about the same things, but is is quite the contrary I would say regarding business functions.This is an area where things really start to deviate from more classic rest stacks. Or at least with what I usually see people doing with it. Here JSON:API goes 100% resource-oriented. All busienss code goes into PUT, POST, DELETE of resources and relationships. So if you have a person with an address, and you implement a "change address" business function, then can be implemented in two, three ways:

This can be quite different to more traditional RPC-style, but usually I found for things to become more cleaner, discoverable ,more intuitive if the architecture/model is properl setup. But there is also potential to mess it up.

maybe something to add to the docs.

st-h commented 4 years ago

just my $.02 💵 here, but the name Repository reminded me about domain driven design concepts and personally I think in that context it fits pretty well. Granted, if you are used to other concepts, then that might seem weird at first, but it will be very hard to find something everyone with every background will like. I don't think spring and ddd exclude each other? (but I am honestly not that familiar with details about spring)

remmeier commented 4 years ago

let's reopen this to extend the exampe application with a good Router example

remmeier commented 4 years ago

regarding the resource-oriented api topics i did a iteration over http://www.crnk.io/releases/latest/documentation/#_architecture