Can one controller call multiple usecase (interactor) in Clean Architecture?

1.6k views Asked by At

I am designing a mobile application with spring backend.

In my mobile app i have a shuffle button. When user clicks the button i am getting the current location of user from GPS and send with request then using this location information for find nearby users to the current user.

By the way shuffle and filter usecases using the same usecase only difference is when shuffle i apply default filters like all ages, all distances etc.

~~ Shuffle/Filter request ~~

{  
  interestedGenders: ["WOMAN"]  
  minDistance: 1,  
  maxDistance: 100,  
  minAge: 18,  
  maxAge: 65,  
  latitude: 31.4,  
  longitude: 27.1  
}

Okey, now my problem is i dont want to track/update user location real time so i thought when user shuffle (or filter) i already have user location so first i update user location in the database then i use this location information to find nearby users with other filters (gender, age etc)

I have 3 simple collection in mongo db (accounts, profiles and userLocations)

~~ profile document (Simplified) ~~

{  
  id: "xxx",  
  accountId: "yyy",  
  gender: "MAN",  
  interestedGenders: ["WOMAN"]  
}

~~ userLocation document ~~

{  
  id: "zzz"  
  accountId: "yyy",  
  location: {  type: "Point",  coordinates: [31.4, 27.1]  }  
  lastUpdated: "2020-10-10T00:59:37.154Z"  
} 

This is the code i use. First i execute update user location usecase. Update location usecase create record if user hasn't a record in db otherwise update user location. Then i find users meets the criteria.

@ApiController
@AllArgsConstructor
public class FilterProfilesController {

    private final UpdateUserLocationUseCase updateUserLocationUseCase;
    private final FilterProfilesUseCase filterProfilesUseCase;

    @PostMapping("/profiles/filter")
    public ResponseEntity<BaseResponse> filterProfiles(@AuthenticationPrincipal AccountId accountId, @RequestBody FilterProfilesRequest request) {
        var userLocation = new Location(new Latitude(request.getLatitude()), new Longitude(request.getLongitude()));

        updateUserLocationUseCase.execute(new UpdateUserLocationUseCase.Command(accountId, userLocation), () -> {
        });
        
        // Conversions from request to value objects (simplified)
        
        var query = new FilterProfilesUseCase.Query(accountId, userLocation, interestedGenders, ageRange, distanceRange);
        var presenter = new FilterProfilesPresenter();

        filterProfilesUseCase.execute(query, presenter);

        return presenter.getViewModel();
    }
}

What is the best way for designing this?
How can i decouple update location usecase (from FilterProfilesController) but still call before filtering usecase?
Should i write a custom Aspect or something?

Answers to some questions

Q: Why i didn't put user location information to inside profiles collection?

A: Because we dont get this information when user register. So location fields remains null until users using shuffle or filters page and i use geospatial query in mongo db so this can cause an error i guess.
Plus profile object already have lots of fields and i think seperating user location provide a lot of flexibility for future usecases.

1

There are 1 answers

4
Subhash On BEST ANSWER

It is generally recommended that you keep the "write" side of the application separate from the "read" side.

You would want to scale your query service independently because writes vs. reads are not proportional. For every write (the GPS location), you may want to perform 10x or 100x queries.

In your example case, you may receive GPS location the first time, but allow the user to perform multiple queries with different filter combinations. Assuming you gather the user's GPS location automatically, it may not change between queries.

Your application's domain model generally does not cater to queries. You would typically construct multiple representations of the same data (the write side) to cater to response formats required by different APIs (the read side). You can do this with a combination of Domain Events and Subscribers, and even create a separate microservice for the query side alone.

So, it's better to keep the GPS Location Update use case separate from the Query. It results in two calls, but you get a ton of flexibility in return.

Consequently, it would be best to let the caller (the UI, in this case) perform the update first before requesting data. This has the additional benefit that you can gather GPS updates asynchronously (as pings) to update the user location, not just when the user requests data.