69

BACKGROUND :

I'm trying to use Uncle Bob's clean architecture in my android app. I studied many open source projects that are trying to show the right way to do it, and I found an interesting implementation based on RxAndroid.

WHAT I NOTICIED :

In every layer (presentation, domain and data), there's a model class for the same entity (talking UML). Plus, there are mapper classes that take care of object's transformation whenever the data is crossing the boundaries (from layer to another).

QUESTION :

Is it required to have model classes in every layer when I know that they'll all end up with the same attributes if all CRUD operations are needed? Or, is it a rule or a best practice when using the clean architecture?

4 Answers 4

72

In my opinion, that's absolutely not how it's meant. And it's a violation of DRY.

The idea is that the entity / domain object in the middle is modeled to represent the domain as good and as convenient as possible. It is in the center of everything and everything can depend on it since the domain itself doesn't change most of the time.

If your database on the outside can store those objects directly, then mapping them to another format for the sake of separating layers is not just pointless but creating duplicates of the model and that is not the intention.

To begin with, the clean architecture was made with a different typical environment / scenario in mind. Business server applications with behemoth outer layers that need their own types of special objects. For example databases that produce SQLRow objects and need SQLTransactions in return to update items. If you were to use those in the center, you were to violate the dependency direction because your core would depend on the database.

With lightweight ORMs that load and store entity objects thats not the case. They do the mapping between their internal SQLRow and your domain. Even if you need put an @Entitiy annotation of the ORM into your domain object, I'd argue that this does not establish a "mention" of the outer layer. Because annotations are just metadata, no code that isn't specifically looking for them will see them. And more importantly, nothing needs to change if you remove them or replace them with a different database's annotation.

In contrast, if you do change your domain and you made all those mappers, you have to change a lot.


Amendment: Above is a little oversimplified and could even be wrong. Because there is a part in clean architecture that wants you to create a representation per layer. But that has to be seen in context of the application.

Namely the following here https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

The important thing is that isolated, simple, data structures are passed across the boundaries. We don’t want to cheat and pass Entities or Database rows. We don’t want the data structures to have any kind of dependency that violates The Dependency Rule.

Passing entities from the center towards the outer layers does not violate the dependency rule, yet they are mentioned. But this has a reason in the context of the envisioned application. Passing entities around would move the application logic towards the outside. Outer layers would need to know how to interpret the inner objects, they would effectively have to do what inner layers like the "use case" layer is supposed to do.

Besides that, it also decouples layers so that changes to the core don't necessarily require changes in outer layers (see SteveCallender's comment). In that context, it's easy to see how objects should represent specifically the purpose they are used for. Also that layers should talk to each other in terms of objects that are made specifically for the purpose of this communication. This can even mean that there are 3 representations, 1 in each layer, 1 for transport between layers.

And there is https://blog.8thlight.com/uncle-bob/2011/11/22/Clean-Architecture.html which addresses above:

Other folks have worried that the net result of my advice would be lots of duplicated code, and lots of rote copying of data from one data structure to another across the layers of the system. Certainly I don’t want this either; and nothing I have suggested would inevitably lead to repetition of data structures and an inordinate of field copying.

That IMO implies that plain 1:1 copying of objects is a smell in the architecture because you're not actually using the proper layers and /or abstractions.

He later explains how he imagines all the "copying"

You separate the UI from the business rules by passing simple data structures between the two. You don’t let your controllers know anything about the business rules. Instead, the controllers unpack the HttpRequest object into a simple vanilla data structure, and then pass that data structure to an interactor object that implements the use case by invoking business objects. The interactor then gathers the response data into another vanilla data structure and passes it back to the UI. The views do not know about the business objects. They just look in that data structure and present the response.

In this application, there is a big difference between the representations. The data that flows isn't just the entities. And this warrants and demands different classes.

However, applied to a simple Android application like a photo viewer where the Photo entity has about 0 business rules and the "use case" that deals with them is nearly non-existing and is actually more concerned about caching & downloading (that process should IMO be represented more explicitly), the point to make separate representations of a photo starts to vanish. I even get the feeling that the photo itself is the data transfer object while the real business-logic-core-layer is missing.

There is a difference between "separate the UI from the business rules by passing simple data structures between the two" and "when you want to display a photo rename it 3 times on the way".

Besides that, the point where I see those demo applications fail at representing the clean architecture is that they add huge emphasis on separating layers for the sake of separating layers but effectively hide what the application does. That is in contrast to what is said in https://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Architecture.html - namely that

the architecture of a software application scream about the use cases of the application

I don't see that emphasis on separating layers in the clean architecture. It's about dependency direction and focusing on representing the core of the application - entities and use cases - in ideally plain java without dependencies towards the outside. It's not so much about dependencies towards that core.

So if your application actually has a core that represents business rules and use cases, and / or different people work on different layers, please separate them in the intended way. If you're on the other hand just writing a simple app all by yourself don't overdo it. 2 layers with fluent bounds may be more than enough. And layers can be added later on as well.

12
  • 1
    @RamiJemli Ideally, the entities are the same in all applications. That's the difference between "enterprise wide business rules" and "application business rules" (sometimes business vs application logic). The core is a very abstract representation of your entities that is generic enough that you could use it everywhere. Imagine a bank that has many applications, one for customer support, one running on cash machines, one as web ui for customers themselves. All those could use the same BankAccount but with application specific rules what you can do with that account.
    – zapl
    Commented Nov 23, 2015 at 16:31
  • 5
    I think an important point in the clean architecture is that by using the interface adapter layer to convert (or as you say map) between the different layers' representation of the entity you reduce the dependency to said entity. Should there be a change in the Usecase or Entity layers (hopefully unlikely but as requirements change these layers will to) then the impact of the change is contained in the adapter layer. If you chose to use the same representation of the entity all through your architecture the impact of this change would be much greater. Commented Nov 25, 2015 at 12:17
  • 1
    @RamiJemli it's good to use frameworks that make life simpler, the point is that you should be careful when your architecture relies on them and you start to put them at the center of everything. Here is even an article about RxJava blog.8thlight.com/uncle-bob/2015/08/06/let-the-magic-die.html - it's not saying you shouldn't use it. It's more like: I've seen this, it's going to be different in a year and when your application is still around you're stuck with it. Make it a detail and do the most important things in plain old java while applying plain old SOLID principles.
    – zapl
    Commented Nov 25, 2015 at 16:40
  • 1
    @zapl Do you feel the same way about a web service layer? In other words, would you put @SerializedName Gson annotations on a domain model? Or would you create a new object responsible for mapping web response to domain model?
    – tir38
    Commented May 17, 2016 at 2:26
  • 2
    @tir38 Separation itself does not provide the benefit, it's the cost of future changes that go down with it. => Depends on the application. 1) it costs you time to create & maintain the added stage that transforms between different representations. E.g. adding a field to the domain and forgetting to add it somewhere else isn't unheard of. Can't happen with the simple approach. 2) It costs to transition to a more complex setup later in case it turns out you need it. Adding layers isn't easy, it's therefore easier in large applications to justify more layers that aren't needed immediately
    – zapl
    Commented May 17, 2016 at 10:29
11

You actually got it right. And there is no violation of DRY because you accept SRP.

For example: You have a business-Method createX(String name) then you may have a Method createX(String name) in the DAO-Layer, called within the business-Method. They may have the same signature and maybe there is only a delegation but they have different purposes. You can also have a createX(String name) on the UseCase. Even then it is not redundant. What I mean with this is: Same signatures do not mean same semantics. Choose other names for it to have the semantics clear. Naming itself this doesn't affect SRP at all.

The UseCase is responsible for application-specific logic, the business object is responsible for application-independent logic and the DAO is responsible for storing.

Due to the different semantics all layers may have their own representation and communication model. Often you see "entities" as "business objects" and often you do not see the necessity to separate them. But in "huge" projects the effort should be taken to properly separate the layers. The greater the project the possibility rises that you need the different semantics represented in different layers and classes.

You can think of different aspects of the same semantic. A User-Object has to be displayed on the screen, it has some inner consistency rules and it must be stored somewhere. Each aspect should be represented in a different class (SRP). Creating the mappers can be pain in the ass so in most projects I worked in these aspects are melted into one class. This is clearly a violation of SRP but nobody really cares.

I call the application of clean architecture and S.O.L.I.D. "not socially acceptable". I would work with it if I am allowed to. Currently I am not allowed to do it. I wait for the moment we have to think about taking S.O.L.I.D. seriously.

2
  • I think no method in the Data layer should have the same signature as any method in the Domain layer. In the Domain layer, you use business-related naming conventions like signUp or login, and in the Data layer, you use save (if DAO pattern), or add(if Repository because this pattern uses Collection as a metaphor). Finally, I'm not talking about entities (Data) and model (Domain), I'm highlighting the uselessness of UserModel and its Mapper (presentation layer). You can call the User class of the domain inside the presentation and this doesn't violates the dependency rule.
    – Rami Jemli
    Commented Nov 25, 2015 at 15:02
  • I concur with Rami, the mapper is unnecessary because you can do the mapping directly in the interactor implementation. Commented Mar 17, 2016 at 7:46
5

No, you don't need to create model classes in every layer.

Entity(DATA_LAYER) - is a full or partial representation of Database object. DATA_LAYER

Mapper(DOMAIN_LAYER) - actually is a class which convert Entity to ModelClass, which will be used on DOMAIN_LAYER

Take a look : https://github.com/lifedemons/photoviewer

3
  • 1
    Of course, I'm not against entities in the data layer, but, in your example, the PhotoModel class in the presentation layer has the same attributes of the Photo class in the domain layer. It's technically the same class. is that necessary?
    – Rami Jemli
    Commented Nov 22, 2015 at 23:58
  • I think something is off in your example as domain layer shouldn't depend on other layers as in your example your mappers depend on the entities in your data layer which IMO, it should be vice versa
    – navid_gh
    Commented Dec 25, 2018 at 15:42
  • in your github example you actually violate the clean architecture by let the domain layer know about data layer. In the mapper there is import of an entity from data layer. Domain layer should not have dependencies on presentation and data layers
    – gts13
    Commented Mar 11, 2020 at 21:11
1

Let's review how Uncle Bob defines an "entity" in the Clean Architecture book (p. 190):

An Entity is an object within our computer system that embodies a small set of critical business rules operating on Critical Business Data. The Entity object either contains the Critical Business Data or has very easy access to that data. The interface of the Entity consists of the functions that implement the Critical Business Rules that operate on that data.

His reasoning not to use entities as part of the use case's request/response model is as follows (p. 194):

You might be tempted to have these data structures contain references to Entity objects. You might think this makes sense because the Entities and the request/response models share so much data. Avoid this temptation! The purpose of these two objects is very different. Over time they will change for very different reasons, so tying them together in any way violates the Common Closure and Single Responsibility Principles. The result would be lots of tramp data, and lots of conditionals in your code.

To me the much bigger point is the distinction between objects and data structures, which Uncle Bob explains in a more recent blog post:

an object is a set of functions that operate on implied data elements

a data structure is a set of data elements operated upon by implied functions.

Based on the definiton of an entity, we can agree that it is supposed to be an object, not a data structure.

When you pass your entity out of the domain layer and into a presenter, you have to provide accessors (getters) to the entity's internal state, enabling the presenter to present it. Your entity object's data elements now went from being implied to being encapsulated, which seems to be a step down. Also, code in the outer layers can now freely call business logic methods on your entity without supervision of a controller or use case.

But if you take your entity's data elements, put them into some DTO - which is a data structure - and pass that around, the business rules, that the entity represents, can safely stay in the domain layer. The entity's implied data elements are only materialized in the DTO.

Of course, you as the architect have to decide whether you want or need this kind of decoupling.

PS: Both "implied data elements" and "encapsulated data elements" mean that the data elements may or may not be there. But I feel the implication that it is there is much stronger with getters. That's why I would say having getters on your entity is worse in the context of Clean Architecture, and using a DTO is better.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.