Description
Hi @android10,
I have a couple of questions regarding how to pass an object from the presentation layer to the domain layer and then to the data layer.
I have a use case when a user signs up within my app.
-
Pass and object from the domain layer to the data layer:
This is the interface (in the domain layer) between the domain and the data layer:
public interface UserRepository { //The User class belongs to the Domain Layer Observable<User> signUp(User user); }
...and this is the UseCase subclass:
public class SignUpUseCase extends UseCase { // Members set up in the SignUpUseCase constructor private User user; private UserRepository userRepository; @Override protected Observable buildUseCaseObservable() { return userRepository.signUp(user); } }
Now, in the data layer, the implementation of the
UserRepository
interface (of the domain layer) is defined as follows:public class UserDataRepository implements UserRepository { private final UserEntityDataMapper userEntityDataMapper; public Observable<User> signUp(User user){ final UserDataStore userDataStore = userDataStoreFactory.createCloudDataStore(); // Declared variable to make this code easier to read UserEntity transformedUserEntity = userEntityDataMapper.transform(user) return userDataStore.signUp(transformedUserEntity) .map(userEntity -> this.userEntityDataMapper.transform(userEntity)); } }
This takes me to define 2 methods in the
UserEntityDataMapper
class as follows:public User transform(UserEntity userEntity) { ... } public UserEntity transform(User user) { ... }
Is this a correct implementation on how to pass objects from the domain to the data layer?
-
Pass and object from the presentation layer to the domain layer:
For the
SignUpUseCase
use case, I need aUser
(defined in the domain layer) to be supplied via it's constructor in theUserModule
.@Module public class UserModule { //The UserModel class belongs to the Presentation Layer private UserModel userModel; @Provides @PerActivity @Named("signUp") UseCase provideSignUpUseCase(UserRepository userRepository, ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread){ return new SignUpUseCase(userModel, userRepository, threadExecutor, postExecutionThread); } }
But then, I'd need a User mapper to transform a
UserModel
(defined in the presentation layer) to aUser
(defined in the domain layer)Where should I place this User mapper, in the
UserModule
or in theSignUpUseCase
class?
Activity
Trikke commentedon Nov 18, 2015
This is correct, Data receives an object from Domain and maps it to whatever Entity the data implementation expects.
If I am reading it correctly,
UserModule
is the Dagger Injection Module for the User scope, so you should not hold instances of other classes in thatUserModule
. It exists purely to provide instances to inject.You are not obliged to keep to the
protected Observable buildUseCaseObservable()
format, since this makes it hard to use when you have dynamic objects that need to be passed to a UseCase. What i would do is have a method which takes aUser
from the Presentation Layer and have aUserMapper
declared in the Presentation Layer which would transform theUserModel
to aUser
and pass that on to the Domain Layer.So the flow would be
Presentation layer
UserModel
UserModel
is passed to a mapper to convert it to aUser
SignUpUseCase.signUp(user)
is calledDomain layer
SignUpUseCase
receives theUser
UserModel
toUser
and passes it to your ServiceData layer
UserService
receives theUser
User
is used to sign up in the cloud via the ServiceSignUpUseCase
is notifiedDomain layer
SignUpUseCase
processes the result and applies any business logic (let's assume the signup was a success)User
locally, so we pass it to theUserRepository
Data layer
UserRepository
receives the processedUser
User
is transformed toUserEntity
( this could be implementation specific )UserEntity
is saved locally in cache to be retrieved later.I hope this helps a bit.
edit was to reflect remark from @lalongooo made below.
lalongooo commentedon Nov 18, 2015
Thanks @Trikke , I'll give it a try later today and let you know the results.
johnwatsondev commentedon Nov 19, 2015
@lalongooo I have made an example for your need. Hope giving you some help. #55
lalongooo commentedon Nov 19, 2015
@johnwatsondev It's a nice approach when passing a couple of params, but...what if you need to pass an object with a lot of properties/members..?
lalongooo commentedon Nov 19, 2015
@Trikke If I let the
SignUpUseCase
to receive theUsermodel
...wouldn't this be breaking the dependency rule?I mean, the domain layer would be depending on a class of the presentation layer.
Trikke commentedon Nov 20, 2015
@lalongooo yes, i have made a mistake as was a bit too quick and forgot about the boundaries. In the Presentation Layer, you could have a mapper (something like
UserModelMapper
), which takes aUserModel
from the Presenter and maps it to aUser
which the Domain will understand. ThisUser
is then passed as an argument to the UseCase. This way the Domain doesn't know about the data from Presentation, which is correctly converted at the boundary.I'll adjust my comment above to reflect your correct remark.
RamiJemli commentedon Nov 24, 2015
@Trikke @lalongooo @android10 Hi everyone, IMO the implementation doesn't need the
UserModel
(Presentation layer) and it's mapper. The dependency rule states that nothing in an inner circle can know anything at all about something in an outer circle, but, the opposite is possible. I think it's possible to call theUser
(Domain layer) inside the presentation layer.Plus, the
UserModel
andUser
have the same attributes. Technically, it's a dupicate and this is a violation of DRY. please correct me if i'm wrong.android10 commentedon Nov 24, 2015
The only thing I would add here is that a UserRepository should not know anything about signing up users, it is a repository and its responsibility is to work with data sources. The login process is part of your domain and the repository will "save" user session.
zhengxiaopeng commentedon Nov 25, 2015
I think the
UserModel
andmapper
is mechanical and dogmatic. If the user's data has changed(eg: add age attribute), you must change youruser
(UserMode-Presentation、User-Domain、UserEntity-Data) and itsmapper
in three layers,its unacceptable!In MVP pattren, the model is an interface defining the data to be displayed or otherwise acted upon in the user interface. (Considering the layered architecture) Model is the embodiment of the data and business, and from the next layer of dependence. We can make it simple and dependency rules are correct, not so many template code(“user” and its "mapper").
spirosoik commentedon Nov 25, 2015
@android10 You mean with "save" user session the request to the cloud but the domain model would create for example the preferences session for the user (login process)
android10 commentedon Nov 25, 2015
@spirosoik yes. My comment was mostly a naming convention. A repository abstracts the origin of your data and should not know anything about login any user :)
spirosoik commentedon Nov 25, 2015
@android10 exactly. Thanks great 👍 . By the way frodo rocks
android10 commentedon Nov 25, 2015
@spirosoik 😄 Thank you!
6 remaining items
android10 commentedon Nov 25, 2015
@zhengxiaopeng what is your solution then?
zhengxiaopeng commentedon Nov 26, 2015
@android10 :-) I am cognizing this question. The following is my view now:
"The models are likely just data structures that are passed from the controllers to the use cases, and then back from the use cases to the presenters and views."
- The Clean Architecture, andRequest model
、Response model
- Robert C Martin - Clean Architecture(42:46). So, remove the user's classes in presentation layer and then put them into domain layer. Mostly, one user model is enough.android10 commentedon Nov 26, 2015
I'm a strong believer of having View Models and each layer should have its model to deal with. So I avoid coupling between layers. If you share Domain models with UI models, you are breaking the dependency rule because Domain knowing something about outer layers in the circle:
You will have to make changes anyway when adding new information to your UI.
zhengxiaopeng commentedon Nov 27, 2015
Alright, I agree the dependency rule. I want to add that we(Android Dev) are just writing a single application, we don't have Front-end developer and Backend developer and so on. All codes(layers) are transparent to us。
RamiJemli commentedon Nov 27, 2015
@zhengxiaopeng I agree with you. @android10 Your great work got me interested in Clean architechture, so i looked deeper into this. When you call the domain model inside the presentation layer, it's the outer circle knowing something about the inner circle. This doesn't violate the dependency rule, but the opposite does.
android10 commentedon Nov 27, 2015
@RamiJemli yup
MehdiChouag commentedon Nov 27, 2015
Hello,
I don't if my question should be asked here (tell me if I'm wrong). My question is about using dynamic parameters in a use case, like @lalongooo I have a
SignUpUseCase
, and this use case need user's email and password.If I take
UserModule
that have the responsibility to createGetUserDetails
an Id is needed to get a specific user, And this Id is specified whenUserModule
is created.In my case when
SignModule
is created I don't already know the user's password and email, so I can't pass it through the constructor.So how can I proceed to pass, user's email and password to my use case when the user hit the button create an account ?
lalongooo commentedon Nov 27, 2015
This may help you @MehdiChouag #32
MehdiChouag commentedon Nov 27, 2015
I'll take a look at this thank you 😄
guliash commentedon Dec 19, 2016
What do you do when you have domain specific data? For example domain level entities have some metadata, while UI level does not need it. How do you save that metadata to be able recreate entity from UI level model?
lbensaad commentedon Apr 15, 2017
Very interesting things going here.
Although this started a couple of years ago, I hope that some of you will comment on this:
What do you thing of making the DomainUser extends DataUser, and the PresentationUser extends DomainUser. Like this the Domain Layer can add more fields and functionalities without the Data Layer knowing about these additions. Also, the Presentation Layer will add functionalities and the Domain Layer will not know about them. This will not break the Clean dependency rule as inner layers are not aware the extends made by outer layers.
kevin-barrientos commentedon May 31, 2017
@lbensaad yes it would be breaking the dependency rule. DomainUser should not know about DataUser.
mishkaowner commentedon Nov 29, 2018
@ghost I have tried all three options you have mentioned! And I realized 2 is the simplest way. but that still has problems...according to bob's clean architecture, we must create a response model for presenter not DomainModel itself. And we must not send DomainModel to UseCase but Request model...so yea...