Where to combine methods from different repositories?
My question is better asked through an example.
Suppose I have 2 repositories, UserRepository and ChatRepository, like below.
public interface UserRepository
{
/**
* Fetches Accounts of all the users who are friends with the authenticated user.
* @return an Observable that emits all the Accounts of friends in a List.
*/
Observable<List<Account>> allFriendAccounts();
}
public interface ChatRepository
{
/**
* Fetches the number of messages sent by a peer that are unseen by the authenticated user, by the peer's userId.
* @param peerUserId the userId of the peer.
* @return an Observable that emits the number of unseen messages sent by the peer as a Long.
*/
Observable<Long> unseenMessageCount(final String peerUserId);
}
The Account class is a model inside the domain layer, contains account information associated with a user and exposes them through methods like getUserId(), getUserName(), getPhotoUrl() which are self explanatory.
I also have separate UseCases for those repository methods named AllFriendAccountsUseCase and UnseenMessageCountUseCase.
Now suppose, I need to show a list of friends in my presentation layer, each item of which shows the name and photo of a friend as well as the number of unseen messages from him/her, similar to the list shown below.

Obviously I'll need to do at least 2 things.
-
Create a new model class which contains name and photo of a user as well as unseen message count. Possibly create separate model classes for domain and presentation layer.
-
Use both
UserRepositoryandChatRepositorymethods to construct a list, containing objects of the class described in 1. (In short, get theAccounts of the friends usingallFriendAccounts(), and then callunseenMessageCount(final String peerUserId)for eachAccount, usingAccount.getUserId()as parameter.)
I can think of 2 approaches, each described below.
Approach 1:
-
Create a model class in domain layer, for example named
ChatItem, containing user name, photo url and unseen message count. -
Create a new
UseCase, for example namedGetChatItems, which uses bothUserRepositoryandChatRepositorymethods internally to build anObservable<List<ChatItem>>. -
Create a model class in presentation layer, for example named
ChatItemModel, corresponding to theChatItemin domain layer. Also create the mapper to transformChatItemtoChatItemModeland adapter class as needed.
Problem: ChatItem seems like a presentation detail and seems out of place inside domain model. Should the domain layer know about how its core models are combined and used in presentation layer?
Approach 2:
-
Do nothing on domain layer.
-
Create a model class in presentation layer, for example named
ChatItemModel, containing user name, photo url and unseen message count. -
Use the existing
UseCases,AllFriendAccountsUseCaseandUnseenMessageCountUseCasein the presentation layer to build anObservable<List<ChatItemModel>>. Also create the adapter class which uses the emitted list.
Problem: Combining UseCases in presentation layer seems ugly.
Looking for suggestions. Is there an approach 3 that I missed completely?
This is a very valid and common scenario. But I prefer approach 2. Presentation layer should handle all presentation logic and I consider your scenario a presentation logic because this is used to display values onto your screen. For me, there's noting wrong in combining multiple use cases in one UI (or presentation). It is very impossible to only have one use case in one UI. So for me, combining use cases in presentation layer should not be a problem.
@mr746866 Get rid of dirty observables away... Use callbacks. Let your presentation layer be object-oriented not procedural. All you need is just a small change in your precious ChatItemModel which is used in presentation-layer. Then, update each of those which has "Unseen-messages" when callback arrives! What if you need fcm to notify your ChatItemModel to display "New arrived messages"? Are you going to merge another Observable into it? or perhaps use a subject? Stop right there. Rx is for streaming not for screaming.
Imho, the first approach is better. The second approach seems to add business logic in presentation layer, which is something that we do not want. If business logic does not live in domain layer then it is not transferable. Also by coupling presentation with business logic, it would be harder to change presentation layer.