I am still thinking about clean architecture and just ran into a question regarding the higher levels (Views and Presenters)
I am posting Uncle Bobs picture first here that you remember what I am talking about:
Lets say I have a small application where CRUD operations can be performed for customers.
Lets also say there are three ways to output the customer data to the user:
- A form component, where a new customer with name and birthdate can be updated.
(The same form is also used to create a new customer, but in this case I do not need to load existing data)
- A detail component where the name and birthdate of the customer are shown on the screen.
- A PDF-Export where a customer should be displayed in a PDF with the birthdate in US-Format. If the customer is exactly 60 years old, the birthdate should be decorated with special color – these customers get a present. 🙂
Now lets say I want to implement this.
My approach for this looks like this:
You can see that I have only one class for output data (one file) and also just one abtract presenter class that gets the OutputData.
But then I have three concrete presenters with three concrete view models that are responsible for my views.
Now my doubts:
-
Is this correct? Did I understand the clean architecture?
-
My concrete presenters and my viewmodels for the HTML form and the HTML detail are exactly the same. This feels redundant. Should I avoid this and re-use my view models for both scenarios? Or should I stick with the borders, as the requirements of the future might change for forms, so that I need seperate models/concrete presenters?
-
If I finally want no US-Format in the date anymore, but lets say… european one. Do I need a new concrete presenter, just for this? Or should the presenter ask the use-case for the global format of the application and then decide on the base of that?
-
If customers that are 60 should receive a birthday present – this logic has to be in a use case. But I am a little lost where this would go… Probably another use case that gets every night all customers from the repository that are 60 and sends a present… ? But this would not relate at all with the decoration in my view model? So the information about the number "60" would be in my viewmodel and my use case? Two redundant times? Or how?
Best Answer
If you want to create a solution consistent with clean architecture you have a number of issues.
Dependencies should only cross boundaries going in one direction. That keeps the inside from needing to know about the outside. That means the outside can change and the inside won't care. This also helps keep your object graph acyclical. It keeps code changes from cycling through your code. Instead of dependencies tending to form a maze they they tend to form trees.
Your
View Model
andOutput Data
objects shouldn't be the same. If they are that means thePresenter
hasn't done anything useful. It's not the views job to transform how data is presented. That's the presenters job.Your
Database
, andEntities
should have an agreed on way to store dates. But yourPresenters
should be free to present those dates in any form they wish.If a user typed in a date, or selected today's date, and sent that date to the correct
Controller
it would build anInput Data
with that date in it, using a way to express dates that theController
andUse Case
agree on.The
Controller
then calls the correctUse Case Interactor
and passes theInput Data
. TheUse Case Interactor
uses either theEntities
or theData Access
(or both) to get a list of people with birthdays on this date and uses that list to buildOutput Data
.Output Data
need not be a result set. It can conform to any structure that theUse Case
andPresenter
agree on. It may simply be a collection.The
Use Case
calls thePresenter
and passes theOutput Data
. ThePresenter
is now free to put last name first, first name first, draft happy birthday emails to be sent to each person, or send one email with a list of these people to a florist. Which one it does depends entirely on which presenter you configured to receive this.Views should not do real work. They should be striped of interesting logic and simply be things you throw data at. Data that is already in its final form.