Java REST API – Implementing a REST API in Clean Architecture

Architecturedesign-patternsjavarestspring-mvc

I have been implementing a proof of concept application using Uncle Bob's Clean Architecture and I have run into a bit of a problem.

Uncle Bob's architecture calls for the explicit separation of request and responses using interfaces. This is not a problem in most cases (e.g. when implementing a UI using the MVP pattern) but I don't know how to apply this to create a REST API using Spring MVC.

My Controller has a method with the following signature:

Response<String> greet(String name)

mapped to /greeting that takes a name and outputs a different greeting depending on the value of the name.

Injected into the Controller is the UseCase that receives the name and creates the greeting, sending the output through the OutputPort injected into it.

The problem is that I cannot separate the inputs and outputs in this way because the Controller needs to interact with both the inputs and outputs to create a response.

The only way to "implement" this, that I can come up with, is returning the value using the InputPort, which sounds pretty bad and not at all what a Clean Architecture calls for.

I've been thinking about this and I cannot find any way that my Controller can act both as a Controller and as a Presenter at the same time. Am I missing something here? Is there a better desing that would allow the separation of the inputs and outputs of the REST API without massively overcomplicating things?

EDIT: I have been reading again chapters 23 and 24 of Clean Architecture and I sure could have phrased my questions a lot better.
What I have right now as a (working, perfectly fine) solution is a One-Dimensional Boundary (page 219) and I was wondering if I could extend this and separate this interface into two reciprocal interfaces. Hopefully this clarifies my point a bit.

Best Answer

OK, I think I see what's going on: you've got a context impedance mismatch.

You're in a framework now, Mr. Bond. The game with the composition is played a little bit differently here.

The basic idea to recognize is this: the "View", so to speak, changes with every HTTP request that arrives (more precisely, it has a scope coupled to the lifetime of the HTTP Connection, but that's not important here).

That means that we need to compose a new Controller->Use-Case-Interactor->Presenter pipeline for each request.

The good news: Spring is already doing that for you under the covers.

The bad news: Spring is using a single Input-Output boundary, rather than separating them.

I say "bad news" because you are trying to make this round peg fit into the square hole described by Martin. I think what Spring is doing here is "fine", it's just inconvenient for the interfaces you want.

So let's pretend: you have a use-case-interactor that expects to be wired to an input boundary and an output boundary, and you have this signature on your controller. Now what?

Response<String> greet(String name) {
    InputBoundary in = inputBoundaryFor(name);

    // Arbitrary choice
    List<String> greetings = new ArrayList();
    OutputBoundary out = outputBoundaryFor(greetings);

    // Here's our composition with request scope.
    UseCaseInteractor uci = useCaseInteractorFor(in, out);

    uci.run();

    String greeting = greetings.get(0);

    return responseFrom(greetings.get(0));
}

If you were a bit luckier with the API for your UseCaseInteractor, it might instead look like:

final UseCaseInteractor uci = ...

Response<String> greet(String name) {
    InputBoundary in = inputBoundaryFor(name);

    // Arbitrary choice - any reasonable container for
    // for a result could be used here.
    CompleteableFuture<String> greetings = new ArrayList();
    OutputBoundary out = outputBoundaryFor(greetings);

    uci.run(in, out);

    String greeting = greetings.get();

    return responseFrom(greetings.get(0));
}

You might find this more familiar if we were to structure it as a callback

Response<String> greet(String name) {
    // Arbitrary choice - any reasonable container for
    // for a result could be used here.
    CompleteableFuture<String> greetings = new ArrayList();

    uci.run(name, (greeting) -> {
        greetings.complete(greeting);
    });

    String greeting = greetings.get();

    return responseFrom(greetings.get(0));
}

The core idea, though, is that you have two functions; in the module with your use case interactor, you have a function that looks like

InputData -> OutputData

and in your web layer, you have a function that looks like

OutputData -> ViewModel

the input and output boundaries are just a way to compose those two functions without introducing a dependency that points the wrong way.