The Top 3 Clean Architecture Mistakes in Android

Поделиться
HTML-код
  • Опубликовано: 27 ноя 2024

Комментарии • 99

  • @keepgoingman5829
    @keepgoingman5829 Год назад +17

    These mistakes are definitely what developers who study CleanArchitecture at first could do. Thank you so much. this is very useful! Keep going bro!

  • @pedrolemoz
    @pedrolemoz Год назад +12

    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

    • @TheMikkelet
      @TheMikkelet Год назад

      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

  • @chauchau0825
    @chauchau0825 Год назад

    Not a android developer, but this guy's view on Clean Architecture™, unlike many other random RUclips videos, is correct

  • @yarn-rdgz
    @yarn-rdgz Год назад +32

    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.

    • @okunevdmitrii
      @okunevdmitrii Год назад +1

      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

    • @Drackmord92
      @Drackmord92 Год назад

      @@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.

    • @yarn-rdgz
      @yarn-rdgz Год назад

      ​@@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!

    • @rickbo5858
      @rickbo5858 Год назад +1

      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.

    • @TheMikkelet
      @TheMikkelet Год назад +1

      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

  • @youtubethoughts4273
    @youtubethoughts4273 Год назад +4

    Make a Job scheduler video,
    How to create jobs and how to control the Jobs

  • @baharudinmaulana78
    @baharudinmaulana78 Год назад +3

    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 ☺️

  • @go_better
    @go_better Год назад

    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.

  • @kyeiiih4422
    @kyeiiih4422 Год назад +1

    Awesome knowledge Philipp ❤

  • @genctasbasi
    @genctasbasi Год назад +1

    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...)

  • @MrSorenstar
    @MrSorenstar Год назад

    Thank you so much. I learn something new from you every time

  • @Akshaykumar-xr9yj
    @Akshaykumar-xr9yj Год назад +1

    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.

  • @amirhosseinghafoorian5256
    @amirhosseinghafoorian5256 Год назад

    Amazing video, and as I noticed, nowinandroid is also a feature-based modular project and it kind of matches the example 3.

  • @breakeract796
    @breakeract796 Год назад +2

    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.

    • @samdroid37
      @samdroid37 Год назад +1

      nice

    • @PhilippLackner
      @PhilippLackner  Год назад +3

      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

  • @Leon-un2ii
    @Leon-un2ii Год назад

    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!

  • @mircodev
    @mircodev Год назад

    Many thanks for sharing your experiences from your code reviews. I appreciate it very much.

  • @denisoluka
    @denisoluka Год назад

    Thank you Philip. This was helpful

  • @denisgithuku8563
    @denisgithuku8563 Год назад

    Finna waiting for this.

  • @rahul_spawar
    @rahul_spawar Год назад

    More videos like this please❤

  • @riyupapa39
    @riyupapa39 Год назад

    I already did watch this video about 3~4 times. And I forget 3 mistakes again. I watch this video now again.

  • @FemiOkedey
    @FemiOkedey Год назад

    I like concept explainer videos like these
    Thank you 😊

  • @mymobile550
    @mymobile550 Год назад +2

    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.

    • @PhilippLackner
      @PhilippLackner  Год назад

      Extension function is better to keep responsibilities separately

  • @kamanchomorgan9655
    @kamanchomorgan9655 Год назад +1

    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.

  • @Ben-wx1ln
    @Ben-wx1ln Год назад

    just amazing as usual

  • @adamibraham
    @adamibraham Год назад +1

    Please I need a tutorial about firebase firestore and clean architiecture and retrofit, caching with jetpack compose

  • @Shakenbeer
    @Shakenbeer Год назад

    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.

    • @PhilippLackner
      @PhilippLackner  Год назад +1

      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

    • @Shakenbeer
      @Shakenbeer Год назад

      @@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.

  • @alonshlider4881
    @alonshlider4881 Год назад +3

    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?

    • @vasiliychernov2123
      @vasiliychernov2123 Год назад +1

      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.

    • @alonshlider4881
      @alonshlider4881 Год назад

      ​@@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

    • @vasiliychernov2123
      @vasiliychernov2123 Год назад

      @@alonshlider4881 Uhm, Philip literally said that use-cases are being tested.

  • @johndominicjasmin
    @johndominicjasmin Год назад

    Thanks Philipp

  • @Zeeshan-Syed
    @Zeeshan-Syed Год назад

    I just had to use other feature's dependency. Seems like I need to put them in core then.

  • @chair_smesh
    @chair_smesh Год назад

    If you have only one database within your app, you would put the database in the core directory right?

  • @karthikgaddam4
    @karthikgaddam4 Год назад +1

    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.

    • @PhilippLackner
      @PhilippLackner  Год назад +1

      Data layer deals with SDKs and APIs. The Android SDK is therefore partly data related

    • @karthikgaddam4
      @karthikgaddam4 Год назад +1

      @@PhilippLackner alright, gotcha!

  • @pavlosoia
    @pavlosoia Год назад +1

    About mistake #3 - if I have use case which I use in many features? Where I should hold this use case?

    • @kriisEU
      @kriisEU Год назад +2

      in the core package

  • @ntikomathaba2662
    @ntikomathaba2662 Год назад

    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

  • @dhruvreyansh1338
    @dhruvreyansh1338 Год назад

    Any example of unit test in MVVM clean architecture with kotlin flow & channels

  • @dayakarpuli2307
    @dayakarpuli2307 Год назад

    Hi Philipp, Please do full video related to SOLID principles with examples.

    • @Mogswamp-
      @Mogswamp- Год назад

      🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝
      *Thanks for watching you have been selected among the lucky winners, inbox* MOGSWAMP
      🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝🔝

  • @harshpratapsingh1638
    @harshpratapsingh1638 Год назад

    please make a basic video on how to use camerax in compose i am little bit confused

  • @thecoderui
    @thecoderui Год назад

    I love you man

  • @khapp7821
    @khapp7821 Год назад

    Hi, would you like to make a tutorial about the state holder class and how can we take benefit from the class!

    • @Mogswamp-
      @Mogswamp- Год назад

      🔝🔝🔝🔝🔝🔝🔝🔝
      *Thanks for watching you have been selected among the lucky winners, inbox* MOGSWAMP
      🔝🔝🔝🔝🔝🔝🔝🔝

    • @khapp7821
      @khapp7821 Год назад

      @@Mogswamp- 🤣🤣

    • @khapp7821
      @khapp7821 Год назад

      @@Mogswamp-
      @PhilipLackner just see this haha

  • @lglf77
    @lglf77 Год назад

    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.

  • @rma1563
    @rma1563 Год назад

    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?

    • @jarkow
      @jarkow Год назад +2

      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.

    • @rma1563
      @rma1563 Год назад

      @@jarkow Thank you

    • @thousandcranes3045
      @thousandcranes3045 Год назад +3

      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...

    • @PhilippLackner
      @PhilippLackner  Год назад +1

      It's definitely not necessary

  • @АртемВинокуров-ъ7о

    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.

  • @sheikhchilli7560
    @sheikhchilli7560 Год назад

    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?

    • @PhilippLackner
      @PhilippLackner  Год назад

      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

    • @sheikhchilli7560
      @sheikhchilli7560 Год назад

      ​@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

    • @sheikhchilli7560
      @sheikhchilli7560 Год назад

      ​@@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.

    • @sheikhchilli7560
      @sheikhchilli7560 Год назад

      Any update regarding my query?

  • @emrahc9558
    @emrahc9558 Год назад

    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.

  • @oxitroy
    @oxitroy Год назад +1

    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.

    • @PhilippLackner
      @PhilippLackner  Год назад

      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

  • @katou9145
    @katou9145 Год назад

    thx :3

  • @VishnuHaridas
    @VishnuHaridas Год назад

    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
      @anegine Год назад

      data layer depends on domain layer - it's ok. domain layer doesn't depend on any module (except any other domain layers)

    • @VishnuHaridas
      @VishnuHaridas Год назад

      @@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?

    • @anegine
      @anegine Год назад +1

      @@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).

    • @VishnuHaridas
      @VishnuHaridas Год назад +1

      @@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.

  • @masmmaw
    @masmmaw Год назад

    Thank you

    • @Mogswamp-
      @Mogswamp- Год назад

      🔝🔝🔝🔝🔝🔝🔝🔝
      *Thanks for watching you have been selected among the lucky winners, inbox* MOGSWAMP
      🔝🔝🔝🔝🔝🔝🔝🔝

  • @mustafaammar551
    @mustafaammar551 Год назад

    very cool video

  • @boukarradhmoez99
    @boukarradhmoez99 Год назад

    Ok that's a good approach but what about the cause when I need Dagger Hilt to inject my repos in my use cases ?

    • @inertia_dagger
      @inertia_dagger Год назад +1

      if you're going to test your use cases, inject repos as interfaces and mock those interfaces in tests

    • @boukarradhmoez99
      @boukarradhmoez99 Год назад +1

      @@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
      @boukarradhmoez99 Год назад +1

      @PhilippLackner 🤔

    • @inertia_dagger
      @inertia_dagger Год назад +1

      @@boukarradhmoez99 I didn't understand your question. Rephrase it, please?

    • @thousandcranes3045
      @thousandcranes3045 Год назад +1

      @@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

  • @muhammadyaseen7022
    @muhammadyaseen7022 Год назад

    👍

  • @ewomer100
    @ewomer100 Год назад

    Isn't #3 the mistake you use in almost all of your long play video tutorials of Clean Architecture?

  • @LazyTram911
    @LazyTram911 Год назад

    top

  • @koncinar
    @koncinar Год назад

    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. 👎