- I'm not a long time blogger, or whatever, so sorry about the lack of didactic-ism.
- I'm not an English fluent person, but I want to reach everyone as possible. So sorry by now about my bad English.
- I'm not trying to make some sort of pattern here or telling that this is the right way. It's only a thought that I really felt the need to discuss.
The point is
Should repository pattern implementations have knowledge about my database schema?
Classical scenario: I have a simple table, with a simple schema, a repository implementation using this cool ORM/DBAL engine of my favorite language returning my simple Entity.
We can use an InMemory implementation for tests? Yes. We can use another type of ORM/DBAL implementation? Yes. We can make this different types of ORM engine connects to different databases? Yes*.
*As long they have the same schema!
If a database with a different schema shows up, our repository implementations will broke and change them will violate SOLID's open/closed principle.
The new kid on the block will not be accepted on the playground, he will suffer bully just because he is different, the main kid Repo knows to talk only with his old friend Base and their cool toy, ORM. #sad
Not so fast...
And if the new kid shows up with his new friend, the SpecializedInAnotherDatabaseRepo? Yay! Now he can play with this cool toy called ORM!
Until now we have 2 schemes, 1 entity, 1 repository and 1 ORM engine. Let's test if this solution can grow up. Can we have another kids and toys without a mess? Should we call Nany?
2 Schemes, 2 Entities, 2 Repositories and 2 ORM engines
Yeah, that's it. Four repository implementations for each entity, is one per each Schema-ORM combination.
As I started this discussion around forums on the Internet I saw people telling me that this is OK, that is the consequence of having 2 schemes and 2 ORM/DBAL engines. Well I'm a newbie in programming, people that told me that are light years ahead and I told on the third note but, just to make sure I'm not telling they are wrong, I not telling I'm right, it's just a thought that I really appreciate some feedback.
Bring me solutions, not problems
So I thought about that, I have thinking about some ways to deal with that, realized that the problem isn't about the role repository implementation, is just on the part that I need to "translate" the queried schema to my entity setters, I saw in some PHP (my main language) libraries that this is called Hydration (Doctrine & Zend). So if I have some way to implement and choose only the hydration algorithm that makes this translation, I won't need to make another role repository implementation, then I research and found that the pattern that encapsulates algorithms is the Strategy (not only for that, I know, but felt the it is a good pattern for that).
There is it, from table to Entity. Now let's add our new schema.
Pretty huh? You can even imagine if more schemes shows up, just one more Hydrator, not a entirely repository. Our new kid that came with his friend SpecializedInAnotherDatabaseRepo and makes the main kid Repo jealous, now came with just a toy SchemaBUserHydrator a toy that our little playground leader Repo knows how to play because it's shares the same abstraction of an old toy of his old friend Base. New schema kids can play, as long they bring their Hydrator toys!
The fire proof - 2 Schemes, 2 Entities, 2 Repositories and 2 ORM engines
Still scary, but better? Comment!
And if instead of having repository implementations for each ORM engine, why not move my repository abstraction to a implementation in my domain layer that has dependency on a Storage/DAL abstraction?
Huh? Yeah! That's better to see don't you agree? Now we can clearly take a look at our layers, blue ones are Domain, red ones are our layer that separates nicely from the yellow ones (infrastructure layer), theses guys will keep our data save whatever name they want to call it.
What about config files? They will work nicely*. You can have just one Hydrator implementation and pass to it a XML/YAML/INI etc config file encapsulated by a Configuration type or something like that.
*As long the schema difference is only the naming convetions.
What I mean: if my schema A has
fullname and schema B has
lastname I need to concatenate these two Strings
user.setName(row.firstname + " " + row.lastname) (or split fullname if my Entity has
setLastname). So having a mapping config file will not always be enough.
Thanks for reading! Please comment, I really want to read some thoughts about that or another solutions.