-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Why data layer has a dependency on the domain layer? #136
Comments
That's because data layer is implementation of use cases. Domain layer tells what to do and data layer tells how to do. |
Yep, because Domain is the most inner layer in this architecture. I'd suggest that you should read those articles again ;) |
@lenguyenthanh I think you're confusing the schemes. It's true that in Clean Architecture representation by Martin, Domain Logic is represented as the most inner layer (the "Entities" circle). So, I repeat, this data layer shouldn't have any type of dependency and shouldn't know anything about the outer Domain layer, am I right @android10? @amatkivskiy It seems that the UserRepository interface is declared in the Domain module and implemented in the Data module. I understand this choice as this is the way to implement the Dependency Inversion Principle. In this way, the Data module (inner) is dependent on Domain module (outer) so whenever you want to swap the Data module you are able to do it, just replace the implementation and respect the contract. However this thing is confusing me... Are the arrows going inwards telling me "whatever is outer shoudln't know anything inner" or "inner shouldn't depend on outer"? Citing this:
It tells me that data layer shouldn't know anything about domain layer, but it must know the UserRepository contract which is part of the domain. Confused. |
@andrewjohnson90
That is wrong, data layer contains data entities and this layer is an implementation detail, it is just your data source and belongs to one of the outer circles. |
@android10 Maybe I'm confusing naming with your example? Being an implementation detail, shouldn't Data be the inner circle? I see logic in Data, not in Domain. You say: Domain contains business rules, Data contains Repository implementation logic, though Domain shouldn't know how data is retrieved and this should be the outer circle. I'm really sorry if I'm confusing stuff, but I want to understand this properly! |
Hi @andrewjohnson90, i'll join the discussion here, hopefully i'm not to late to this party. I'll do my best to shed some light on this matter as i too had to wrap my head around the concept. First off, i think the schematics might be a bit confusing as they actually show different things. The one you are citing shows how data flow through the layers, not dependencies or relation of the layers between themselves. The layers themselves and the relation between them are more onion-shaped. The image below shows how the architecture itself is layered (this is not the same as how this repo implements that layering). The architecture is structured as an onion because each layer depends on the layer below itself. (again, this is the actual architecture and it's layers, not the layers from @android10). As you probably already know, the 2 innermost layers are your core business code. Then comes a layer on how to access that data and rules and this is wrapped by external systems. I have annotated in horrible red on how @android10 structured his layers on top of this architecture. His Domain layer contains his core business logic and rules. This includes interfaces on how Data is accessed from a Repository. If there were other Services to external apis, these interfaces would be here as well. Remember this layer actually describes the app's inner workings. The Presentation and Data Layer sit on top of this as seen in the onion, so this is why the Data layer depends on the Domain layer. So the thing to take away from this is that how the architecture is layered and how an example repo like this is layered can look different. I could add (and have) add an Infrastructure Layer, which would handle all interfacing with systems on the device. This layer would be part of the outmost blue layer in the onion architecture. But in my actual project, this layer would also depend on the Domain layer, as shown by the Dependency arrows in the onion. The Domain layer holds the interfaces of the Infrastructure layer, because that is business logic. The actual implementation is of no matter for the Domain, so that ends up in the Infrastructure layer. |
@Trikke Thank you so much for your reply and patience to write this answer and explanation. I'm still a little bit confused, I don't know if it's me or if some time is needed to get this concept completely. So, based on your schemes, you're telling me that @android10 architecture is based on "2 onion layers"? He has data and presentation on the same outer layer and domain as an inner layer. As I understood it from Uncle Bob (probably wrong) is that the most deep we go, the most there's implementation logic that can be swapped. A direct dependence to the DB on the same outer layer (where there's UI as well) doesn't make sense to me. Could you please tell me where to read this matter |
@andrewjohnson90 i'm glad you read all that and have an open mind 😄 I'm guessing the point that you are struggling the most with is that there is a difference between the actual architectural diagram proposed by Uncle Bob and the implementation of that architecture into a project. Both have layers and dependencies, but the concepts are different. I think another issue is thinking that the DB is important and should be in the inner circle. This is a good read on Software Architecture by Uncle Bob. A quick quote on the DB (but please do read it entirely):
The DB doesn't belong in the inner layer because it is not important to our Business. How we store data doesn't matter. It could be Sql, a text file or on Amazon S3. But from the architectural standpoint, it shouldn't matter, we don't care how we store data. So that is why it belongs on the very edge of the architecture. The Parse shutdown from a while ago showed a lot of people that if you depend very heavily on a data system (that is external to you) and it's part of the very core of your app, you're going to have a hard time adapting another one if you need to switch. Another thing with these diagrams and ideas is that sometimes people talk about "the deeper you go", or "the more outwards in the structure" and it becomes hard for anyone new to follow about which "direction" they are talking about because sometimes they mean the same thing. God knows i had (and still have) issues when following a talk and people start talking in "directions". I'm just rambling a bit here, so please do ask questions, always. |
@Trikke you opened my mind. I didn't get it at all then. I will write here in case we want to discuss further, but I want to thank you and for this. I compared this layering system to a top-to-bottom implementation. Take Java language, from a high level language to the bytecode, to the JVM, to assembler, to kernel, to machine hardware etc etc... the deeper the more specific. But it's not like that. I'm going to read another 10 times those articles, trying to stick them in my mind. Thanks again. |
I'm glad to help! it's good that you keep an open mind and are willing to learn and improve on these not-so-easy-to-understand concepts. The helpdesk is always open 😅 |
@android10 @Trikke maybe a suggestion to write a new blog post from this discussion? :) |
@andrewjohnson90 you wanna write one maybe? 😄 |
|
I have the same confusion as @andrewjohnson90. And the discussion here helps me a lot. Thank you very much :-) |
Inside data layer, UserEntityDataMapper transforms from UserEntity to User (com.fernandocejas.android10.sample.domain.User). |
Please read up on the subject. In the posts above, the dependencies and layers are explained.
There's no problem with using tooling libraries to control data through all layers. A logging framework for instance would be the same. It too, would be available across all layers to log stuff. These things are just Cross-cutting concerns.
You'd just be hand writing your dependencies, there's no added benefit. Dagger isn't all that hard to understand, it's a very easy example on Dependency Injection. Even then, you should learn from examples. This shouldn't be a repo that you can just copy and start building your own app upon. That'd be crazy. |
@Trikke you saved me a lot of words. Thanks! Amén! |
Probably a wrong place to discuss the architecture principle itself, but seems like this is the best thread on the entire internet about fundamental flaw of the onion architecture diagram pointed out by @ghost, which causes so much confusion about the data layer. Let's start with simple case. As an end-user using Web and any UI, I interact with the application, and my expectation that it works by accessing a DB (that's where application data is stored). But how does it work if the architecture does not allow those layers to communicate? (I know what you are thinking, but bear with me). Now, here is the trick: I'm not trying to argue the approach here, Onion is great indeed! The only problem is that the original diagram tries to combine the architecture and design together - and that's the it's biggest problem what creates so much confusion. Once you remove the infrastructure from the diagram, it's going to be priceless, and if you need to have an understanding where the data layer is - you need a second design diagram showing important implementation aspects. Or perhaps you can fit the data layer in a 3rd dimension on top of the domain layer of a 2d diagram. Update |
Why are the UseCase interfaces stored in the Domain layer, not in the Presentation Layer? |
@mefilt why would they be part of the presentation if they are not dealing with any UI? :) |
Thanks a lot for this issue, old but gold. I shared this multiple times with people who were confusing data flow & dependency rules. I wrote a medium about it, any feedback is welcome! https://proandroiddev.com/clean-architecture-data-flow-dependency-rule-615ffdd79e29 |
@Oziomajnr if you go the README file...you will see the links to the posts that lead to this repo:
Also as a result, there is a new version in Kotlin: |
@mefilt |
As stated in Clean Architecture, dependencies should point only inwards. This separates layers in a way that they could be interchangeable easily and also decoupled (right?).
In your case, you decided to create 3 layers which have this dependency relationship:
presentation ---> domain ---> data
You use DI for some good reasons (still decoupling, dependency inversion ecc).
However from your build.gradle files, it seems that data has a dependency on domain, while domain doesn't have anything. The dependency is in this way:
presentation ---> domain <--- data
Could you explain me why? Is there something I'm missing?
The text was updated successfully, but these errors were encountered: