Nice video! Just one thing to note: in the first mistake, I would rather use a custom Regular Expression to validate the email instead of relying on the given implementation. Why? I want to know exactly what's going on. Furthermore, since Regular Expressions are usually part of any programming language, it's safe to use it in the domain layer, as it won't be dependent of anything other than the language itself. About the last mistake, I don't consider it a mistake. It depends of the project size, and the chosen approach. If you are dealing with mono-repo, then it will be an issue. But if you are using multi-repo, micro-frontends, or something like that, you probably will make this mistake, but since everything is very separated, it's not a problem
PROPER Regex is incredibly difficult to write and maintain lol (most codebases that do this just go with a contains([@.]]-approach. There's absolutely no shame in using a validator library
Separating project into features is not a mistake, it's a decision, and has nothing to do with Clean Architecture. Actually, uncle Bob never spoke about that in the book, and this is applied to any architecture you use. I used to always take the feature oriented approach, but in the last project I've been working on, we decided to go with "layer-oriented" because of how coupled the data was. The thing with the feature oriented is that you are splitting your app in mini parts. Ideally, these parts must be independent, so if two features share some logic, where you should put that logic? This absolutely should not go to core/common folder, since this is not something to be shared with the entire app, it's only meant to be shared with these two features. You either go to "mirroring" the data on the other feature, or importing from one feature to another. The price to pay for either of these, in some projects, can be bigger than having these centralized.
I very like separate project into features-modules, and i think that it is the best way for clear, convenient and easily expandable architecture of Android App) yeap, i have read your comment, and understand that we can use it in any project, not only Android, but i have experience only in Android and KMP) For your problem sharing of data between 2/3/4 (as u want) modules u can use next way: in my project i use 2 modules - api and impl inside feature-modules, all private logic i put in impl module (repositories and usecases implementations, screens, viewModels, uiModels and etc.. Also i put here DI and retrofit Api Interfaces, but it depends on situation and di libraries for example). In Api module i put only data for sharing. For example: UseCases and Repository Interfaces if i need to share it, maybe data layers mappers if need(more often not), maybe sharing constants, domain models for sharing usecases and screen parameters, which i need to get for screen start (for example id of user). In this case any your feature module has dependencies only for api modules of other feature-modules and knows nothing about feature-impl modules. And your Api module is really small and light. And in this way you can implement your feature-api module only in those modules what u need. Also this way help your improve building time, cause u have dependencies only for abstractions or rarely changeble classes as ScreenParams, and it means that u feature-module2 with dependency feature-module2-api will not rebuild after each changes in feature-module2-impl architecture scheme looks like: --feature-module ----api ------data ------domain ------presentation(here presentation layer needs me only for sharing of screen parameters if it's neccesary) ----impl ------data ------di ------domain ------presentation
@@okunevdmitrii Your approach surely works, but to me it looks hell of a complication! Having all modules having a separate part declaring all their shared classes as interfaces so that they can be injected by DI into sibling modules makes quite hard to figure out how the whole project is structured as it scales. I'm sure most projects can be organised fully in feature modules like Phillip described but in the ones I happened to work, backed provided those very large APIs that returned very interconnected data, and that kind of approach never worked. What I found is best in those situations is just to consider your "app" just the presentation layer, and divide it in feature modules accordingly, with just one module providing shared UI resources. Domain and Data can be while modules that behave like libraries for your UI, data providing repositories that internally manage data sources and domain exposing use cases and models. This also allows me to organise the three layers differently: for example, the division in features that makes sense on the UI level might not be very relevant to the domain layer that could instead group use cases by area of influence, and the data layer could group repositories and data sources around the actual Apis provided by the backend. Forcing a singular organisation vertically for the whole app rarely comes natural.
@@Drackmord92 that last approach is just what we've been doing in our last project. But that doesn't mean that if you want to have domain/repositories/all-your-repositories it's a mistake. At the end of the day, it would be exactly the same code (same clean Architecture guidelines), with a very subjective way of organizing how your team edit and visualize it. You can think this as a flatten version of feature oriented, which can be easier to read depending on the team or the project. Don't take me wrong, I will always prefer the feature oriented version, but is not a mistake by any means doing it the other way. Also Philipp, keep going with your videos mate, you are doing awesome!
Tbh, if a 'feature' can operate independently from the other 'feature' u should probably put it in its own module/package. I've never made a native android app but worked with other frameworks (Flutter). Feature is something to figure out for later, because it forces you to think about what will work with what en where. You could say: plan better, but in my experience plans will always change in development because of unforeseen problems. The argument: "it's better because all the related files are grouped together" is non-sense, because with most IDE's you can just click on the import to jump to the source.
I tried the feature-approach as well, and I "solved" your problem by having a 'shared' package with data, doman, and presentation. Problem was though, I ended up puttin so much into the shared-package, that it sort of negated the feature-approach lol
Thank you, Philipp. I like your take on clean architecture. I just want to know best practices beforehand to not stumble upon scaling difficulties later on.
Moral of the first 'mistake': "You don't eliminate the complexity, you just push it around" ☺ (I don't mean Philipp obviously, just the saying goes...)
Where do i put alarm manager in clean architecture. I have to perform some business logic in broadcast receiver (where should i put broadcast receiver in clean archotecture). can I access usecase in data layer.
For the second point. I guess you do not know/use MapStruct library. I'm always declare the mapper as an interface and the library gonna generate mapper implementation code for me. interface DtoMapper { fun toDomain(dto: DTO): DOMAIN } @Mapper interface BookMapper: DtoMapper Declare mapper as interface is really effective because the implementation of mapper can be change by business logic/rule anytime. When combination with Dagger we will @provide mapper, it can be injected to many class, I just need to re-provide the mapper implementation.
Great video as always. I take security very serious so, I would be thankful, if you could do a lock screen app. Maybe with a pin/password/fingerprint? Would be awesome!
Why do we even have to create a book mapper class ? Just open the body of the data class and write all the related functions in there. Or just simply make an extension function below that class.
According second mistake it is not a mistake. For example if you need to write fake mapper for unit test you need this abstraction. And you have 2 implementation.
Great video whatsoever. Claim about "if there is no second implementation - throw abstraction away" is to strong. You, probably won't have second implementation for that pattern validator. So next to multiple implementations one should always remember about testing and how abstraction make it easier. Second thing I don't agree is that data-domain-presentation does not scale. Just use feature packages inside those layer packages. I have seen both approaches and in wrong hands both could lead to a disaster.
Regarding the second thing, that's similar to what I suggested in the video. I was referring to people who use presentation, domain and data packages and then don't use features in these. That's quite common
@@PhilippLackner To be absolutely honest your approach kinda better. For me having data-domain-presentation in this order from top to bottom in package manager is more menthal thing, carried from times before clean architecture and even Android development.
They exist for different purposes. Repository works with data and that's it. Use-cases contain valuable business logic. As an abstract example, it may get data from one repository, then pass its output to another repository and in the end aggregate it with another data. If use-cases just proxy repository calls, you should reconsider their necessity. View-model maps data from use-cases to UI-friendly structures and receives UI events. These all are different cases for testing. In repository you test correctness of data logic. In use-case you test correctness of business logic. And in view-model you test correctness of presentation logic.
@@vasiliychernov2123 "In use-case you test correctness of business logic" - so by what you say I see that use-case testing is indeed necessary, which goes against what Philip said in the video
Hey, just curious why you had to implement the validator in data layer. If I understand correctly, the data layer should only focus on things that deal with raw data. But in this case, validator just takes a string and returns a boolean, which doesn't really deal with data.
Hey Phillip. I just implemented a list-detail view in compose for large screens. Would love to see how you would implement it. There aren't many examples out there
Can anyone give a tip on how to do 10,000 mathematical calculations without freezing the Android activity screen? I have no idea what to use as a clean architecture if the numeric data comes from a static json.
is clean architecture absolute necessary? I don't work as an android developer as a job, rather I only develop apps on my spare time. So should I be extra careful with these things? because for me some of these "clean architecture" looks like a lot of extra code for the simplest of things. Is there any performace boast with these methods?
It's not necessary, but it's easier to maintain your app and add new features. You won't gain any performance boost though or might be an overkill if your app is very simple.
It is not. I'm pretty sure even Google says domain layer is optional. If the only thing your use cases do is call 1 method from your repository, then you should throw them away. If you don't even plan to write unit tests, you probably don't even need to abstract your repositories. Clean Architecture is not really a concept from Android, it's used across all domains (iOS, Web, Backend, etc) and it only matters when you have scalability in mind. That said, IMO even on small apps, it's probably still not too good of an idea to put everything in your activities tho...
Why ```interface Mapper { fun toDomain(data: Data): DOMAIN fun toData(domain: DOMAIN): DATA }``` is bad solution? If u need to cover 100% unit tests for example.
You said don't abstract usecase. If we want to test the usecase we can use fake repository and test it. Now we will use viewmodel which have usecase as parameter now when we want to test the viewmodel how can I send the usecase Instance. If we make usecase as abstraction we can pass fake usecase. Correct me if I am wrong?
@Philipp Lackner If we didn't abstract usecase then all usecase will be tightly coupled in view model right doesn't it break the DIP principle. Also if we want to test the viewmodel which contains the actual implementation of usecase how you will test it. My suggestion would be to upload a separate video describing this will be helpful for us. Thank-you
@@PhilippLacknerIn the context of use cases, it may be necessary to create a mock implementation of a use case in order to test the code that calls it. By creating an interface for the use case and passing in a mock implementation during testing, you can test the code that calls the use case in isolation, without having to worry about the implementation details of the use case itself.
Hey Phillip, I agree with you on the first two points but as for your package structure, I personally disagree. Because your package structure can be just as quickly confusing and above all, you should clearly separate the logic from the UI, even within the package. But in the end, it's a personal feeling how you want something to be better organized. I can send you our package structure as a picture on Instagram if you like. It is a mixture of your first and second variant.
6:10 "An abstraction only make sense if you actually have multiple implementation ... if you only have one implementation you can simply throw it away" I'm really surprised with this clear and direct statement as it goes against DIP principle and as uncle bob and others have said : "Somehow developers think that you should have an interface “only in the case of multiple implementations”. This couldn’t be more wrong. " Interface in clean architecture are aimed to control flow of dependency and they help us in isolating changes, and avoiding to propagate them everywhere. Their only purpose is not to have multiple implementations and even more when working with clean arch. Would love to hear your pov if i misunderstood your statement.
In my experience, it very quickly leads to over engineering if you abstract everything while it comes with pretty much no benefit. You either want multiple implementations or really need to abstract stuff to use it in domain
In (1) the interface `EmailPatternValidator` is in the domain layer and the implementation is in the data layer. Doesn't that make the data layer depend on the domain layer which creates a circular dependency now? I would keep both the interface and implementation in the data layer itself, under `data/util`. Any other options?
@@anegine The use-cases in the domain layer can access the repositories in the data layer and now both the domain and data layers are depending both ways right?
@@VishnuHaridas Domain layer MUST NOT depend on data layer. Domain layer contains Repository interface. Data layer contains Repository implementation, which is injected to domain UseCase (with DI / ServiceLocator or any other way).
@@anegine Oh deja-vu! I have seen similar conversations before when Google introduced the "layered" architecture where domain depends on data layer and not the other way around. I think it will be good to clearly specify that this is not the official "layered" architecture recommendation when making videos on Clean architecture on Android, otherwise new developers can get it mixed up.
@@inertia_dagger First thank you for your answer , Yes that's what im using (interfaces) but i was asking about to add Hilt dependency in the domain Module.
@@boukarradhmoez99 Repositories are defined as an abstraction so you can have 2 different implementations - 1) the entity, which is the real implementation (which is most likely done in data layer) and 2) usually mocks for your test cases. On Philips' example, he didn't need to create 2 different implementations of a use case, because the the actual implementation of the use case, the business logic itself is what he wants to test. If you're wondering why don't we test the entity of repositories - it's simply because it has direct correlation with data (retrofit responses, rpc calls, room, cache, online configs, whatever) and you don't really care about that in your tests. Another reason is you obviously don't want to actually hit an API on your test cases
Let's be honest, mistake #1 is only a mistake because Android is designed badly. Ideally you should be able to unit test the original code and shouldn't need to abstract it (like the whole mistake #2 states). In Java days, Pair was super useful class, very generic, but nope, no testability. 👎
These mistakes are definitely what developers who study CleanArchitecture at first could do. Thank you so much. this is very useful! Keep going bro!
Glad it was helpful!
Nice video! Just one thing to note: in the first mistake, I would rather use a custom Regular Expression to validate the email instead of relying on the given implementation. Why? I want to know exactly what's going on. Furthermore, since Regular Expressions are usually part of any programming language, it's safe to use it in the domain layer, as it won't be dependent of anything other than the language itself.
About the last mistake, I don't consider it a mistake. It depends of the project size, and the chosen approach. If you are dealing with mono-repo, then it will be an issue. But if you are using multi-repo, micro-frontends, or something like that, you probably will make this mistake, but since everything is very separated, it's not a problem
PROPER Regex is incredibly difficult to write and maintain lol (most codebases that do this just go with a contains([@.]]-approach. There's absolutely no shame in using a validator library
Not a android developer, but this guy's view on Clean Architecture™, unlike many other random RUclips videos, is correct
Separating project into features is not a mistake, it's a decision, and has nothing to do with Clean Architecture. Actually, uncle Bob never spoke about that in the book, and this is applied to any architecture you use. I used to always take the feature oriented approach, but in the last project I've been working on, we decided to go with "layer-oriented" because of how coupled the data was.
The thing with the feature oriented is that you are splitting your app in mini parts. Ideally, these parts must be independent, so if two features share some logic, where you should put that logic? This absolutely should not go to core/common folder, since this is not something to be shared with the entire app, it's only meant to be shared with these two features. You either go to "mirroring" the data on the other feature, or importing from one feature to another. The price to pay for either of these, in some projects, can be bigger than having these centralized.
I very like separate project into features-modules, and i think that it is the best way for clear, convenient and easily expandable architecture of Android App) yeap, i have read your comment, and understand that we can use it in any project, not only Android, but i have experience only in Android and KMP)
For your problem sharing of data between 2/3/4 (as u want) modules u can use next way: in my project i use 2 modules - api and impl inside feature-modules, all private logic i put in impl module (repositories and usecases implementations, screens, viewModels, uiModels and etc.. Also i put here DI and retrofit Api Interfaces, but it depends on situation and di libraries for example). In Api module i put only data for sharing. For example: UseCases and Repository Interfaces if i need to share it, maybe data layers mappers if need(more often not), maybe sharing constants, domain models for sharing usecases and screen parameters, which i need to get for screen start (for example id of user). In this case any your feature module has dependencies only for api modules of other feature-modules and knows nothing about feature-impl modules. And your Api module is really small and light.
And in this way you can implement your feature-api module only in those modules what u need. Also this way help your improve building time, cause u have dependencies only for abstractions or rarely changeble classes as ScreenParams, and it means that u feature-module2 with dependency feature-module2-api will not rebuild after each changes in feature-module2-impl
architecture scheme looks like:
--feature-module
----api
------data
------domain
------presentation(here presentation layer needs me only for sharing of screen parameters if it's neccesary)
----impl
------data
------di
------domain
------presentation
@@okunevdmitrii Your approach surely works, but to me it looks hell of a complication! Having all modules having a separate part declaring all their shared classes as interfaces so that they can be injected by DI into sibling modules makes quite hard to figure out how the whole project is structured as it scales.
I'm sure most projects can be organised fully in feature modules like Phillip described but in the ones I happened to work, backed provided those very large APIs that returned very interconnected data, and that kind of approach never worked.
What I found is best in those situations is just to consider your "app" just the presentation layer, and divide it in feature modules accordingly, with just one module providing shared UI resources. Domain and Data can be while modules that behave like libraries for your UI, data providing repositories that internally manage data sources and domain exposing use cases and models.
This also allows me to organise the three layers differently: for example, the division in features that makes sense on the UI level might not be very relevant to the domain layer that could instead group use cases by area of influence, and the data layer could group repositories and data sources around the actual Apis provided by the backend.
Forcing a singular organisation vertically for the whole app rarely comes natural.
@@Drackmord92 that last approach is just what we've been doing in our last project. But that doesn't mean that if you want to have domain/repositories/all-your-repositories it's a mistake. At the end of the day, it would be exactly the same code (same clean Architecture guidelines), with a very subjective way of organizing how your team edit and visualize it. You can think this as a flatten version of feature oriented, which can be easier to read depending on the team or the project.
Don't take me wrong, I will always prefer the feature oriented version, but is not a mistake by any means doing it the other way.
Also Philipp, keep going with your videos mate, you are doing awesome!
Tbh, if a 'feature' can operate independently from the other 'feature' u should probably put it in its own module/package. I've never made a native android app but worked with other frameworks (Flutter). Feature is something to figure out for later, because it forces you to think about what will work with what en where. You could say: plan better, but in my experience plans will always change in development because of unforeseen problems.
The argument: "it's better because all the related files are grouped together" is non-sense, because with most IDE's you can just click on the import to jump to the source.
I tried the feature-approach as well, and I "solved" your problem by having a 'shared' package with data, doman, and presentation. Problem was though, I ended up puttin so much into the shared-package, that it sort of negated the feature-approach lol
Make a Job scheduler video,
How to create jobs and how to control the Jobs
Yeayyy that good, i learn about clean architecture. But I do that mistake 😢, thank for the knowledge, i hope you always good, i open for feedback ☺️
I also
Thank you, Philipp. I like your take on clean architecture. I just want to know best practices beforehand to not stumble upon scaling difficulties later on.
Awesome knowledge Philipp ❤
Moral of the first 'mistake': "You don't eliminate the complexity, you just push it around" ☺ (I don't mean Philipp obviously, just the saying goes...)
Thank you so much. I learn something new from you every time
Where do i put alarm manager in clean architecture. I have to perform some business logic in broadcast receiver (where should i put broadcast receiver in clean archotecture). can I access usecase in data layer.
Amazing video, and as I noticed, nowinandroid is also a feature-based modular project and it kind of matches the example 3.
For the second point. I guess you do not know/use MapStruct library.
I'm always declare the mapper as an interface and the library gonna generate mapper implementation code for me.
interface DtoMapper {
fun toDomain(dto: DTO): DOMAIN
}
@Mapper
interface BookMapper: DtoMapper
Declare mapper as interface is really effective because the implementation of mapper can be change by business logic/rule anytime.
When combination with Dagger we will @provide mapper, it can be injected to many class, I just need to re-provide the mapper implementation.
nice
Why inject it if you can just make it an extension function to not need to inject it anywhere 😅 you don't gain anything with that interface
Great video as always. I take security very serious so, I would be thankful, if you could do a lock screen app. Maybe with a pin/password/fingerprint? Would be awesome!
Many thanks for sharing your experiences from your code reviews. I appreciate it very much.
Thank you Philip. This was helpful
Finna waiting for this.
More videos like this please❤
I already did watch this video about 3~4 times. And I forget 3 mistakes again. I watch this video now again.
I like concept explainer videos like these
Thank you 😊
Why do we even have to create a book mapper class ? Just open the body of the data class and write all the related functions in there. Or just simply make an extension function below that class.
Extension function is better to keep responsibilities separately
According second mistake it is not a mistake. For example if you need to write fake mapper for unit test you need this abstraction. And you have 2 implementation.
Why would you need a fake mapper?
to test something
just amazing as usual
Please I need a tutorial about firebase firestore and clean architiecture and retrofit, caching with jetpack compose
Great video whatsoever. Claim about "if there is no second implementation - throw abstraction away" is to strong. You, probably won't have second implementation for that pattern validator. So next to multiple implementations one should always remember about testing and how abstraction make it easier. Second thing I don't agree is that data-domain-presentation does not scale. Just use feature packages inside those layer packages. I have seen both approaches and in wrong hands both could lead to a disaster.
Regarding the second thing, that's similar to what I suggested in the video. I was referring to people who use presentation, domain and data packages and then don't use features in these. That's quite common
@@PhilippLackner To be absolutely honest your approach kinda better. For me having data-domain-presentation in this order from top to bottom in package manager is more menthal thing, carried from times before clean architecture and even Android development.
7:57 Can you please elaborate on that - what is the difference of testing the usecase layer than for example testing a repository or a viewmodel?
They exist for different purposes. Repository works with data and that's it. Use-cases contain valuable business logic. As an abstract example, it may get data from one repository, then pass its output to another repository and in the end aggregate it with another data. If use-cases just proxy repository calls, you should reconsider their necessity. View-model maps data from use-cases to UI-friendly structures and receives UI events. These all are different cases for testing. In repository you test correctness of data logic. In use-case you test correctness of business logic. And in view-model you test correctness of presentation logic.
@@vasiliychernov2123 "In use-case you test correctness of business logic" - so by what you say I see that use-case testing is indeed necessary, which goes against what Philip said in the video
@@alonshlider4881 Uhm, Philip literally said that use-cases are being tested.
Thanks Philipp
I just had to use other feature's dependency. Seems like I need to put them in core then.
If you have only one database within your app, you would put the database in the core directory right?
Hey, just curious why you had to implement the validator in data layer. If I understand correctly, the data layer should only focus on things that deal with raw data. But in this case, validator just takes a string and returns a boolean, which doesn't really deal with data.
Data layer deals with SDKs and APIs. The Android SDK is therefore partly data related
@@PhilippLackner alright, gotcha!
About mistake #3 - if I have use case which I use in many features? Where I should hold this use case?
in the core package
Hey Phillip. I just implemented a list-detail view in compose for large screens. Would love to see how you would implement it. There aren't many examples out there
Any example of unit test in MVVM clean architecture with kotlin flow & channels
Hi Philipp, Please do full video related to SOLID principles with examples.
🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝
*Thanks for watching you have been selected among the lucky winners, inbox* MOGSWAMP
🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝
please make a basic video on how to use camerax in compose i am little bit confused
I love you man
Hi, would you like to make a tutorial about the state holder class and how can we take benefit from the class!
🔝🔝🔝🔝🔝🔝🔝🔝
*Thanks for watching you have been selected among the lucky winners, inbox* MOGSWAMP
🔝🔝🔝🔝🔝🔝🔝🔝
@@Mogswamp- 🤣🤣
@@Mogswamp-
@PhilipLackner just see this haha
Can anyone give a tip on how to do 10,000 mathematical calculations without freezing the Android activity screen? I have no idea what to use as a clean architecture if the numeric data comes from a static json.
is clean architecture absolute necessary? I don't work as an android developer as a job, rather I only develop apps on my spare time. So should I be extra careful with these things? because for me some of these "clean architecture" looks like a lot of extra code for the simplest of things. Is there any performace boast with these methods?
It's not necessary, but it's easier to maintain your app and add new features. You won't gain any performance boost though or might be an overkill if your app is very simple.
@@jarkow Thank you
It is not. I'm pretty sure even Google says domain layer is optional. If the only thing your use cases do is call 1 method from your repository, then you should throw them away. If you don't even plan to write unit tests, you probably don't even need to abstract your repositories. Clean Architecture is not really a concept from Android, it's used across all domains (iOS, Web, Backend, etc) and it only matters when you have scalability in mind.
That said, IMO even on small apps, it's probably still not too good of an idea to put everything in your activities tho...
It's definitely not necessary
Why ```interface Mapper {
fun toDomain(data: Data): DOMAIN
fun toData(domain: DOMAIN): DATA
}```
is bad solution? If u need to cover 100% unit tests for example.
You said don't abstract usecase. If we want to test the usecase we can use fake repository and test it. Now we will use viewmodel which have usecase as parameter now when we want to test the viewmodel how can I send the usecase Instance. If we make usecase as abstraction we can pass fake usecase. Correct me if I am wrong?
Why would you want to pass fake business logic? What's the point? Use cases are always isolated logic, there's zero reasons to abstract that
@Philipp Lackner If we didn't abstract usecase then all usecase will be tightly coupled in view model right doesn't it break the DIP principle. Also if we want to test the viewmodel which contains the actual implementation of usecase how you will test it. My suggestion would be to upload a separate video describing this will be helpful for us. Thank-you
@@PhilippLacknerIn the context of use cases, it may be necessary to create a mock implementation of a use case in order to test the code that calls it. By creating an interface for the use case and passing in a mock implementation during testing, you can test the code that calls the use case in isolation, without having to worry about the implementation details of the use case itself.
Any update regarding my query?
Hey Phillip,
I agree with you on the first two points but as for your package structure, I personally disagree.
Because your package structure can be just as quickly confusing and above all, you should clearly separate the logic from the UI, even within the package.
But in the end, it's a personal feeling how you want something to be better organized. I can send you our package structure as a picture on Instagram if you like. It is a mixture of your first and second variant.
6:10 "An abstraction only make sense if you actually have multiple implementation ... if you only have one implementation you can simply throw it away"
I'm really surprised with this clear and direct statement as it goes against DIP principle and as uncle bob and others have said :
"Somehow developers think that you should have an interface “only in the case of multiple implementations”.
This couldn’t be more wrong. "
Interface in clean architecture are aimed to control flow of dependency and they help us in isolating changes, and avoiding to propagate them everywhere.
Their only purpose is not to have multiple implementations and even more when working with clean arch.
Would love to hear your pov if i misunderstood your statement.
In my experience, it very quickly leads to over engineering if you abstract everything while it comes with pretty much no benefit. You either want multiple implementations or really need to abstract stuff to use it in domain
thx :3
In (1) the interface `EmailPatternValidator` is in the domain layer and the implementation is in the data layer. Doesn't that make the data layer depend on the domain layer which creates a circular dependency now?
I would keep both the interface and implementation in the data layer itself, under `data/util`. Any other options?
data layer depends on domain layer - it's ok. domain layer doesn't depend on any module (except any other domain layers)
@@anegine The use-cases in the domain layer can access the repositories in the data layer and now both the domain and data layers are depending both ways right?
@@VishnuHaridas Domain layer MUST NOT depend on data layer. Domain layer contains Repository interface. Data layer contains Repository implementation, which is injected to domain UseCase (with DI / ServiceLocator or any other way).
@@anegine Oh deja-vu! I have seen similar conversations before when Google introduced the "layered" architecture where domain depends on data layer and not the other way around.
I think it will be good to clearly specify that this is not the official "layered" architecture recommendation when making videos on Clean architecture on Android, otherwise new developers can get it mixed up.
Thank you
🔝🔝🔝🔝🔝🔝🔝🔝
*Thanks for watching you have been selected among the lucky winners, inbox* MOGSWAMP
🔝🔝🔝🔝🔝🔝🔝🔝
very cool video
Ok that's a good approach but what about the cause when I need Dagger Hilt to inject my repos in my use cases ?
if you're going to test your use cases, inject repos as interfaces and mock those interfaces in tests
@@inertia_dagger First thank you for your answer ,
Yes that's what im using (interfaces) but i was asking about to add Hilt dependency in the domain Module.
@PhilippLackner 🤔
@@boukarradhmoez99 I didn't understand your question. Rephrase it, please?
@@boukarradhmoez99 Repositories are defined as an abstraction so you can have 2 different implementations - 1) the entity, which is the real implementation (which is most likely done in data layer) and 2) usually mocks for your test cases. On Philips' example, he didn't need to create 2 different implementations of a use case, because the the actual implementation of the use case, the business logic itself is what he wants to test.
If you're wondering why don't we test the entity of repositories - it's simply because it has direct correlation with data (retrofit responses, rpc calls, room, cache, online configs, whatever) and you don't really care about that in your tests. Another reason is you obviously don't want to actually hit an API on your test cases
👍
Isn't #3 the mistake you use in almost all of your long play video tutorials of Clean Architecture?
top
Let's be honest, mistake #1 is only a mistake because Android is designed badly. Ideally you should be able to unit test the original code and shouldn't need to abstract it (like the whole mistake #2 states).
In Java days, Pair was super useful class, very generic, but nope, no testability. 👎