Most of the discussions about Clean Architecture focus on diagrams. That’s great, if you’re used to reading diagrams. If you’re not, the thing you really need is a Clean Architecture example.
When we learn a new language, the first thing we normally write is a Hello World application. Why not do the same with Clean Architecture?
This week, that’s exactly what we’re going to do.
Saying Hello in Clean Architecture
The typical Hello World example is a one-liner with just enough boilerplate code around it to allow it to run. To say hello in Clean Architecture, we need to separate input, processing, and output though. That makes it a bit more complicated.
Let’s take a look at each responsibility in turn and then we’ll look the overall flow.
Receiving Input the Clean Architecture Way
There are two elements that make Clean Architecture input clean. First, the interface method, which is implemented by the Interactor. Second, the Request Model, which is what carries the actual input values used by the Interactor.
I like to name the interface Story, because I believe that an Interactor is something closer to a story than a use case. Also, because an Interactor follows the Command Pattern, I like to name the interface method execute.
In order to standardize on a single interface, we have to standardize the parameter type along with the name of the interface method. Since, I name my interface Story, I tend to call the parameter StoryRequest.
A StoryRequest has no methods. It’s a simple tag interface that allows me to call the Story interface method consistently. The Story must either leverage template types (generics in Java) or cast to the expected type.
The implementation is a simple, flat object that only contains public fields.
Delivering Output the Clean Architecture Way
Output, similar to input, depends on two elements.
The first element is a standardization of the Story output. This piece, because I like symmetry, I like to call StoryResponse. It works just like StoryRequest, but for output.
Just like the StoryRequest, the StoryResponse is a simple tag interface. Its implementation is a simple, flat object.
Standardizing the form that Story output takes is critical because it lets us also standardize another thing: the way we send output to the view.
Output is sent to the view using a presenter. Again, to be symmetrical, I like to call this interface StoryPresenter.
Where the Story interface is the Interactor input boundary, the StoryPresenter is the Interactor output boundary.
What Does a Story Do? It Processes!
Alright. The controller has instantiated a Story and StoryRequest, and the execute method of the Story has been called with the StoryRequest.
Now what?
Now, the Story implementation needs to downcast the StoryRequest to its implementation type. Once downcasted, the fields sent on the request are accessible.
Again, you could use Java generics instead of downcasting. I just don’t see the value, because this one type is used in one place and the cast will only occur one time.
Back to our processing explanation.
Typically values contained on the request will be used to find or update one or more Entity instances, which are given to the Story implementation through a Gateway interface. Java folks could use a Data Access Object here, but – for complex applications, depending on your intended deployment strategy – a Data Access Object may become too coarse-grained to be ideal.
So, the Story has received its input. It has used this input to collaborate with zero or more Gateway implementations to find, store, and/or update zero or more Entity instances. It now needs to communicate an outcome to the view.
In order to send output to the view, the Story creates a StoryResponse implementation using all data that must be sent to the view and calls the execute method on the StoryPresenter with the StoryResponse. The StoryPresenter then updates the view model by applying the values on the StoryResponse.
What’s a Presenter to Do?
The C file-oriented operations that Uncle Bob talks about are focused on moving bytes. Our interactors work on data that may have many different shapes though.
The presenter is the thing that allows an Interactor to behave as generically as that copy operation. What this means is that the presenter is going to be heavily specialized to the specific story and delivery mechanism.
In a thick client, the presenter may be created with a reference to the view model and then apply updated values to that view model. From there, Observer / Observable kicks in and works its magic to update the view.
This is true because of the fact that the view, view model, and all of the Clean Architecture bits and pieces run in the same JVM.
For web-oriented applications, that’s just not how it works. The view likely lives on a completely different machine. Even if it does exist on the same machine, it lives in a very different process.
References don’t work for a web-oriented application. Instead, we have to simply create the view model and somehow send it to the view on the HttpServletRequest (for JSP applications) or HttpServletResponse (for Ajax or REST applications).
Hello, Clean Architecture!
So, to have a whole slice of Clean Architecture, we need to have a Story, StoryRequest, StoryResponse, and a StoryPresenter.
A Story should be named after its story. When I think about the story for Hello World, I come up with GreetUser. So, that’ll be our implementation class.
GreetUser will need a request (GreetUserStoryRequest) to bring us our input – the userId of the User to be greeted. It will also need a response (GreetUserStoryResponse) we can use to call the presenter (GreetUserStoryPresenter).
We’ll also need a gateway (UserGateway) to help find the User.
To keep things simple, and because a presenter may be implemented in several different ways, ours will do the transformation to the view model and then just emit that to stdout.
Implementing GreetUser
Here are the elements of the implementation.
GreetUserStoryRequest
public class GreetUserStoryRequest implements StoryRequest {
public final String userId;
public GreetUserStoryRequest(String userId) {
this.userId = userId;
}
}
GreetUser
public class GreetUser implements Story {
private final UserGateway userGateway;
private final GreetUserStoryPresenter presenter;
public GreetUser(GreetUserStoryPresenter presenter, UserGateway userGateway) {
this.userGateway = userGateway;
this.presenter = presenter;
}
public void execute(StoryRequest request) {
GreetUserStoryRequest greetRequest = (GreetUserStoryRequest) request;
User user = userGateway.findByUserId(greetRequest.userId);
presenter.execute(new GreetUserStoryResponse(user.getFirstName()));
}
}
UserGateway
The Data Access Object pattern is so commonplace, I hardly think you need an example. So, I’m skipping this one.
GreetUserStoryResponse
public class GreetUserStoryResponse implements StoryResponse {
public final String firstName;
public GreetUserStoryRequest(String firstName) {
this.firstName = firstName;
}
}
GreetUserStoryPresenter
Remember, we’re shortcutting a little here. Keep that in mind when you look at how we “hand the view model to the view.” We’ll take a closer look at sending data to the view with the upcoming Clean Architecture course.
public class GreetUserStoryPresenter implements StoryPresenter {
public void execute(StoryResponse response) {
GreetUserStoryResponse greetResponse = (GreetUserStoryResponse) response;
Map<String, Object> viewModel = Collections.singletonMap(“firstName”, greetResponse.firstName);
System.out.println(String.format(“Hello, %s!”, viewModel.get(“firstName”));
}
}
Wrapping It Up
That’s it! An entire slice of Clean Architecture!
Yes, it’s simple. However, you should be able to see all of the dots and lines connecting them now.
In a more complex application, some commonalities would be present among the StoryRequest, StoryResponse, and StoryPresenter implementations. Our Clean Architecture example is anything but complex though.
Closing Thoughts
An article showing a Clean Architecture example is good, but a course would be better. The people have made their voices heard, and I am building a Clean Architecture course!
Stay tuned for updates – it’s coming!
That’s it for this week! Until next time, this is Eddie Bush for Craftsmanship Counts reminding you to keep your tests passing and your code clean!
Remember to comment, like and share!
Leave a Reply