How to deal with attributes common to Entities, Repositories, and Serializers

160 views
Skip to first unread message

Reed Law

unread,
Mar 13, 2015, 4:42:03 AM3/13/15
to clean-code...@googlegroups.com
What do you do when you have a list of attributes that are 80% the same and used across 3 different classes? For example, a user repository might have id, first_name, last_name, email, encrypted_password, password_reset_token, etc. The user entity might have first_name, last_name, email, and schedule. Should the common attributes be extracted out into a configuration file? If a new attribute is added to the repository, it's easy to forget to add it to the other two classes. Where do you put your attributes?

Caio Fernando Paes de Andrade

unread,
Mar 13, 2015, 6:40:36 AM3/13/15
to clean-code...@googlegroups.com
The tests will force me to add those to all places that are needed.

You can cut this corner by having an entity DTO with the shared information and passing it around the layers, but be careful about it.

Caio

Enviado do meu iPhone

Em 13/03/2015, às 05:42, Reed Law <ree...@gmail.com> escreveu:

What do you do when you have a list of attributes that are 80% the same and used across 3 different classes? For example, a user repository might have id, first_name, last_name, email, encrypted_password, password_reset_token, etc. The user entity might have first_name, last_name, email, and schedule. Should the common attributes be extracted out into a configuration file? If a new attribute is added to the repository, it's easy to forget to add it to the other two classes. Where do you put your attributes?

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.
To post to this group, send email to clean-code...@googlegroups.com.
Visit this group at http://groups.google.com/group/clean-code-discussion.

Norbert Nemes

unread,
Mar 13, 2015, 11:11:18 PM3/13/15
to clean-code...@googlegroups.com
Hi, I'm not exactly sure, but from what you wrote, it sounds like you are doing something wrong.

How can a user repository have id, first_name, last_name, email, encrypted_password, password_reset_token, etc. ?

A user repository holds and manages User objects. All those properties seem to be user properties.
The repository should have methods like addUser, deleteUser, SaveUser, etc.

Regards,

Norbert

Reed Law

unread,
Mar 13, 2015, 11:27:55 PM3/13/15
to clean-code...@googlegroups.com
Norbert,

I mean the database table that the Repository uses. I'm using Rails and our Repository classes are subclasses of ActiveRecord. The database has a user_repository table with all of those columns. Even in your example the repository has to know which attributes to save with the addUser and saveUser methods.

Do you have any examples of how you handle repositories?

Reed Law

unread,
Mar 13, 2015, 11:30:29 PM3/13/15
to clean-code...@googlegroups.com, caio...@icloud.com
Caio,

When you add the same attributes to the tests for three classes, do you not sense a need to refactor?

I'm not sure how the DTO concept would work in practice. Do you have an example?


On Friday, March 13, 2015 at 6:40:36 AM UTC-4, Caio Fernando Paes de Andrade wrote:
The tests will force me to add those to all places that are needed.

You can cut this corner by having an entity DTO with the shared information and passing it around the layers, but be careful about it.

Caio

Enviado do meu iPhone

Em 13/03/2015, às 05:42, Reed Law <ree...@gmail.com> escreveu:

What do you do when you have a list of attributes that are 80% the same and used across 3 different classes? For example, a user repository might have id, first_name, last_name, email, encrypted_password, password_reset_token, etc. The user entity might have first_name, last_name, email, and schedule. Should the common attributes be extracted out into a configuration file? If a new attribute is added to the repository, it's easy to forget to add it to the other two classes. Where do you put your attributes?

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discussion+unsub...@googlegroups.com.

Norbert Nemes

unread,
Mar 14, 2015, 3:29:01 AM3/14/15
to clean-code...@googlegroups.com
>Even in your example the repository has to know which attributes to save with the addUser and saveUser methods.
No, not really.

The way I implement repositories is the following:
Let's say I have an ItemRepository that manages items. This repository is just a plain object of whatever language you're using.
It has methods like:
- getAllItems
- getItemForId
- loadData
- addItem
and whatever else you need to manage objects. The return type and parameters to all these methods are usually Items (in your case Users). Of course one will take an ID, like getItemForId.
The repository also has a gateway, which is also just a plain objects that manages serialization of the underlying Item objects.
So when a call comes in along the lines saveItem(Item), the repository forwards it to the gateway and the gateway takes care of it. The gateway could be talking to a DB, to an API, the file system, or whatever. This detail is completely hidden from the system.
So in your case I suspect the gateway would create an ActiveObject based on the incoming Item object and save it (sorry I don't know Ruby).
We had a similar problem on iOS with CoreData. It uses a NSManagedObject to do it's DB stuff, and that type would pop up everywhere in the system, even in places that do not need CoreData.
So the gateway helps, because it does the translation between normal objects and whatever ugly data type you need to serialize those objects.

However if you do not need to hold items in memory in a list, and pretty much every method calls the DB, then perhaps your repository could double as the gateway. Still, I would not make it a derivative of ActiveRecord. I would create this type in the methods that need it, populate it from the incoming data and do whatever it needs to do with it.

Reed Law

unread,
Mar 16, 2015, 8:48:04 PM3/16/15
to clean-code...@googlegroups.com
It sounds like you're using different terminology. In our case, a Repository is the class that is responsible for talking with the DB. It leverages ActiveRecord so we don't have to build SQL queries by hand unless we want to. A Builder is a class that converts the query results into Entities. A Serializer is a class that turns entities into hash-like objects (or JSON). It sounds like your repositories return entities directly. In any case we're dealing with layers of indirection. I want to keep the db queries isolated so my tests are fast, but I don't want a lot of duplicated code. Since repositories are querying the db and builders are turning query results into entities, how can I avoid having them all repeat the same attribute names? Entities have a list of attr_accessor attributes (Ruby's way to create getters and setters). The db has a list of columns that may or may not correspond with entities' attr_accessor attributes. But in most cases they are largely the same. Do I just move those into a config file somewhere? It feels somewhat awkward.

Caio Fernando Bertoldi Paes de Andrade

unread,
Mar 17, 2015, 6:24:47 AM3/17/15
to clean-code...@googlegroups.com
Reed,

If you want to mitigate that duplication, there are lots of ways to do so, and all of them couple two or more components together, that’s the price you pay. If you create a configuration file, you are coupling your gateway and your entities to some external file, which would violate the dependency rule. If you just let the gateway have access to the entity, you are exposing its business rules and some weird things can happen. I would advise you to let the entities have DTOs that are reused by other layers, so you can mitigate duplication and isolate the business rules but still somewhat conform to the dependency rule:

Your entity holds a reference to a data structure…

class AddressEntity {
    AddressDTO data

    AddressEntity(AddressDTO data){
        validate(data);
        this.data = data
    }

    void doSomething(){
        // business rule manipulating the DTO in this.data
    }

    AddressDTO serialize(){
        return this.data;
    }
}

…that is reused in the gateway component…

class AddressORM extends AddressDTO {    // note that the ORM extends the DTO and not the Entity itself
    // ORM specific stuff goes in here
}

class AddressGateway {
    void save(AddressDTO address){    
        AddressORM orm = new AddressORM(address)
        orm.save()
    }
}

…so you avoid rewriting all those fields, hide entity business rules from the gateway, and still keep the dependencies pointing inwards in your architecture.

You don’t have to use inheritance like I’ve shown here, you can be creative and use other language features to achieve the same reuse given the dependency stays the same.

I’m sorry for not investing more time in building a better example, I’m pretty sure this code doesn’t even compile and the names are really bad, but I think this is sufficient for you to have a clearer idea about this approach I suggested.

Caio

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.

Norbert Nemes

unread,
Mar 17, 2015, 9:24:38 AM3/17/15
to clean-code...@googlegroups.com, caio...@icloud.com
Right! :)  In my system I need to have a local repository of items as well, so I call that a repository. And yes, it returns entities (the gateway creates them) :)
It sounds though as if you're coupling the gateway to the ActiveRecord class which may not be necessary. In my opinion it should be a normal class that takes an entity (or a DTO as Caio said) and then map it to an ActiveRecord object and serialize it. As such nobody in the system except for the gateway implementation knows how exactly entities are serialized.

Caio Fernando Bertoldi Paes de Andrade

unread,
Mar 17, 2015, 9:39:48 AM3/17/15
to clean-code...@googlegroups.com
I’m concerned about this approach. If the gateway is the only place that knows how to serialise and build entities, the core system doesn’t work without it. I should be able to create a different gateway with ease and swap them, and this approach seems to go against this.

What makes more sense to me is the serializers and builders belonging in the core, together with entities and use cases. Data structures are then used to communicate across boundaries (gateways), like Uncle Bob pointed as a guideline many times.

Just a bonus point: If you make your entities hold DTOs, serialization and building becomes just a matter of one (or maybe only few) getter and setter, no big deal. ;-)

Caio

Em 17/03/2015, à(s) 10:24, Norbert Nemes <deepbl...@gmail.com> escreveu:

Right! :)  In my system I need to have a local repository of items as well, so I call that a repository. And yes, it returns entities (the gateway creates them) :)
It sounds though as if you're coupling the gateway to the ActiveRecord class which may not be necessary. In my opinion it should be a normal class that takes an entity (or a DTO as Caio said) and then map it to an ActiveRecord object and serialize it. As such nobody in the system except for the gateway implementation knows how exactly entities are serialized.

Norbert Nemes

unread,
Mar 18, 2015, 3:02:07 AM3/18/15
to clean-code...@googlegroups.com, caio...@icloud.com
I’m concerned about this approach. If the gateway is the only place that knows how to serialise and build entities, the core system doesn’t work without it. I should be able to create a different gateway with ease and swap them, and this approach seems to go against this.

No it doesn't and that's exactly the point!
The system knows about the gateway interface, but not the implementation. This way, you can switch it out to whatever you want.
The system does not know how exactly stuff is going to be serialized. It only knows that there is someone that takes care of it. The gateway could store the entity in a DB, send it over a network, of write the entity out to a file. It does not matter. The core need not be concerned with this detail.
If your gateway interface says ActiveRecord, you already coupled it to whatever ActiveRecord uses and can never change it to something different.
Oh, and factories, builders/serializers are outside the app boundary. If I remember correctly, UB explicitly mentions this in one of his episodes (I think it was "Architecture").

Regards,

Norbert

Israel Fonseca

unread,
Mar 18, 2015, 8:35:52 AM3/18/15
to clean-code...@googlegroups.com, caio...@icloud.com
Norbert, you said that your Repository have a Gateway, but it wasn't clear for me whats the role of it.

What I pictured:

ShowItemUseCase -> ItemRepository -> |Boundary| -> ItemGateway

UseCase: Business rules
Repository: transform a data structure retrieved from the gateway back into entities
Gateway: retrieves andata structure from a datasource (file, database, magic)

This way, any refactoring of entities wouldn't affect the gateways. If your gateway knows how to parse an entity directly (the problem that Caio pointed out), any change in entities would break the gateways too.

Do you agree?



--

Norbert Nemes

unread,
Mar 18, 2015, 10:06:42 AM3/18/15
to clean-code...@googlegroups.com, caio...@icloud.com
I think we might have a terminology issue here. :)

Your picture is correct:
ShowItemUseCase -> ItemRepository -> |Boundary| -> ItemGateway
The boundary being the Gateway interface.

However, the next two lines are incorrect:
Repository: transform a data structure retrieved from the gateway back into entities
Gateway: retrieves andata structure from a datasource (file, database, magic)

In my case, I needed a place to store and manage one type of entity in my system (in memory) so I created this class that does just that and called it ItemRepository.
It searches/filters items, fetches them from the gateway / sends them to the gateway to be stored at a remote location. It is essentially just another ENTITY of my system, containing application independent business rules.
The Gateway is something like a transceiver/factory. It receives one type of entity, breaks it  down and transmits/stores it to wherever. It also fetches objects from wherever, deserializes them and creates and entity that is returned to the repository. It does NOT expose the way it doest things through its interface.
Gateways are nothing but just another form of interactors. 
Interactors manipulate entities and return specially formatted data to presenters (entities at one end, presenters at the other).
Gateways manipulate entities and return specially formatted data to the network, for instance (entities at one end, the outside world on the other).
Interactors contain application dependent business rules, and so do gateways, because the method by which entities are stored / transmitted is application dependent.

I think in your case, the repository IS the gateway. So it is in UB's Agile Software Development book (payroll case study). But there as well, entities (Employe) are passed to it, and the way it stores them is completely unknown to the system.

You mentioned refactoring:
Of course changing a field on the entity will break the gateway: it's a base type of the system! It will break your repository too (and your gateway)!

Hope it's clearer now what I meant. :)

Israel Fonseca

unread,
Mar 19, 2015, 7:24:32 AM3/19/15
to clean-code...@googlegroups.com, caio...@icloud.com
I get it Norbert, but about the refactoring point:

Remember that your 'core' could be distributed appart of the gateway implementations(delivery mechanisms). So internal refactorings of your concepts (entities like the Item) would affect all ecosystem that's already using your code, effectively breaking code around the world.

So your tests, would fail, and the people using your module too.

If your gateway contract just send/retrieve an datastructure (not your entity directly) this wouldn't happen (you wouldnt need to change the data structure, only the entity and the serialization to cross boundary). More boilerplate, but safer.

Do you see that? Or am I missing something? In the Clean Architecture diagram, the entity is hidden behind the usecase layer, so ideally (remember that everything has a tradeoff), you shouldn't leak it outside to avoid such problems.

I'm not saying that your practice is wrong, but just pointing a tradeoff that I think that you would get.


--

Norbert Nemes

unread,
Mar 22, 2015, 8:15:17 AM3/22/15
to clean-code...@googlegroups.com, caio...@icloud.com
Hi Israel

Sorry for the late response.
There's no offence taken, and I'm not criticizing  your way either :). 
My point is that, although passing DTOs around has its nice points and makes sense, your code and tests WILL break if you refactor the Entity in a way that affects the DTO. 
And while you're right that the entities are hidden behind use cases, don't forget that they are manipulated in those use cases. And if you consider a Gateway as just another use case (application dependent transmission/storage), having Entities in Gateways should be perfectly OK. There is no one on the other side of the wire who can access the entities, so they are not leaked out. 
As you said, it's a tradeoff based on what you need in your system (currently I'm working on mobile applications, so the independent deployability point is moot). :) 

Reply all
Reply to author
Forward
0 new messages